blob: dbed5d182855f8972dbc9ac84e79d04ab313f0e6 [file] [log] [blame]
/*
* 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 static jdk.nashorn.internal.runtime.regexp.joni.BitStatus.bsAt;
import static jdk.nashorn.internal.runtime.regexp.joni.EncodingHelper.isNewLine;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isFindCondition;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isFindLongest;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isFindNotEmpty;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isNotBol;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isNotEol;
import static jdk.nashorn.internal.runtime.regexp.joni.Option.isPosixRegion;
import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
import jdk.nashorn.internal.runtime.regexp.joni.constants.OPCode;
import jdk.nashorn.internal.runtime.regexp.joni.encoding.IntHolder;
import jdk.nashorn.internal.runtime.regexp.joni.exception.ErrorMessages;
import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
class ByteCodeMachine extends StackMachine {
private int bestLen; // return value
private int s = 0; // current char
private int range; // right range
private int sprev;
private int sstart;
private int sbegin;
private final int[] code; // byte code
private int ip; // instruction pointer
ByteCodeMachine(final Regex regex, final char[] chars, final int p, final int end) {
super(regex, chars, p, end);
this.code = regex.code;
}
private boolean stringCmpIC(final int caseFlodFlag, final int s1p, final IntHolder ps2, final int mbLen, final int textEnd) {
int s1 = s1p;
int s2 = ps2.value;
final int end1 = s1 + mbLen;
while (s1 < end1) {
final char c1 = EncodingHelper.toLowerCase(chars[s1++]);
final char c2 = EncodingHelper.toLowerCase(chars[s2++]);
if (c1 != c2) {
return false;
}
}
ps2.value = s2;
return true;
}
private void debugMatchBegin() {
Config.log.println("match_at: " +
"str: " + str +
", end: " + end +
", start: " + this.sstart +
", sprev: " + this.sprev);
Config.log.println("size: " + (end - str) + ", start offset: " + (this.sstart - str));
}
private void debugMatchLoop() {
if (Config.DEBUG_MATCH) {
Config.log.printf("%4d", (s - str)).print("> \"");
int q, i;
for (i=0, q=s; i<7 && q<end && s>=0; i++) {
if (q < end) {
Config.log.print(new String(new char[]{chars[q++]}));
}
}
final String string = q < end ? "...\"" : "\"";
q += string.length();
Config.log.print(string);
for (i=0; i<20-(q-s);i++) {
Config.log.print(" ");
}
final StringBuilder sb = new StringBuilder();
new ByteCodePrinter(regex).compiledByteCodeToString(sb, ip);
Config.log.println(sb.toString());
}
}
@Override
protected final int matchAt(final int r, final int ss, final int sp) {
this.range = r;
this.sstart = ss;
this.sprev = sp;
stk = 0;
ip = 0;
if (Config.DEBUG_MATCH) {
debugMatchBegin();
}
init();
bestLen = -1;
s = ss;
final int[] c = this.code;
while (true) {
if (Config.DEBUG_MATCH) {
debugMatchLoop();
}
sbegin = s;
switch (c[ip++]) {
case OPCode.END: if (opEnd()) {
return finish();
} break;
case OPCode.EXACT1: opExact1(); break;
case OPCode.EXACT2: opExact2(); continue;
case OPCode.EXACT3: opExact3(); continue;
case OPCode.EXACT4: opExact4(); continue;
case OPCode.EXACT5: opExact5(); continue;
case OPCode.EXACTN: opExactN(); continue;
case OPCode.EXACT1_IC: opExact1IC(); break;
case OPCode.EXACTN_IC: opExactNIC(); continue;
case OPCode.CCLASS: opCClass(); break;
case OPCode.CCLASS_MB: opCClassMB(); break;
case OPCode.CCLASS_MIX: opCClassMIX(); break;
case OPCode.CCLASS_NOT: opCClassNot(); break;
case OPCode.CCLASS_MB_NOT: opCClassMBNot(); break;
case OPCode.CCLASS_MIX_NOT: opCClassMIXNot(); break;
case OPCode.CCLASS_NODE: opCClassNode(); break;
case OPCode.ANYCHAR: opAnyChar(); break;
case OPCode.ANYCHAR_ML: opAnyCharML(); break;
case OPCode.ANYCHAR_STAR: opAnyCharStar(); break;
case OPCode.ANYCHAR_ML_STAR: opAnyCharMLStar(); break;
case OPCode.ANYCHAR_STAR_PEEK_NEXT: opAnyCharStarPeekNext(); break;
case OPCode.ANYCHAR_ML_STAR_PEEK_NEXT: opAnyCharMLStarPeekNext(); break;
case OPCode.WORD: opWord(); break;
case OPCode.NOT_WORD: opNotWord(); break;
case OPCode.WORD_BOUND: opWordBound(); continue;
case OPCode.NOT_WORD_BOUND: opNotWordBound(); continue;
case OPCode.WORD_BEGIN: opWordBegin(); continue;
case OPCode.WORD_END: opWordEnd(); continue;
case OPCode.BEGIN_BUF: opBeginBuf(); continue;
case OPCode.END_BUF: opEndBuf(); continue;
case OPCode.BEGIN_LINE: opBeginLine(); continue;
case OPCode.END_LINE: opEndLine(); continue;
case OPCode.SEMI_END_BUF: opSemiEndBuf(); continue;
case OPCode.BEGIN_POSITION: opBeginPosition(); continue;
case OPCode.MEMORY_START_PUSH: opMemoryStartPush(); continue;
case OPCode.MEMORY_START: opMemoryStart(); continue;
case OPCode.MEMORY_END_PUSH: opMemoryEndPush(); continue;
case OPCode.MEMORY_END: opMemoryEnd(); continue;
case OPCode.MEMORY_END_PUSH_REC: opMemoryEndPushRec(); continue;
case OPCode.MEMORY_END_REC: opMemoryEndRec(); continue;
case OPCode.BACKREF1: opBackRef1(); continue;
case OPCode.BACKREF2: opBackRef2(); continue;
case OPCode.BACKREFN: opBackRefN(); continue;
case OPCode.BACKREFN_IC: opBackRefNIC(); continue;
case OPCode.BACKREF_MULTI: opBackRefMulti(); continue;
case OPCode.BACKREF_MULTI_IC: opBackRefMultiIC(); continue;
case OPCode.BACKREF_WITH_LEVEL: opBackRefAtLevel(); continue;
case OPCode.NULL_CHECK_START: opNullCheckStart(); continue;
case OPCode.NULL_CHECK_END: opNullCheckEnd(); continue;
case OPCode.NULL_CHECK_END_MEMST: opNullCheckEndMemST(); continue;
case OPCode.JUMP: opJump(); continue;
case OPCode.PUSH: opPush(); continue;
case OPCode.POP: opPop(); continue;
case OPCode.PUSH_OR_JUMP_EXACT1: opPushOrJumpExact1(); continue;
case OPCode.PUSH_IF_PEEK_NEXT: opPushIfPeekNext(); continue;
case OPCode.REPEAT: opRepeat(); continue;
case OPCode.REPEAT_NG: opRepeatNG(); continue;
case OPCode.REPEAT_INC: opRepeatInc(); continue;
case OPCode.REPEAT_INC_SG: opRepeatIncSG(); continue;
case OPCode.REPEAT_INC_NG: opRepeatIncNG(); continue;
case OPCode.REPEAT_INC_NG_SG: opRepeatIncNGSG(); continue;
case OPCode.PUSH_POS: opPushPos(); continue;
case OPCode.POP_POS: opPopPos(); continue;
case OPCode.PUSH_POS_NOT: opPushPosNot(); continue;
case OPCode.FAIL_POS: opFailPos(); continue;
case OPCode.PUSH_STOP_BT: opPushStopBT(); continue;
case OPCode.POP_STOP_BT: opPopStopBT(); continue;
case OPCode.LOOK_BEHIND: opLookBehind(); continue;
case OPCode.PUSH_LOOK_BEHIND_NOT: opPushLookBehindNot(); continue;
case OPCode.FAIL_LOOK_BEHIND_NOT: opFailLookBehindNot(); continue;
case OPCode.FINISH:
return finish();
case OPCode.FAIL: opFail(); continue;
default:
throw new InternalException(ErrorMessages.ERR_UNDEFINED_BYTECODE);
} // main switch
} // main while
}
private boolean opEnd() {
final int n = s - sstart;
if (n > bestLen) {
if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) {
if (isFindLongest(regex.options)) {
if (n > msaBestLen) {
msaBestLen = n;
msaBestS = sstart;
} else {
// goto end_best_len;
return endBestLength();
}
}
} // USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE
bestLen = n;
final Region region = msaRegion;
if (region != null) {
// USE_POSIX_REGION_OPTION ... else ...
region.beg[0] = msaBegin = sstart - str;
region.end[0] = msaEnd = s - str;
for (int i = 1; i <= regex.numMem; i++) {
// opt!
if (repeatStk[memEndStk + i] != INVALID_INDEX) {
region.beg[i] = bsAt(regex.btMemStart, i) ?
stack[repeatStk[memStartStk + i]].getMemPStr() - str :
repeatStk[memStartStk + i] - str;
region.end[i] = bsAt(regex.btMemEnd, i) ?
stack[repeatStk[memEndStk + i]].getMemPStr() :
repeatStk[memEndStk + i] - str;
} else {
region.beg[i] = region.end[i] = Region.REGION_NOTPOS;
}
}
} else {
msaBegin = sstart - str;
msaEnd = s - str;
}
} else {
final Region region = msaRegion;
if (Config.USE_POSIX_API_REGION_OPTION) {
if (!isPosixRegion(regex.options)) {
if (region != null) {
region.clear();
} else {
msaBegin = msaEnd = 0;
}
}
} else {
if (region != null) {
region.clear();
} else {
msaBegin = msaEnd = 0;
}
} // USE_POSIX_REGION_OPTION
}
// end_best_len:
/* default behavior: return first-matching result. */
return endBestLength();
}
private boolean endBestLength() {
if (isFindCondition(regex.options)) {
if (isFindNotEmpty(regex.options) && s == sstart) {
bestLen = -1;
{opFail(); return false;} /* for retry */
}
if (isFindLongest(regex.options) && s < range) {
{opFail(); return false;} /* for retry */
}
}
// goto finish;
return true;
}
private void opExact1() {
if (s >= range || code[ip] != chars[s++]) {opFail(); return;}
//if (s > range) {opFail(); return;}
ip++;
sprev = sbegin; // break;
}
private void opExact2() {
if (s + 2 > range) {opFail(); return;}
if (code[ip] != chars[s]) {opFail(); return;}
ip++; s++;
if (code[ip] != chars[s]) {opFail(); return;}
sprev = s;
ip++; s++;
}
private void opExact3() {
if (s + 3 > range) {opFail(); return;}
if (code[ip] != chars[s]) {opFail(); return;}
ip++; s++;
if (code[ip] != chars[s]) {opFail(); return;}
ip++; s++;
if (code[ip] != chars[s]) {opFail(); return;}
sprev = s;
ip++; s++;
}
private void opExact4() {
if (s + 4 > range) {opFail(); return;}
if (code[ip] != chars[s]) {opFail(); return;}
ip++; s++;
if (code[ip] != chars[s]) {opFail(); return;}
ip++; s++;
if (code[ip] != chars[s]) {opFail(); return;}
ip++; s++;
if (code[ip] != chars[s]) {opFail(); return;}
sprev = s;
ip++; s++;
}
private void opExact5() {
if (s + 5 > range) {opFail(); return;}
if (code[ip] != chars[s]) {opFail(); return;}
ip++; s++;
if (code[ip] != chars[s]) {opFail(); return;}
ip++; s++;
if (code[ip] != chars[s]) {opFail(); return;}
ip++; s++;
if (code[ip] != chars[s]) {opFail(); return;}
ip++; s++;
if (code[ip] != chars[s]) {opFail(); return;}
sprev = s;
ip++; s++;
}
private void opExactN() {
int tlen = code[ip++];
if (s + tlen > range) {opFail(); return;}
if (Config.USE_STRING_TEMPLATES) {
final char[] bs = regex.templates[code[ip++]];
int ps = code[ip++];
while (tlen-- > 0) {
if (bs[ps++] != chars[s++]) {opFail(); return;}
}
} else {
while (tlen-- > 0) {
if (code[ip++] != chars[s++]) {opFail(); return;}
}
}
sprev = s - 1;
}
private void opExact1IC() {
if (s >= range || code[ip] != EncodingHelper.toLowerCase(chars[s++])) {opFail(); return;}
ip++;
sprev = sbegin; // break;
}
private void opExactNIC() {
int tlen = code[ip++];
if (s + tlen > range) {opFail(); return;}
if (Config.USE_STRING_TEMPLATES) {
final char[] bs = regex.templates[code[ip++]];
int ps = code[ip++];
while (tlen-- > 0) {
if (bs[ps++] != EncodingHelper.toLowerCase(chars[s++])) {opFail(); return;}
}
} else {
while (tlen-- > 0) {
if (code[ip++] != EncodingHelper.toLowerCase(chars[s++])) {opFail(); return;}
}
}
sprev = s - 1;
}
private boolean isInBitSet() {
final int c = chars[s];
return (c <= 0xff && (code[ip + (c >>> BitSet.ROOM_SHIFT)] & (1 << c)) != 0);
}
private void opCClass() {
if (s >= range || !isInBitSet()) {opFail(); return;}
ip += BitSet.BITSET_SIZE;
s++;
sprev = sbegin; // break;
}
private boolean isInClassMB() {
final int tlen = code[ip++];
if (s >= range) {
return false;
}
final int ss = s;
s++;
final int c = chars[ss];
if (!EncodingHelper.isInCodeRange(code, ip, c)) {
return false;
}
ip += tlen;
return true;
}
private void opCClassMB() {
// beyond string check
if (s >= range || chars[s] <= 0xff) {opFail(); return;}
if (!isInClassMB()) {opFail(); return;} // not!!!
sprev = sbegin; // break;
}
private void opCClassMIX() {
if (s >= range) {opFail(); return;}
if (chars[s] > 0xff) {
ip += BitSet.BITSET_SIZE;
if (!isInClassMB()) {opFail(); return;}
} else {
if (!isInBitSet()) {opFail(); return;}
ip += BitSet.BITSET_SIZE;
final int tlen = code[ip++]; // by code range length
ip += tlen;
s++;
}
sprev = sbegin; // break;
}
private void opCClassNot() {
if (s >= range || isInBitSet()) {opFail(); return;}
ip += BitSet.BITSET_SIZE;
s++;
sprev = sbegin; // break;
}
private boolean isNotInClassMB() {
final int tlen = code[ip++];
if (!(s + 1 <= range)) {
if (s >= range) {
return false;
}
s = end;
ip += tlen;
return true;
}
final int ss = s;
s++;
final int c = chars[ss];
if (EncodingHelper.isInCodeRange(code, ip, c)) {
return false;
}
ip += tlen;
return true;
}
private void opCClassMBNot() {
if (s >= range) {opFail(); return;}
if (chars[s] <= 0xff) {
s++;
final int tlen = code[ip++];
ip += tlen;
sprev = sbegin; // break;
return;
}
if (!isNotInClassMB()) {opFail(); return;}
sprev = sbegin; // break;
}
private void opCClassMIXNot() {
if (s >= range) {opFail(); return;}
if (chars[s] > 0xff) {
ip += BitSet.BITSET_SIZE;
if (!isNotInClassMB()) {opFail(); return;}
} else {
if (isInBitSet()) {opFail(); return;}
ip += BitSet.BITSET_SIZE;
final int tlen = code[ip++];
ip += tlen;
s++;
}
sprev = sbegin; // break;
}
private void opCClassNode() {
if (s >= range) {opFail(); return;}
final CClassNode cc = (CClassNode)regex.operands[code[ip++]];
final int ss = s;
s++;
final int c = chars[ss];
if (!cc.isCodeInCCLength(c)) {opFail(); return;}
sprev = sbegin; // break;
}
private void opAnyChar() {
if (s >= range) {opFail(); return;}
if (isNewLine(chars[s])) {opFail(); return;}
s++;
sprev = sbegin; // break;
}
private void opAnyCharML() {
if (s >= range) {opFail(); return;}
s++;
sprev = sbegin; // break;
}
private void opAnyCharStar() {
final char[] ch = this.chars;
while (s < range) {
pushAlt(ip, s, sprev);
if (isNewLine(ch, s, end)) {opFail(); return;}
sprev = s;
s++;
}
sprev = sbegin; // break;
}
private void opAnyCharMLStar() {
while (s < range) {
pushAlt(ip, s, sprev);
sprev = s;
s++;
}
sprev = sbegin; // break;
}
private void opAnyCharStarPeekNext() {
final char c = (char)code[ip];
final char[] ch = this.chars;
while (s < range) {
final char b = ch[s];
if (c == b) {
pushAlt(ip + 1, s, sprev);
}
if (isNewLine(b)) {opFail(); return;}
sprev = s;
s++;
}
ip++;
sprev = sbegin; // break;
}
private void opAnyCharMLStarPeekNext() {
final char c = (char)code[ip];
final char[] ch = this.chars;
while (s < range) {
if (c == ch[s]) {
pushAlt(ip + 1, s, sprev);
}
sprev = s;
s++;
}
ip++;
sprev = sbegin; // break;
}
private void opWord() {
if (s >= range || !EncodingHelper.isWord(chars[s])) {opFail(); return;}
s++;
sprev = sbegin; // break;
}
private void opNotWord() {
if (s >= range || EncodingHelper.isWord(chars[s])) {opFail(); return;}
s++;
sprev = sbegin; // break;
}
private void opWordBound() {
if (s == str) {
if (s >= range || !EncodingHelper.isWord(chars[s])) {opFail(); return;}
} else if (s == end) {
if (sprev >= end || !EncodingHelper.isWord(chars[sprev])) {opFail(); return;}
} else {
if (EncodingHelper.isWord(chars[s]) == EncodingHelper.isWord(chars[sprev])) {opFail(); return;}
}
}
private void opNotWordBound() {
if (s == str) {
if (s < range && EncodingHelper.isWord(chars[s])) {opFail(); return;}
} else if (s == end) {
if (sprev < end && EncodingHelper.isWord(chars[sprev])) {opFail(); return;}
} else {
if (EncodingHelper.isWord(chars[s]) != EncodingHelper.isWord(chars[sprev])) {opFail(); return;}
}
}
private void opWordBegin() {
if (s < range && EncodingHelper.isWord(chars[s])) {
if (s == str || !EncodingHelper.isWord(chars[sprev])) {
return;
}
}
opFail();
}
private void opWordEnd() {
if (s != str && EncodingHelper.isWord(chars[sprev])) {
if (s == end || !EncodingHelper.isWord(chars[s])) {
return;
}
}
opFail();
}
private void opBeginBuf() {
if (s != str) {
opFail();
}
}
private void opEndBuf() {
if (s != end) {
opFail();
}
}
private void opBeginLine() {
if (s == str) {
if (isNotBol(msaOptions)) {
opFail();
}
return;
} else if (isNewLine(chars, sprev, end) && s != end) {
return;
}
opFail();
}
private void opEndLine() {
if (s == end) {
if (Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
if (str == end || !isNewLine(chars, sprev, end)) {
if (isNotEol(msaOptions)) {
opFail();
}
}
return;
}
if (isNotEol(msaOptions)) {
opFail();
}
return;
} else if (isNewLine(chars, s, end)) {
return;
}
opFail();
}
private void opSemiEndBuf() {
if (s == end) {
if (Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
if (str == end || !isNewLine(chars, sprev, end)) {
if (isNotEol(msaOptions)) {
opFail();
}
}
return;
}
if (isNotEol(msaOptions)) {
opFail();
}
return;
} else if (isNewLine(chars, s, end) && s + 1 == end) {
return;
}
opFail();
}
private void opBeginPosition() {
if (s != msaStart) {
opFail();
}
}
private void opMemoryStartPush() {
final int mem = code[ip++];
pushMemStart(mem, s);
}
private void opMemoryStart() {
final int mem = code[ip++];
repeatStk[memStartStk + mem] = s;
}
private void opMemoryEndPush() {
final int mem = code[ip++];
pushMemEnd(mem, s);
}
private void opMemoryEnd() {
final int mem = code[ip++];
repeatStk[memEndStk + mem] = s;
}
private void opMemoryEndPushRec() {
final int mem = code[ip++];
final int stkp = getMemStart(mem); /* should be before push mem-end. */
pushMemEnd(mem, s);
repeatStk[memStartStk + mem] = stkp;
}
private void opMemoryEndRec() {
final int mem = code[ip++];
repeatStk[memEndStk + mem] = s;
final int stkp = getMemStart(mem);
if (BitStatus.bsAt(regex.btMemStart, mem)) {
repeatStk[memStartStk + mem] = stkp;
} else {
repeatStk[memStartStk + mem] = stack[stkp].getMemPStr();
}
pushMemEndMark(mem);
}
private boolean backrefInvalid(final int mem) {
return repeatStk[memEndStk + mem] == INVALID_INDEX || repeatStk[memStartStk + mem] == INVALID_INDEX;
}
private int backrefStart(final int mem) {
return bsAt(regex.btMemStart, mem) ? stack[repeatStk[memStartStk + mem]].getMemPStr() : repeatStk[memStartStk + mem];
}
private int backrefEnd(final int mem) {
return bsAt(regex.btMemEnd, mem) ? stack[repeatStk[memEndStk + mem]].getMemPStr() : repeatStk[memEndStk + mem];
}
private void backref(final int mem) {
/* if you want to remove following line,
you should check in parse and compile time. (numMem) */
if (mem > regex.numMem || backrefInvalid(mem)) {opFail(); return;}
int pstart = backrefStart(mem);
final int pend = backrefEnd(mem);
int n = pend - pstart;
if (s + n > range) {opFail(); return;}
sprev = s;
// STRING_CMP
while(n-- > 0) {
if (chars[pstart++] != chars[s++]) {opFail(); return;}
}
// beyond string check
if (sprev < range) {
while (sprev + 1 < s) {
sprev++;
}
}
}
private void opBackRef1() {
backref(1);
}
private void opBackRef2() {
backref(2);
}
private void opBackRefN() {
backref(code[ip++]);
}
private void opBackRefNIC() {
final int mem = code[ip++];
/* if you want to remove following line,
you should check in parse and compile time. (numMem) */
if (mem > regex.numMem || backrefInvalid(mem)) {opFail(); return;}
final int pstart = backrefStart(mem);
final int pend = backrefEnd(mem);
final int n = pend - pstart;
if (s + n > range) {opFail(); return;}
sprev = s;
value = s;
if (!stringCmpIC(regex.caseFoldFlag, pstart, this, n, end)) {opFail(); return;}
s = value;
// if (sprev < chars.length)
while (sprev + 1 < s) {
sprev++;
}
}
private void opBackRefMulti() {
final int tlen = code[ip++];
int i;
loop:for (i=0; i<tlen; i++) {
final int mem = code[ip++];
if (backrefInvalid(mem)) {
continue;
}
int pstart = backrefStart(mem);
final int pend = backrefEnd(mem);
int n = pend - pstart;
if (s + n > range) {opFail(); return;}
sprev = s;
int swork = s;
while (n-- > 0) {
if (chars[pstart++] != chars[swork++]) {
continue loop;
}
}
s = swork;
// beyond string check
if (sprev < range) {
while (sprev + 1 < s) {
sprev++;
}
}
ip += tlen - i - 1; // * SIZE_MEMNUM (1)
break; /* success */
}
if (i == tlen) {opFail(); return;}
}
private void opBackRefMultiIC() {
final int tlen = code[ip++];
int i;
loop:for (i=0; i<tlen; i++) {
final int mem = code[ip++];
if (backrefInvalid(mem)) {
continue;
}
final int pstart = backrefStart(mem);
final int pend = backrefEnd(mem);
final int n = pend - pstart;
if (s + n > range) {opFail(); return;}
sprev = s;
value = s;
if (!stringCmpIC(regex.caseFoldFlag, pstart, this, n, end))
{
continue loop; // STRING_CMP_VALUE_IC
}
s = value;
// if (sprev < chars.length)
while (sprev + 1 < s) {
sprev++;
}
ip += tlen - i - 1; // * SIZE_MEMNUM (1)
break; /* success */
}
if (i == tlen) {opFail(); return;}
}
private boolean memIsInMemp(final int mem, final int num, final int mempp) {
for (int i=0, memp = mempp; i<num; i++) {
final int m = code[memp++];
if (mem == m) {
return true;
}
}
return false;
}
// USE_BACKREF_AT_LEVEL // (s) and (end) implicit
private boolean backrefMatchAtNestedLevel(final boolean ignoreCase, final int caseFoldFlag,
final int nest, final int memNum, final int memp) {
int pend = -1;
int level = 0;
int k = stk - 1;
while (k >= 0) {
final StackEntry e = stack[k];
if (e.type == CALL_FRAME) {
level--;
} else if (e.type == RETURN) {
level++;
} else if (level == nest) {
if (e.type == MEM_START) {
if (memIsInMemp(e.getMemNum(), memNum, memp)) {
final int pstart = e.getMemPStr();
if (pend != -1) {
if (pend - pstart > end - s) {
return false; /* or goto next_mem; */
}
int p = pstart;
value = s;
if (ignoreCase) {
if (!stringCmpIC(caseFoldFlag, pstart, this, pend - pstart, end)) {
return false; /* or goto next_mem; */
}
} else {
while (p < pend) {
if (chars[p++] != chars[value++]) {
return false; /* or goto next_mem; */
}
}
}
s = value;
return true;
}
}
} else if (e.type == MEM_END) {
if (memIsInMemp(e.getMemNum(), memNum, memp)) {
pend = e.getMemPStr();
}
}
}
k--;
}
return false;
}
private void opBackRefAtLevel() {
final int ic = code[ip++];
final int level = code[ip++];
final int tlen = code[ip++];
sprev = s;
if (backrefMatchAtNestedLevel(ic != 0, regex.caseFoldFlag, level, tlen, ip)) { // (s) and (end) implicit
while (sprev + 1 < s) {
sprev++;
}
ip += tlen; // * SIZE_MEMNUM
} else {
{opFail(); return;}
}
}
private void opNullCheckStart() {
final int mem = code[ip++];
pushNullCheckStart(mem, s);
}
private void nullCheckFound() {
// null_check_found:
/* empty loop founded, skip next instruction */
switch(code[ip++]) {
case OPCode.JUMP:
case OPCode.PUSH:
ip++; // p += SIZE_RELADDR;
break;
case OPCode.REPEAT_INC:
case OPCode.REPEAT_INC_NG:
case OPCode.REPEAT_INC_SG:
case OPCode.REPEAT_INC_NG_SG:
ip++; // p += SIZE_MEMNUM;
break;
default:
throw new InternalException(ErrorMessages.ERR_UNEXPECTED_BYTECODE);
} // switch
}
private void opNullCheckEnd() {
final int mem = code[ip++];
final int isNull = nullCheck(mem, s); /* mem: null check id */
if (isNull != 0) {
if (Config.DEBUG_MATCH) {
Config.log.println("NULL_CHECK_END: skip id:" + mem + ", s:" + s);
}
nullCheckFound();
}
}
// USE_INFINITE_REPEAT_MONOMANIAC_MEM_STATUS_CHECK
private void opNullCheckEndMemST() {
final int mem = code[ip++]; /* mem: null check id */
final int isNull = nullCheckMemSt(mem, s);
if (isNull != 0) {
if (Config.DEBUG_MATCH) {
Config.log.println("NULL_CHECK_END_MEMST: skip id:" + mem + ", s:" + s);
}
if (isNull == -1) {opFail(); return;}
nullCheckFound();
}
}
private void opJump() {
ip += code[ip] + 1;
}
private void opPush() {
final int addr = code[ip++];
pushAlt(ip + addr, s, sprev);
}
private void opPop() {
popOne();
}
private void opPushOrJumpExact1() {
final int addr = code[ip++];
// beyond string check
if (s < range && code[ip] == chars[s]) {
ip++;
pushAlt(ip + addr, s, sprev);
return;
}
ip += addr + 1;
}
private void opPushIfPeekNext() {
final int addr = code[ip++];
// beyond string check
if (s < range && code[ip] == chars[s]) {
ip++;
pushAlt(ip + addr, s, sprev);
return;
}
ip++;
}
private void opRepeat() {
final int mem = code[ip++]; /* mem: OP_REPEAT ID */
final int addr= code[ip++];
// ensure1();
repeatStk[mem] = stk;
pushRepeat(mem, ip);
if (regex.repeatRangeLo[mem] == 0) { // lower
pushAlt(ip + addr, s, sprev);
}
}
private void opRepeatNG() {
final int mem = code[ip++]; /* mem: OP_REPEAT ID */
final int addr= code[ip++];
// ensure1();
repeatStk[mem] = stk;
pushRepeat(mem, ip);
if (regex.repeatRangeLo[mem] == 0) {
pushAlt(ip, s, sprev);
ip += addr;
}
}
private void repeatInc(final int mem, final int si) {
final StackEntry e = stack[si];
e.increaseRepeatCount();
if (e.getRepeatCount() >= regex.repeatRangeHi[mem]) {
/* end of repeat. Nothing to do. */
} else if (e.getRepeatCount() >= regex.repeatRangeLo[mem]) {
pushAlt(ip, s, sprev);
ip = e.getRepeatPCode(); /* Don't use stkp after PUSH. */
} else {
ip = e.getRepeatPCode();
}
pushRepeatInc(si);
}
private void opRepeatInc() {
final int mem = code[ip++]; /* mem: OP_REPEAT ID */
final int si = repeatStk[mem];
repeatInc(mem, si);
}
private void opRepeatIncSG() {
final int mem = code[ip++]; /* mem: OP_REPEAT ID */
final int si = getRepeat(mem);
repeatInc(mem, si);
}
private void repeatIncNG(final int mem, final int si) {
final StackEntry e = stack[si];
e.increaseRepeatCount();
if (e.getRepeatCount() < regex.repeatRangeHi[mem]) {
if (e.getRepeatCount() >= regex.repeatRangeLo[mem]) {
final int pcode = e.getRepeatPCode();
pushRepeatInc(si);
pushAlt(pcode, s, sprev);
} else {
ip = e.getRepeatPCode();
pushRepeatInc(si);
}
} else if (e.getRepeatCount() == regex.repeatRangeHi[mem]) {
pushRepeatInc(si);
}
}
private void opRepeatIncNG() {
final int mem = code[ip++];
final int si = repeatStk[mem];
repeatIncNG(mem, si);
}
private void opRepeatIncNGSG() {
final int mem = code[ip++];
final int si = getRepeat(mem);
repeatIncNG(mem, si);
}
private void opPushPos() {
pushPos(s, sprev);
}
private void opPopPos() {
final StackEntry e = stack[posEnd()];
s = e.getStatePStr();
sprev= e.getStatePStrPrev();
}
private void opPushPosNot() {
final int addr = code[ip++];
pushPosNot(ip + addr, s, sprev);
}
private void opFailPos() {
popTilPosNot();
opFail();
}
private void opPushStopBT() {
pushStopBT();
}
private void opPopStopBT() {
stopBtEnd();
}
private void opLookBehind() {
final int tlen = code[ip++];
s = EncodingHelper.stepBack(str, s, tlen);
if (s == -1) {opFail(); return;}
sprev = EncodingHelper.prevCharHead(str, s);
}
private void opPushLookBehindNot() {
final int addr = code[ip++];
final int tlen = code[ip++];
final int q = EncodingHelper.stepBack(str, s, tlen);
if (q == -1) {
/* too short case -> success. ex. /(?<!XXX)a/.match("a")
If you want to change to fail, replace following line. */
ip += addr;
// return FAIL;
} else {
pushLookBehindNot(ip + addr, s, sprev);
s = q;
sprev = EncodingHelper.prevCharHead(str, s);
}
}
private void opFailLookBehindNot() {
popTilLookBehindNot();
opFail();
}
private void opFail() {
if (stack == null) {
ip = regex.codeLength - 1;
return;
}
final StackEntry e = pop();
ip = e.getStatePCode();
s = e.getStatePStr();
sprev = e.getStatePStrPrev();
}
private int finish() {
return bestLen;
}
}