/*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package jdk.nashorn.internal.runtime.regexp.joni;
import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
import jdk.nashorn.internal.runtime.regexp.joni.constants.Arguments;
import jdk.nashorn.internal.runtime.regexp.joni.constants.OPCode;
import jdk.nashorn.internal.runtime.regexp.joni.constants.OPSize;
import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
class ByteCodePrinter {
final int[] code;
final int codeLength;
final char[][] templates;
Object[] operands;
private final static String OpCodeNames[] = new String[] {
"finish", /*OP_FINISH*/
"end", /*OP_END*/
"exact1", /*OP_EXACT1*/
"exact2", /*OP_EXACT2*/
"exact3", /*OP_EXACT3*/
"exact4", /*OP_EXACT4*/
"exact5", /*OP_EXACT5*/
"exactn", /*OP_EXACTN*/
"exactmb2-n1", /*OP_EXACTMB2N1*/
"exactmb2-n2", /*OP_EXACTMB2N2*/
"exactmb2-n3", /*OP_EXACTMB2N3*/
"exactmb2-n", /*OP_EXACTMB2N*/
"exactmb3n", /*OP_EXACTMB3N*/
"exactmbn", /*OP_EXACTMBN*/
"exact1-ic", /*OP_EXACT1_IC*/
"exactn-ic", /*OP_EXACTN_IC*/
"cclass", /*OP_CCLASS*/
"cclass-mb", /*OP_CCLASS_MB*/
"cclass-mix", /*OP_CCLASS_MIX*/
"cclass-not", /*OP_CCLASS_NOT*/
"cclass-mb-not", /*OP_CCLASS_MB_NOT*/
"cclass-mix-not", /*OP_CCLASS_MIX_NOT*/
"cclass-node", /*OP_CCLASS_NODE*/
"anychar", /*OP_ANYCHAR*/
"anychar-ml", /*OP_ANYCHAR_ML*/
"anychar*", /*OP_ANYCHAR_STAR*/
"anychar-ml*", /*OP_ANYCHAR_ML_STAR*/
"anychar*-peek-next", /*OP_ANYCHAR_STAR_PEEK_NEXT*/
"anychar-ml*-peek-next", /*OP_ANYCHAR_ML_STAR_PEEK_NEXT*/
"word", /*OP_WORD*/
"not-word", /*OP_NOT_WORD*/
"word-bound", /*OP_WORD_BOUND*/
"not-word-bound", /*OP_NOT_WORD_BOUND*/
"word-begin", /*OP_WORD_BEGIN*/
"word-end", /*OP_WORD_END*/
"begin-buf", /*OP_BEGIN_BUF*/
"end-buf", /*OP_END_BUF*/
"begin-line", /*OP_BEGIN_LINE*/
"end-line", /*OP_END_LINE*/
"semi-end-buf", /*OP_SEMI_END_BUF*/
"begin-position", /*OP_BEGIN_POSITION*/
"backref1", /*OP_BACKREF1*/
"backref2", /*OP_BACKREF2*/
"backrefn", /*OP_BACKREFN*/
"backrefn-ic", /*OP_BACKREFN_IC*/
"backref_multi", /*OP_BACKREF_MULTI*/
"backref_multi-ic", /*OP_BACKREF_MULTI_IC*/
"backref_at_level", /*OP_BACKREF_AT_LEVEL*/
"mem-start", /*OP_MEMORY_START*/
"mem-start-push", /*OP_MEMORY_START_PUSH*/
"mem-end-push", /*OP_MEMORY_END_PUSH*/
"mem-end-push-rec", /*OP_MEMORY_END_PUSH_REC*/
"mem-end", /*OP_MEMORY_END*/
"mem-end-rec", /*OP_MEMORY_END_REC*/
"fail", /*OP_FAIL*/
"jump", /*OP_JUMP*/
"push", /*OP_PUSH*/
"pop", /*OP_POP*/
"push-or-jump-e1", /*OP_PUSH_OR_JUMP_EXACT1*/
"push-if-peek-next", /*OP_PUSH_IF_PEEK_NEXT*/
"repeat", /*OP_REPEAT*/
"repeat-ng", /*OP_REPEAT_NG*/
"repeat-inc", /*OP_REPEAT_INC*/
"repeat-inc-ng", /*OP_REPEAT_INC_NG*/
"repeat-inc-sg", /*OP_REPEAT_INC_SG*/
"repeat-inc-ng-sg", /*OP_REPEAT_INC_NG_SG*/
"null-check-start", /*OP_NULL_CHECK_START*/
"null-check-end", /*OP_NULL_CHECK_END*/
"null-check-end-memst", /*OP_NULL_CHECK_END_MEMST*/
"null-check-end-memst-push", /*OP_NULL_CHECK_END_MEMST_PUSH*/
"push-pos", /*OP_PUSH_POS*/
"pop-pos", /*OP_POP_POS*/
"push-pos-not", /*OP_PUSH_POS_NOT*/
"fail-pos", /*OP_FAIL_POS*/
"push-stop-bt", /*OP_PUSH_STOP_BT*/
"pop-stop-bt", /*OP_POP_STOP_BT*/
"look-behind", /*OP_LOOK_BEHIND*/
"push-look-behind-not", /*OP_PUSH_LOOK_BEHIND_NOT*/
"fail-look-behind-not", /*OP_FAIL_LOOK_BEHIND_NOT*/
"call", /*OP_CALL*/
"return", /*OP_RETURN*/
"state-check-push", /*OP_STATE_CHECK_PUSH*/
"state-check-push-or-jump", /*OP_STATE_CHECK_PUSH_OR_JUMP*/
"state-check", /*OP_STATE_CHECK*/
"state-check-anychar*", /*OP_STATE_CHECK_ANYCHAR_STAR*/
"state-check-anychar-ml*", /*OP_STATE_CHECK_ANYCHAR_ML_STAR*/
"set-option-push", /*OP_SET_OPTION_PUSH*/
"set-option", /*OP_SET_OPTION*/
};
private final static int OpCodeArgTypes[] = new int[] {
Arguments.NON, /*OP_FINISH*/
Arguments.NON, /*OP_END*/
Arguments.SPECIAL, /*OP_EXACT1*/
Arguments.SPECIAL, /*OP_EXACT2*/
Arguments.SPECIAL, /*OP_EXACT3*/
Arguments.SPECIAL, /*OP_EXACT4*/
Arguments.SPECIAL, /*OP_EXACT5*/
Arguments.SPECIAL, /*OP_EXACTN*/
Arguments.SPECIAL, /*OP_EXACTMB2N1*/
Arguments.SPECIAL, /*OP_EXACTMB2N2*/
Arguments.SPECIAL, /*OP_EXACTMB2N3*/
Arguments.SPECIAL, /*OP_EXACTMB2N*/
Arguments.SPECIAL, /*OP_EXACTMB3N*/
Arguments.SPECIAL, /*OP_EXACTMBN*/
Arguments.SPECIAL, /*OP_EXACT1_IC*/
Arguments.SPECIAL, /*OP_EXACTN_IC*/
Arguments.SPECIAL, /*OP_CCLASS*/
Arguments.SPECIAL, /*OP_CCLASS_MB*/
Arguments.SPECIAL, /*OP_CCLASS_MIX*/
Arguments.SPECIAL, /*OP_CCLASS_NOT*/
Arguments.SPECIAL, /*OP_CCLASS_MB_NOT*/
Arguments.SPECIAL, /*OP_CCLASS_MIX_NOT*/
Arguments.SPECIAL, /*OP_CCLASS_NODE*/
Arguments.NON, /*OP_ANYCHAR*/
Arguments.NON, /*OP_ANYCHAR_ML*/
Arguments.NON, /*OP_ANYCHAR_STAR*/
Arguments.NON, /*OP_ANYCHAR_ML_STAR*/
Arguments.SPECIAL, /*OP_ANYCHAR_STAR_PEEK_NEXT*/
Arguments.SPECIAL, /*OP_ANYCHAR_ML_STAR_PEEK_NEXT*/
Arguments.NON, /*OP_WORD*/
Arguments.NON, /*OP_NOT_WORD*/
Arguments.NON, /*OP_WORD_BOUND*/
Arguments.NON, /*OP_NOT_WORD_BOUND*/
Arguments.NON, /*OP_WORD_BEGIN*/
Arguments.NON, /*OP_WORD_END*/
Arguments.NON, /*OP_BEGIN_BUF*/
Arguments.NON, /*OP_END_BUF*/
Arguments.NON, /*OP_BEGIN_LINE*/
Arguments.NON, /*OP_END_LINE*/
Arguments.NON, /*OP_SEMI_END_BUF*/
Arguments.NON, /*OP_BEGIN_POSITION*/
Arguments.NON, /*OP_BACKREF1*/
Arguments.NON, /*OP_BACKREF2*/
Arguments.MEMNUM, /*OP_BACKREFN*/
Arguments.SPECIAL, /*OP_BACKREFN_IC*/
Arguments.SPECIAL, /*OP_BACKREF_MULTI*/
Arguments.SPECIAL, /*OP_BACKREF_MULTI_IC*/
Arguments.SPECIAL, /*OP_BACKREF_AT_LEVEL*/
Arguments.MEMNUM, /*OP_MEMORY_START*/
Arguments.MEMNUM, /*OP_MEMORY_START_PUSH*/
Arguments.MEMNUM, /*OP_MEMORY_END_PUSH*/
Arguments.MEMNUM, /*OP_MEMORY_END_PUSH_REC*/
Arguments.MEMNUM, /*OP_MEMORY_END*/
Arguments.MEMNUM, /*OP_MEMORY_END_REC*/
Arguments.NON, /*OP_FAIL*/
Arguments.RELADDR, /*OP_JUMP*/
Arguments.RELADDR, /*OP_PUSH*/
Arguments.NON, /*OP_POP*/
Arguments.SPECIAL, /*OP_PUSH_OR_JUMP_EXACT1*/
Arguments.SPECIAL, /*OP_PUSH_IF_PEEK_NEXT*/
Arguments.SPECIAL, /*OP_REPEAT*/
Arguments.SPECIAL, /*OP_REPEAT_NG*/
Arguments.MEMNUM, /*OP_REPEAT_INC*/
Arguments.MEMNUM, /*OP_REPEAT_INC_NG*/
Arguments.MEMNUM, /*OP_REPEAT_INC_SG*/
Arguments.MEMNUM, /*OP_REPEAT_INC_NG_SG*/
Arguments.MEMNUM, /*OP_NULL_CHECK_START*/
Arguments.MEMNUM, /*OP_NULL_CHECK_END*/
Arguments.MEMNUM, /*OP_NULL_CHECK_END_MEMST*/
Arguments.MEMNUM, /*OP_NULL_CHECK_END_MEMST_PUSH*/
Arguments.NON, /*OP_PUSH_POS*/
Arguments.NON, /*OP_POP_POS*/
Arguments.RELADDR, /*OP_PUSH_POS_NOT*/
Arguments.NON, /*OP_FAIL_POS*/
Arguments.NON, /*OP_PUSH_STOP_BT*/
Arguments.NON, /*OP_POP_STOP_BT*/
Arguments.SPECIAL, /*OP_LOOK_BEHIND*/
Arguments.SPECIAL, /*OP_PUSH_LOOK_BEHIND_NOT*/
Arguments.NON, /*OP_FAIL_LOOK_BEHIND_NOT*/
Arguments.ABSADDR, /*OP_CALL*/
Arguments.NON, /*OP_RETURN*/
Arguments.SPECIAL, /*OP_STATE_CHECK_PUSH*/
Arguments.SPECIAL, /*OP_STATE_CHECK_PUSH_OR_JUMP*/
Arguments.STATE_CHECK, /*OP_STATE_CHECK*/
Arguments.STATE_CHECK, /*OP_STATE_CHECK_ANYCHAR_STAR*/
Arguments.STATE_CHECK, /*OP_STATE_CHECK_ANYCHAR_ML_STAR*/
Arguments.OPTION, /*OP_SET_OPTION_PUSH*/
Arguments.OPTION, /*OP_SET_OPTION*/
};
public ByteCodePrinter(final Regex regex) {
code = regex.code;
codeLength = regex.codeLength;
operands = regex.operands;
templates = regex.templates;
}
public String byteCodeListToString() {
return compiledByteCodeListToString();
}
private void pString(final StringBuilder sb, final int len, final int s) {
sb.append(":");
sb.append(new String(code, s, len));
}
private void pLenString(final StringBuilder sb, final int len, final int s) {
sb.append(":").append(len).append(":");
sb.append(new String(code, s, len));
}
private static void pLenStringFromTemplate(final StringBuilder sb, final int len, final char[] tm, final int idx) {
sb.append(":T:").append(len).append(":");
sb.append(tm, idx, len);
}
public int compiledByteCodeToString(final StringBuilder sb, final int bptr) {
int len, n, mem, addr, scn, cod;
BitSet bs;
CClassNode cc;
int tm, idx;
int bp = bptr;
sb.append("[").append(OpCodeNames[code[bp]]);
final int argType = OpCodeArgTypes[code[bp]];
final int ip = bp;
if (argType != Arguments.SPECIAL) {
bp++;
switch (argType) {
default:
case Arguments.NON:
break;
case Arguments.RELADDR:
sb.append(":(").append(code[bp]).append(")");
bp += OPSize.RELADDR;
break;
case Arguments.ABSADDR:
sb.append(":(").append(code[bp]).append(")");
bp += OPSize.ABSADDR;
break;
case Arguments.LENGTH:
sb.append(":").append(code[bp]);
bp += OPSize.LENGTH;
break;
case Arguments.MEMNUM:
sb.append(":").append(code[bp]);
bp += OPSize.MEMNUM;
break;
case Arguments.OPTION:
sb.append(":").append(code[bp]);
bp += OPSize.OPTION;
break;
case Arguments.STATE_CHECK:
sb.append(":").append(code[bp]);
bp += OPSize.STATE_CHECK;
break;
}
} else {
switch (code[bp++]) {
case OPCode.EXACT1:
case OPCode.ANYCHAR_STAR_PEEK_NEXT:
case OPCode.ANYCHAR_ML_STAR_PEEK_NEXT:
pString(sb, 1, bp++);
break;
case OPCode.EXACT2:
pString(sb, 2, bp);
bp += 2;
break;
case OPCode.EXACT3:
pString(sb, 3, bp);
bp += 3;
break;
case OPCode.EXACT4:
pString(sb, 4, bp);
bp += 4;
break;
case OPCode.EXACT5:
pString(sb, 5, bp);
bp += 5;
break;
case OPCode.EXACTN:
len = code[bp];
bp += OPSize.LENGTH;
if (Config.USE_STRING_TEMPLATES) {
tm = code[bp];
bp += OPSize.INDEX;
idx = code[bp];
bp += OPSize.INDEX;
pLenStringFromTemplate(sb, len, templates[tm], idx);
} else {
pLenString(sb, len, bp);
bp += len;
}
break;
case OPCode.EXACT1_IC:
pString(sb, 1, bp);
bp++;
break;
case OPCode.EXACTN_IC:
len = code[bp];
bp += OPSize.LENGTH;
if (Config.USE_STRING_TEMPLATES) {
tm = code[bp];
bp += OPSize.INDEX;
idx = code[bp];
bp += OPSize.INDEX;
pLenStringFromTemplate(sb, len, templates[tm], idx);
} else {
pLenString(sb, len, bp);
bp += len;
}
break;
case OPCode.CCLASS:
bs = new BitSet();
System.arraycopy(code, bp, bs.bits, 0, BitSet.BITSET_SIZE);
n = bs.numOn();
bp += BitSet.BITSET_SIZE;
sb.append(":").append(n);
break;
case OPCode.CCLASS_NOT:
bs = new BitSet();
System.arraycopy(code, bp, bs.bits, 0, BitSet.BITSET_SIZE);
n = bs.numOn();
bp += BitSet.BITSET_SIZE;
sb.append(":").append(n);
break;
case OPCode.CCLASS_MB:
case OPCode.CCLASS_MB_NOT:
len = code[bp];
bp += OPSize.LENGTH;
cod = code[bp];
//bp += OPSize.CODE_POINT;
bp += len;
sb.append(":").append(cod).append(":").append(len);
break;
case OPCode.CCLASS_MIX:
case OPCode.CCLASS_MIX_NOT:
bs = new BitSet();
System.arraycopy(code, bp, bs.bits, 0, BitSet.BITSET_SIZE);
n = bs.numOn();
bp += BitSet.BITSET_SIZE;
len = code[bp];
bp += OPSize.LENGTH;
cod = code[bp];
//bp += OPSize.CODE_POINT;
bp += len;
sb.append(":").append(n).append(":").append(cod).append(":").append(len);
break;
case OPCode.CCLASS_NODE:
cc = (CClassNode)operands[code[bp]];
bp += OPSize.POINTER;
n = cc.bs.numOn();
sb.append(":").append(cc).append(":").append(n);
break;
case OPCode.BACKREFN_IC:
mem = code[bp];
bp += OPSize.MEMNUM;
sb.append(":").append(mem);
break;
case OPCode.BACKREF_MULTI_IC:
case OPCode.BACKREF_MULTI:
sb.append(" ");
len = code[bp];
bp += OPSize.LENGTH;
for (int i=0; i<len; i++) {
mem = code[bp];
bp += OPSize.MEMNUM;
if (i > 0) {
sb.append(", ");
}
sb.append(mem);
}
break;
case OPCode.BACKREF_WITH_LEVEL: {
final int option = code[bp];
bp += OPSize.OPTION;
sb.append(":").append(option);
final int level = code[bp];
bp += OPSize.LENGTH;
sb.append(":").append(level);
sb.append(" ");
len = code[bp];
bp += OPSize.LENGTH;
for (int i=0; i<len; i++) {
mem = code[bp];
bp += OPSize.MEMNUM;
if (i > 0) {
sb.append(", ");
}
sb.append(mem);
}
break;
}
case OPCode.REPEAT:
case OPCode.REPEAT_NG:
mem = code[bp];
bp += OPSize.MEMNUM;
addr = code[bp];
bp += OPSize.RELADDR;
sb.append(":").append(mem).append(":").append(addr);
break;
case OPCode.PUSH_OR_JUMP_EXACT1:
case OPCode.PUSH_IF_PEEK_NEXT:
addr = code[bp];
bp += OPSize.RELADDR;
sb.append(":(").append(addr).append(")");
pString(sb, 1, bp);
bp++;
break;
case OPCode.LOOK_BEHIND:
len = code[bp];
bp += OPSize.LENGTH;
sb.append(":").append(len);
break;
case OPCode.PUSH_LOOK_BEHIND_NOT:
addr = code[bp];
bp += OPSize.RELADDR;
len = code[bp];
bp += OPSize.LENGTH;
sb.append(":").append(len).append(":(").append(addr).append(")");
break;
case OPCode.STATE_CHECK_PUSH:
case OPCode.STATE_CHECK_PUSH_OR_JUMP:
scn = code[bp];
bp += OPSize.STATE_CHECK_NUM;
addr = code[bp];
bp += OPSize.RELADDR;
sb.append(":").append(scn).append(":(").append(addr).append(")");
break;
default:
throw new InternalException("undefined code: " + code[--bp]);
}
}
sb.append("]");
// @opcode_address(opcode_size)
if (Config.DEBUG_COMPILE_BYTE_CODE_INFO) {
sb.append("@").append(ip).append("(").append((bp - ip)).append(")");
}
return bp;
}
private String compiledByteCodeListToString() {
final StringBuilder sb = new StringBuilder();
sb.append("code length: ").append(codeLength).append("\n");
int ncode = 0;
int bp = 0;
final int end = codeLength;
while (bp < end) {
ncode++;
if (bp > 0) {
sb.append(ncode % 5 == 0 ? "\n" : " ");
}
bp = compiledByteCodeToString(sb, bp);
}
sb.append("\n");
return sb.toString();
}
}