changeset 114:e42fd1640ff9

8006028: Integrate Joni regexp engine with Nashorn Reviewed-by: lagergren, attila
author hannesw
date Fri, 22 Feb 2013 17:00:22 +0100
parents 508da3c7fc3a
children 7f5b7c6859d7
files THIRD_PARTY_README docs/DEVELOPER_README src/jdk/nashorn/internal/runtime/regexp/JoniRegExp.java src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java src/jdk/nashorn/internal/runtime/regexp/joni/Analyser.java src/jdk/nashorn/internal/runtime/regexp/joni/ApplyCaseFold.java src/jdk/nashorn/internal/runtime/regexp/joni/ApplyCaseFoldArg.java src/jdk/nashorn/internal/runtime/regexp/joni/ArrayCompiler.java src/jdk/nashorn/internal/runtime/regexp/joni/AsmCompiler.java src/jdk/nashorn/internal/runtime/regexp/joni/AsmCompilerSupport.java src/jdk/nashorn/internal/runtime/regexp/joni/BitSet.java src/jdk/nashorn/internal/runtime/regexp/joni/BitStatus.java src/jdk/nashorn/internal/runtime/regexp/joni/ByteCodeMachine.java src/jdk/nashorn/internal/runtime/regexp/joni/ByteCodePrinter.java src/jdk/nashorn/internal/runtime/regexp/joni/CaptureTreeNode.java src/jdk/nashorn/internal/runtime/regexp/joni/CodeRangeBuffer.java src/jdk/nashorn/internal/runtime/regexp/joni/Compiler.java src/jdk/nashorn/internal/runtime/regexp/joni/Config.java src/jdk/nashorn/internal/runtime/regexp/joni/EncodingHelper.java src/jdk/nashorn/internal/runtime/regexp/joni/Lexer.java src/jdk/nashorn/internal/runtime/regexp/joni/Matcher.java src/jdk/nashorn/internal/runtime/regexp/joni/MatcherFactory.java src/jdk/nashorn/internal/runtime/regexp/joni/MinMaxLen.java src/jdk/nashorn/internal/runtime/regexp/joni/NameEntry.java src/jdk/nashorn/internal/runtime/regexp/joni/NativeMachine.java src/jdk/nashorn/internal/runtime/regexp/joni/NodeOptInfo.java src/jdk/nashorn/internal/runtime/regexp/joni/OptAnchorInfo.java src/jdk/nashorn/internal/runtime/regexp/joni/OptEnvironment.java src/jdk/nashorn/internal/runtime/regexp/joni/OptExactInfo.java src/jdk/nashorn/internal/runtime/regexp/joni/OptMapInfo.java src/jdk/nashorn/internal/runtime/regexp/joni/Option.java src/jdk/nashorn/internal/runtime/regexp/joni/Parser.java src/jdk/nashorn/internal/runtime/regexp/joni/Regex.java src/jdk/nashorn/internal/runtime/regexp/joni/Region.java src/jdk/nashorn/internal/runtime/regexp/joni/ScanEnvironment.java src/jdk/nashorn/internal/runtime/regexp/joni/ScannerSupport.java src/jdk/nashorn/internal/runtime/regexp/joni/SearchAlgorithm.java src/jdk/nashorn/internal/runtime/regexp/joni/StackEntry.java src/jdk/nashorn/internal/runtime/regexp/joni/StackMachine.java src/jdk/nashorn/internal/runtime/regexp/joni/Syntax.java src/jdk/nashorn/internal/runtime/regexp/joni/Token.java src/jdk/nashorn/internal/runtime/regexp/joni/UnsetAddrList.java src/jdk/nashorn/internal/runtime/regexp/joni/WarnCallback.java src/jdk/nashorn/internal/runtime/regexp/joni/Warnings.java src/jdk/nashorn/internal/runtime/regexp/joni/ast/AnchorNode.java src/jdk/nashorn/internal/runtime/regexp/joni/ast/AnyCharNode.java src/jdk/nashorn/internal/runtime/regexp/joni/ast/BackRefNode.java src/jdk/nashorn/internal/runtime/regexp/joni/ast/CClassNode.java src/jdk/nashorn/internal/runtime/regexp/joni/ast/CTypeNode.java src/jdk/nashorn/internal/runtime/regexp/joni/ast/CallNode.java src/jdk/nashorn/internal/runtime/regexp/joni/ast/ConsAltNode.java src/jdk/nashorn/internal/runtime/regexp/joni/ast/EncloseNode.java src/jdk/nashorn/internal/runtime/regexp/joni/ast/Node.java src/jdk/nashorn/internal/runtime/regexp/joni/ast/QuantifierNode.java src/jdk/nashorn/internal/runtime/regexp/joni/ast/StateNode.java src/jdk/nashorn/internal/runtime/regexp/joni/ast/StringNode.java src/jdk/nashorn/internal/runtime/regexp/joni/bench/AbstractBench.java src/jdk/nashorn/internal/runtime/regexp/joni/bench/BenchGreedyBacktrack.java src/jdk/nashorn/internal/runtime/regexp/joni/bench/BenchRailsRegs.java src/jdk/nashorn/internal/runtime/regexp/joni/bench/BenchSeveralRegexps.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/AnchorType.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/Arguments.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/AsmConstants.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/CCSTATE.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/CCVALTYPE.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/EncloseType.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/MetaChar.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/NodeStatus.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/NodeType.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/OPCode.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/OPSize.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/Reduce.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/RegexState.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/StackPopLevel.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/StackType.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/StringType.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/SyntaxProperties.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/TargetInfo.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/TokenType.java src/jdk/nashorn/internal/runtime/regexp/joni/constants/Traverse.java src/jdk/nashorn/internal/runtime/regexp/joni/encoding/AsciiTables.java src/jdk/nashorn/internal/runtime/regexp/joni/encoding/CharacterType.java src/jdk/nashorn/internal/runtime/regexp/joni/encoding/IntHolder.java src/jdk/nashorn/internal/runtime/regexp/joni/encoding/ObjPtr.java src/jdk/nashorn/internal/runtime/regexp/joni/encoding/PosixBracket.java src/jdk/nashorn/internal/runtime/regexp/joni/encoding/Ptr.java src/jdk/nashorn/internal/runtime/regexp/joni/exception/ErrorMessages.java src/jdk/nashorn/internal/runtime/regexp/joni/exception/InternalException.java src/jdk/nashorn/internal/runtime/regexp/joni/exception/JOniException.java src/jdk/nashorn/internal/runtime/regexp/joni/exception/SyntaxException.java src/jdk/nashorn/internal/runtime/regexp/joni/exception/ValueException.java
diffstat 91 files changed, 17781 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/THIRD_PARTY_README	Fri Feb 22 16:31:10 2013 +0100
+++ b/THIRD_PARTY_README	Fri Feb 22 17:00:22 2013 +0100
@@ -98,3 +98,26 @@
 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 --- end of LICENSE ---
+
+%% This notice is provided with respect to Joni library which is included
+with the Nashorn technology.
+
+--- begin of LICENSE ---
+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.
+--- end of LICENSE ---
--- a/docs/DEVELOPER_README	Fri Feb 22 16:31:10 2013 +0100
+++ b/docs/DEVELOPER_README	Fri Feb 22 17:00:22 2013 +0100
@@ -338,6 +338,15 @@
 this system property.
 
 
+SYSTEM_PROPERTY: nashorn.regexp.impl=[jdk|joni]
+
+This property defines the regular expression engine to be used by
+Nashorn. The default implementation is "jdk" which is based on the
+JDK's java.util.regex package. Set this property to "joni" to install
+an implementation based on Joni, the regular expression engine used by
+the JRuby project.
+
+
 ===============
 2. The loggers.
 ===============
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/JoniRegExp.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime.regexp;
+
+import jdk.nashorn.internal.runtime.ParserException;
+import jdk.nashorn.internal.runtime.regexp.joni.Matcher;
+import jdk.nashorn.internal.runtime.regexp.joni.Option;
+import jdk.nashorn.internal.runtime.regexp.joni.Regex;
+import jdk.nashorn.internal.runtime.regexp.joni.Region;
+import jdk.nashorn.internal.runtime.regexp.joni.Syntax;
+import jdk.nashorn.internal.runtime.regexp.joni.exception.JOniException;
+
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * Regular expression implementation based on the Joni engine from the JRuby project.
+ */
+public class JoniRegExp extends RegExp {
+
+    /** Compiled Joni Regex */
+    private Regex regex;
+
+    /** Matcher */
+    private RegExpMatcher matcher;
+
+    /**
+     * Construct a Regular expression from the given {@code pattern} and {@code flags} strings.
+     *
+     * @param pattern RegExp pattern string
+     * @param flags RegExp flag string
+     * @throws ParserException if flags is invalid or pattern string has syntax error.
+     */
+    public JoniRegExp(final String pattern, final String flags) throws ParserException {
+        super(pattern, flags);
+
+        int option = Option.SINGLELINE;
+
+        if (this.isIgnoreCase()) {
+            option |= Option.IGNORECASE;
+        }
+        if (this.isMultiline()) {
+            option &= ~Option.SINGLELINE;
+            option |= Option.NEGATE_SINGLELINE;
+        }
+
+        try {
+            RegExpScanner parsed;
+
+            try {
+                parsed = RegExpScanner.scan(pattern);
+            } catch (final PatternSyntaxException e) {
+                // refine the exception with a better syntax error, if this
+                // passes, just rethrow what we have
+                Pattern.compile(pattern, 0);
+                throw e;
+            }
+
+            if (parsed != null) {
+                char[] javaPattern = parsed.getJavaPattern().toCharArray();
+                this.regex = new Regex(javaPattern, 0, javaPattern.length, option, Syntax.JAVASCRIPT);
+                this.groupsInNegativeLookahead = parsed.getGroupsInNegativeLookahead();
+            }
+        } catch (final PatternSyntaxException e2) {
+            throwParserException("syntax", e2.getMessage());
+        } catch (JOniException e2) {
+            throwParserException("syntax", e2.getMessage());
+        }
+    }
+
+    @Override
+    public RegExpMatcher match(final String input) {
+        if (regex == null) {
+            return null;
+        }
+
+        RegExpMatcher matcher = this.matcher;
+
+        if (matcher == null || input != matcher.getInput()) {
+            matcher = new JoniMatcher(input);
+            this.matcher = matcher;
+        }
+
+        return matcher;
+    }
+
+    /**
+     * RegExp Factory class for Joni regexp engine.
+     */
+    public static class Factory extends RegExpFactory {
+
+        @Override
+        protected RegExp compile(final String pattern, final String flags) throws ParserException {
+            return new JoniRegExp(pattern, flags);
+        }
+
+        @Override
+        protected String replaceToken(final String str) {
+            return str.equals("[^]") ? "[\\s\\S]" : str;
+        }
+    }
+
+    class JoniMatcher implements RegExpMatcher {
+        final String input;
+        final Matcher matcher;
+
+        JoniMatcher(final String input) {
+            this.input = input;
+            this.matcher = regex.matcher(input.toCharArray());
+        }
+
+        @Override
+        public boolean search(final int start) {
+            return matcher.search(start, input.length(), Option.NONE) > -1;
+        }
+
+        @Override
+        public String getInput() {
+            return input;
+        }
+
+        @Override
+        public int start() {
+            return matcher.getBegin();
+        }
+
+        @Override
+        public int start(final int group) {
+            return group == 0 ? start() : matcher.getRegion().beg[group];
+        }
+
+        @Override
+        public int end() {
+            return matcher.getEnd();
+        }
+
+        @Override
+        public int end(final int group) {
+            return group == 0 ? end() : matcher.getRegion().end[group];
+        }
+
+        @Override
+        public String group() {
+            return input.substring(matcher.getBegin(), matcher.getEnd());
+        }
+
+        @Override
+        public String group(final int group) {
+            if (group == 0) {
+                return group();
+            }
+            final Region region = matcher.getRegion();
+            return input.substring(region.beg[group], region.end[group]);
+        }
+
+        @Override
+        public int groupCount() {
+            final Region region = matcher.getRegion();
+            return region == null ? 0 : region.numRegs - 1;
+        }
+    }
+}
--- a/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java	Fri Feb 22 16:31:10 2013 +0100
+++ b/src/jdk/nashorn/internal/runtime/regexp/RegExpFactory.java	Fri Feb 22 17:00:22 2013 +0100
@@ -27,14 +27,34 @@
 
 import jdk.nashorn.internal.parser.Lexer;
 import jdk.nashorn.internal.runtime.ParserException;
+import jdk.nashorn.internal.runtime.options.Options;
 
 /**
  * Factory class for regular expressions. This class creates instances of {@link DefaultRegExp}.
+ * An alternative factory can be installed using the {@code nashorn.regexp.impl} system property.
  */
 public class RegExpFactory {
 
 
-    private final static RegExpFactory instance = new RegExpFactory();
+    private final static RegExpFactory instance;
+
+    private final static String JDK  = "jdk";
+    private final static String JONI = "joni";
+
+    static {
+        final String impl = Options.getStringProperty("nashorn.regexp.impl", JDK);
+        switch (impl) {
+            case JONI:
+                instance = new JoniRegExp.Factory();
+                break;
+            case JDK:
+                instance = new RegExpFactory();
+                break;
+            default:
+                instance = null;
+                throw new InternalError("Unsupported RegExp factory: " + impl);
+        }
+    }
 
     /**
      * Creates a Regular expression from the given {@code pattern} and {@code flags} strings.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/Analyser.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,2162 @@
+/*
+ * 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.bsAll;
+import static jdk.nashorn.internal.runtime.regexp.joni.BitStatus.bsAt;
+import static jdk.nashorn.internal.runtime.regexp.joni.BitStatus.bsClear;
+import static jdk.nashorn.internal.runtime.regexp.joni.BitStatus.bsOnAt;
+import static jdk.nashorn.internal.runtime.regexp.joni.BitStatus.bsOnAtSimple;
+import static jdk.nashorn.internal.runtime.regexp.joni.Option.isCaptureGroup;
+import static jdk.nashorn.internal.runtime.regexp.joni.Option.isFindCondition;
+import static jdk.nashorn.internal.runtime.regexp.joni.Option.isIgnoreCase;
+import static jdk.nashorn.internal.runtime.regexp.joni.Option.isMultiline;
+import static jdk.nashorn.internal.runtime.regexp.joni.ast.ConsAltNode.newAltNode;
+import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.isRepeatInfinite;
+
+import java.util.HashSet;
+
+import jdk.nashorn.internal.runtime.regexp.joni.ast.AnchorNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.BackRefNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.CTypeNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.CallNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.ConsAltNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.EncloseNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.Node;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.StringNode;
+import jdk.nashorn.internal.runtime.regexp.joni.constants.AnchorType;
+import jdk.nashorn.internal.runtime.regexp.joni.constants.EncloseType;
+import jdk.nashorn.internal.runtime.regexp.joni.constants.NodeType;
+import jdk.nashorn.internal.runtime.regexp.joni.constants.RegexState;
+import jdk.nashorn.internal.runtime.regexp.joni.constants.StackPopLevel;
+import jdk.nashorn.internal.runtime.regexp.joni.constants.TargetInfo;
+import jdk.nashorn.internal.runtime.regexp.joni.encoding.CharacterType;
+import jdk.nashorn.internal.runtime.regexp.joni.encoding.ObjPtr;
+import jdk.nashorn.internal.runtime.regexp.joni.encoding.Ptr;
+
+final class Analyser extends Parser {
+
+    protected Analyser(ScanEnvironment env, char[] chars, int p, int end) {
+        super(env, chars, p, end);
+    }
+
+    protected final void compile() {
+        regex.state = RegexState.COMPILING;
+
+        if (Config.DEBUG) {
+            Config.log.println(new String(chars, getBegin(), getEnd()));
+        }
+
+        reset();
+
+        regex.numMem = 0;
+        regex.numRepeat = 0;
+        regex.numNullCheck = 0;
+        //regex.repeatRangeAlloc = 0;
+        regex.repeatRangeLo = null;
+        regex.repeatRangeHi = null;
+        regex.numCombExpCheck = 0;
+
+        if (Config.USE_COMBINATION_EXPLOSION_CHECK) regex.numCombExpCheck = 0;
+
+        parse();
+
+        if (Config.USE_NAMED_GROUP) {
+            /* mixed use named group and no-named group */
+            if (env.numNamed > 0 && syntax.captureOnlyNamedGroup() && !isCaptureGroup(regex.options)) {
+                if (env.numNamed != env.numMem) {
+                    root = disableNoNameGroupCapture(root);
+                } else {
+                    numberedRefCheck(root);
+                }
+            }
+        } // USE_NAMED_GROUP
+
+        if (Config.USE_NAMED_GROUP) {
+            if (env.numCall > 0) {
+                env.unsetAddrList = new UnsetAddrList(env.numCall);
+                setupSubExpCall(root);
+                // r != 0 ???
+                subexpRecursiveCheckTrav(root);
+                // r < 0 -< err, FOUND_CALLED_NODE = 1
+                subexpInfRecursiveCheckTrav(root);
+                // r != 0  recursion infinite ???
+                regex.numCall = env.numCall;
+            } else {
+                regex.numCall = 0;
+            }
+        } // USE_NAMED_GROUP
+
+        if (Config.DEBUG_PARSE_TREE_RAW && Config.DEBUG_PARSE_TREE) {
+            Config.log.println("<RAW TREE>");
+            Config.log.println(root + "\n");
+        }
+
+        root = setupTree(root, 0);
+        if (Config.DEBUG_PARSE_TREE) {
+            if (Config.DEBUG_PARSE_TREE_RAW) Config.log.println("<TREE>");
+            root.verifyTree(new HashSet<Node>(), env.reg.warnings);
+            Config.log.println(root + "\n");
+        }
+
+        regex.captureHistory = env.captureHistory;
+        regex.btMemStart = env.btMemStart;
+        regex.btMemEnd = env.btMemEnd;
+
+        if (isFindCondition(regex.options)) {
+            regex.btMemEnd = bsAll();
+        } else {
+            regex.btMemEnd = env.btMemEnd;
+            regex.btMemEnd |= regex.captureHistory;
+        }
+
+        if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
+            if (env.backrefedMem == 0 || (Config.USE_SUBEXP_CALL && env.numCall == 0)) {
+                setupCombExpCheck(root, 0);
+
+                if (Config.USE_SUBEXP_CALL && env.hasRecursion) {
+                    env.numCombExpCheck = 0;
+                } else { // USE_SUBEXP_CALL
+                    if (env.combExpMaxRegNum > 0) {
+                        for (int i=1; i<env.combExpMaxRegNum; i++) {
+                            if (bsAt(env.backrefedMem, i)) {
+                                env.numCombExpCheck = 0;
+                                break;
+                            }
+                        }
+                    }
+                }
+
+            } // USE_SUBEXP_CALL
+            regex.numCombExpCheck = env.numCombExpCheck;
+        } // USE_COMBINATION_EXPLOSION_CHECK
+
+        regex.clearOptimizeInfo();
+
+        if (!Config.DONT_OPTIMIZE) setOptimizedInfoFromTree(root);
+
+        env.memNodes = null;
+
+        new ArrayCompiler(this).compile();
+        //new AsmCompiler(this).compile();
+
+        if (regex.numRepeat != 0 || regex.btMemEnd != 0) {
+            regex.stackPopLevel = StackPopLevel.ALL;
+        } else {
+            if (regex.btMemStart != 0) {
+                regex.stackPopLevel = StackPopLevel.MEM_START;
+            } else {
+                regex.stackPopLevel = StackPopLevel.FREE;
+            }
+        }
+
+        if (Config.DEBUG_COMPILE) {
+            if (Config.USE_NAMED_GROUP) Config.log.print(regex.nameTableToString());
+            Config.log.println("stack used: " + regex.stackNeeded);
+            if (Config.USE_STRING_TEMPLATES) Config.log.print("templates: " + regex.templateNum + "\n");
+            Config.log.println(new ByteCodePrinter(regex).byteCodeListToString());
+
+        } // DEBUG_COMPILE
+
+        regex.state = RegexState.NORMAL;
+    }
+
+    private void noNameDisableMapFor_cosAlt(Node node, int[]map, Ptr counter) {
+        ConsAltNode can = (ConsAltNode)node;
+        do {
+            can.setCar(noNameDisableMap(can.car, map, counter));
+        } while ((can = can.cdr) != null);
+    }
+
+    private void noNameDisableMapFor_quantifier(Node node, int[]map, Ptr counter) {
+        QuantifierNode qn = (QuantifierNode)node;
+        Node target = qn.target;
+        Node old = target;
+        target = noNameDisableMap(target, map, counter);
+
+        if (target != old) {
+            qn.setTarget(target);
+            if (target.getType() == NodeType.QTFR) qn.reduceNestedQuantifier((QuantifierNode)target);
+        }
+    }
+
+    private Node noNameDisableMapFor_enclose(Node node, int[]map, Ptr counter) {
+        EncloseNode en = (EncloseNode)node;
+        if (en.type == EncloseType.MEMORY) {
+            if (en.isNamedGroup()) {
+                counter.p++;
+                map[en.regNum] = counter.p;
+                en.regNum = counter.p;
+                //en.target = noNameDisableMap(en.target, map, counter);
+                en.setTarget(noNameDisableMap(en.target, map, counter)); // ???
+            } else {
+                node = en.target;
+                en.target = null; // remove first enclose: /(a)(?<b>c)/
+                node = noNameDisableMap(node, map, counter);
+            }
+        } else {
+            //en.target = noNameDisableMap(en.target, map, counter);
+            en.setTarget(noNameDisableMap(en.target, map, counter)); // ???
+        }
+        return node;
+    }
+
+    private void noNameDisableMapFor_anchor(Node node, int[]map, Ptr counter) {
+        AnchorNode an = (AnchorNode)node;
+        switch (an.type) {
+            case AnchorNode.PREC_READ:
+            case AnchorNode.PREC_READ_NOT:
+            case AnchorNode.LOOK_BEHIND:
+            case AnchorNode.LOOK_BEHIND_NOT:
+                an.setTarget(noNameDisableMap(an.target, map, counter));
+        }
+    }
+
+    private Node noNameDisableMap(Node node, int[]map, Ptr counter) {
+        switch (node.getType()) {
+        case NodeType.LIST:
+        case NodeType.ALT:
+            noNameDisableMapFor_cosAlt(node, map, counter);
+            break;
+        case NodeType.QTFR:
+            noNameDisableMapFor_quantifier(node, map, counter);
+            break;
+        case NodeType.ENCLOSE:
+            node = noNameDisableMapFor_enclose(node, map, counter);
+            break;
+        case NodeType.ANCHOR:
+            noNameDisableMapFor_anchor(node, map, counter);
+            break;
+        } // switch
+        return node;
+    }
+
+    private void renumberByMap(Node node, int[]map) {
+        switch (node.getType()) {
+        case NodeType.LIST:
+        case NodeType.ALT:
+            ConsAltNode can = (ConsAltNode)node;
+            do {
+                renumberByMap(can.car, map);
+            } while ((can = can.cdr) != null);
+            break;
+
+        case NodeType.QTFR:
+            renumberByMap(((QuantifierNode)node).target, map);
+            break;
+
+        case NodeType.ENCLOSE:
+            renumberByMap(((EncloseNode)node).target, map);
+            break;
+
+        case NodeType.BREF:
+            ((BackRefNode)node).renumber(map);
+            break;
+        } // switch
+    }
+
+    protected final void numberedRefCheck(Node node) {
+        switch (node.getType()) {
+        case NodeType.LIST:
+        case NodeType.ALT:
+            ConsAltNode can = (ConsAltNode)node;
+            do {
+                numberedRefCheck(can.car);
+            } while ((can = can.cdr) != null);
+            break;
+
+        case NodeType.QTFR:
+            numberedRefCheck(((QuantifierNode)node).target);
+            break;
+
+        case NodeType.ENCLOSE:
+            numberedRefCheck(((EncloseNode)node).target);
+            break;
+
+        case NodeType.BREF:
+            BackRefNode br = (BackRefNode)node;
+            if (!br.isNameRef()) newValueException(ERR_NUMBERED_BACKREF_OR_CALL_NOT_ALLOWED);
+            break;
+        } // switch
+    }
+
+    protected final Node disableNoNameGroupCapture(Node root) {
+        int[]map = new int[env.numMem + 1];
+
+        for (int i=1; i<=env.numMem; i++) map[i] = 0;
+
+        root = noNameDisableMap(root, map, new Ptr(0));
+        renumberByMap(root, map);
+
+        for (int i=1, pos=1; i<=env.numMem; i++) {
+            if (map[i] > 0) {
+                env.memNodes[pos] = env.memNodes[i];
+                pos++;
+            }
+        }
+
+        int loc = env.captureHistory;
+        env.captureHistory = bsClear();
+
+        for (int i=1; i<=Config.MAX_CAPTURE_HISTORY_GROUP; i++) {
+            if (bsAt(loc, i)) {
+                env.captureHistory = bsOnAtSimple(env.captureHistory, map[i]);
+            }
+        }
+
+        env.numMem = env.numNamed;
+        regex.numMem = env.numNamed;
+
+        regex.renumberNameTable(map);
+
+        return root;
+    }
+
+    private void swap(Node a, Node b) {
+        a.swap(b);
+
+        if (root == b) {
+            root = a;
+        } else if (root == a) {
+            root = b;
+        }
+    }
+
+    // USE_INFINITE_REPEAT_MONOMANIAC_MEM_STATUS_CHECK
+    private int quantifiersMemoryInfo(Node node) {
+        int info = 0;
+
+        switch(node.getType()) {
+        case NodeType.LIST:
+        case NodeType.ALT:
+            ConsAltNode can = (ConsAltNode)node;
+            do {
+                int v = quantifiersMemoryInfo(can.car);
+                if (v > info) info = v;
+            } while ((can = can.cdr) != null);
+            break;
+
+        case NodeType.CALL:
+            if (Config.USE_SUBEXP_CALL) {
+                CallNode cn = (CallNode)node;
+                if (cn.isRecursion()) {
+                    return TargetInfo.IS_EMPTY_REC; /* tiny version */
+                } else {
+                    info = quantifiersMemoryInfo(cn.target);
+                }
+            } // USE_SUBEXP_CALL
+            break;
+
+        case NodeType.QTFR:
+            QuantifierNode qn = (QuantifierNode)node;
+            if (qn.upper != 0) {
+                info = quantifiersMemoryInfo(qn.target);
+            }
+            break;
+
+        case NodeType.ENCLOSE:
+            EncloseNode en = (EncloseNode)node;
+            switch (en.type) {
+            case EncloseType.MEMORY:
+                return TargetInfo.IS_EMPTY_MEM;
+
+            case EncloseType.OPTION:
+            case EncloseNode.STOP_BACKTRACK:
+                info = quantifiersMemoryInfo(en.target);
+                break;
+
+            default:
+                break;
+            } // inner switch
+            break;
+
+        case NodeType.BREF:
+        case NodeType.STR:
+        case NodeType.CTYPE:
+        case NodeType.CCLASS:
+        case NodeType.CANY:
+        case NodeType.ANCHOR:
+        default:
+            break;
+        } // switch
+
+        return info;
+    }
+
+    private int getMinMatchLength(Node node) {
+        int min = 0;
+
+        switch (node.getType()) {
+        case NodeType.BREF:
+            BackRefNode br = (BackRefNode)node;
+            if (br.isRecursion()) break;
+
+            if (br.back[0] > env.numMem) newValueException(ERR_INVALID_BACKREF);
+            min = getMinMatchLength(env.memNodes[br.back[0]]);
+
+            for (int i=1; i<br.backNum; i++) {
+                if (br.back[i] > env.numMem) newValueException(ERR_INVALID_BACKREF);
+                int tmin = getMinMatchLength(env.memNodes[br.back[i]]);
+                if (min > tmin) min = tmin;
+            }
+            break;
+
+        case NodeType.CALL:
+            if (Config.USE_SUBEXP_CALL) {
+                CallNode cn = (CallNode)node;
+                if (cn.isRecursion()) {
+                    EncloseNode en = (EncloseNode)cn.target;
+                    if (en.isMinFixed()) min = en.minLength;
+                } else {
+                    min = getMinMatchLength(cn.target);
+                }
+            } // USE_SUBEXP_CALL
+            break;
+
+        case NodeType.LIST:
+            ConsAltNode can = (ConsAltNode)node;
+            do {
+                min += getMinMatchLength(can.car);
+            } while ((can = can.cdr) != null);
+            break;
+
+        case NodeType.ALT:
+            ConsAltNode y = (ConsAltNode)node;
+            do {
+                Node x = y.car;
+                int tmin = getMinMatchLength(x);
+                if (y == node) {
+                    min = tmin;
+                } else if (min > tmin) {
+                    min = tmin;
+                }
+            } while ((y = y.cdr) != null);
+            break;
+
+        case NodeType.STR:
+            min = ((StringNode)node).length();
+            break;
+
+        case NodeType.CTYPE:
+            min = 1;
+            break;
+
+        case NodeType.CCLASS:
+        case NodeType.CANY:
+            min = 1;
+            break;
+
+        case NodeType.QTFR:
+            QuantifierNode qn = (QuantifierNode)node;
+            if (qn.lower > 0) {
+                min = getMinMatchLength(qn.target);
+                min = MinMaxLen.distanceMultiply(min, qn.lower);
+            }
+            break;
+
+        case NodeType.ENCLOSE:
+            EncloseNode en = (EncloseNode)node;
+            switch (en.type) {
+            case EncloseType.MEMORY:
+                if (Config.USE_SUBEXP_CALL) {
+                    if (en.isMinFixed()) {
+                        min = en.minLength;
+                    } else {
+                        min = getMinMatchLength(en.target);
+                        en.minLength = min;
+                        en.setMinFixed();
+                    }
+                } // USE_SUBEXP_CALL
+                break;
+
+            case EncloseType.OPTION:
+            case EncloseType.STOP_BACKTRACK:
+                min = getMinMatchLength(en.target);
+                break;
+            } // inner switch
+            break;
+
+        case NodeType.ANCHOR:
+        default:
+            break;
+        } // switch
+
+        return min;
+    }
+
+    private int getMaxMatchLength(Node node) {
+        int max = 0;
+
+        switch (node.getType()) {
+        case NodeType.LIST:
+            ConsAltNode ln = (ConsAltNode)node;
+            do {
+                int tmax = getMaxMatchLength(ln.car);
+                max = MinMaxLen.distanceAdd(max, tmax);
+            } while ((ln = ln.cdr) != null);
+            break;
+
+        case NodeType.ALT:
+            ConsAltNode an = (ConsAltNode)node;
+            do {
+                int tmax = getMaxMatchLength(an.car);
+                if (max < tmax) max = tmax;
+            } while ((an = an.cdr) != null);
+            break;
+
+        case NodeType.STR:
+            max = ((StringNode)node).length();
+            break;
+
+        case NodeType.CTYPE:
+            max = 1;
+            break;
+
+        case NodeType.CCLASS:
+        case NodeType.CANY:
+            max = 1;
+            break;
+
+        case NodeType.BREF:
+            BackRefNode br = (BackRefNode)node;
+            if (br.isRecursion()) {
+                max = MinMaxLen.INFINITE_DISTANCE;
+                break;
+            }
+
+            for (int i=0; i<br.backNum; i++) {
+                if (br.back[i] > env.numMem) newValueException(ERR_INVALID_BACKREF);
+                int tmax = getMaxMatchLength(env.memNodes[br.back[i]]);
+                if (max < tmax) max = tmax;
+            }
+            break;
+
+        case NodeType.CALL:
+            if (Config.USE_SUBEXP_CALL) {
+                CallNode cn = (CallNode)node;
+                if (!cn.isRecursion()) {
+                    max = getMaxMatchLength(cn.target);
+                } else {
+                    max = MinMaxLen.INFINITE_DISTANCE;
+                }
+            } // USE_SUBEXP_CALL
+            break;
+
+        case NodeType.QTFR:
+            QuantifierNode qn = (QuantifierNode)node;
+            if (qn.upper != 0) {
+                max = getMaxMatchLength(qn.target);
+                if (max != 0) {
+                    if (!isRepeatInfinite(qn.upper)) {
+                        max = MinMaxLen.distanceMultiply(max, qn.upper);
+                    } else {
+                        max = MinMaxLen.INFINITE_DISTANCE;
+                    }
+                }
+            }
+            break;
+
+        case NodeType.ENCLOSE:
+            EncloseNode en = (EncloseNode)node;
+            switch (en.type) {
+            case EncloseType.MEMORY:
+                if (Config.USE_SUBEXP_CALL) {
+                    if (en.isMaxFixed()) {
+                        max = en.maxLength;
+                    } else {
+                        max = getMaxMatchLength(en.target);
+                        en.maxLength = max;
+                        en.setMaxFixed();
+                    }
+                } // USE_SUBEXP_CALL
+                break;
+
+            case EncloseType.OPTION:
+            case EncloseType.STOP_BACKTRACK:
+                max = getMaxMatchLength(en.target);
+                break;
+            } // inner switch
+            break;
+
+        case NodeType.ANCHOR:
+        default:
+            break;
+        } // switch
+
+        return max;
+    }
+
+    private static final int GET_CHAR_LEN_VARLEN            = -1;
+    private static final int GET_CHAR_LEN_TOP_ALT_VARLEN    = -2;
+    protected final int getCharLengthTree(Node node) {
+        return getCharLengthTree(node, 0);
+    }
+
+    private int getCharLengthTree(Node node, int level) {
+        level++;
+
+        int len = 0;
+        returnCode = 0;
+
+        switch(node.getType()) {
+        case NodeType.LIST:
+            ConsAltNode ln = (ConsAltNode)node;
+            do {
+                int tlen = getCharLengthTree(ln.car, level);
+                if (returnCode == 0) len = MinMaxLen.distanceAdd(len, tlen);
+            } while (returnCode == 0 && (ln = ln.cdr) != null);
+            break;
+
+        case NodeType.ALT:
+            ConsAltNode an = (ConsAltNode)node;
+            boolean varLen = false;
+
+            int tlen = getCharLengthTree(an.car, level);
+            while (returnCode == 0 && (an = an.cdr) != null) {
+                int tlen2 = getCharLengthTree(an.car, level);
+                if (returnCode == 0) {
+                    if (tlen != tlen2) varLen = true;
+                }
+            }
+
+            if (returnCode == 0) {
+                if (varLen) {
+                    if (level == 1) {
+                        returnCode = GET_CHAR_LEN_TOP_ALT_VARLEN;
+                    } else {
+                        returnCode = GET_CHAR_LEN_VARLEN;
+                    }
+                } else {
+                    len = tlen;
+                }
+            }
+            break;
+
+        case NodeType.STR:
+            StringNode sn = (StringNode)node;
+            len = sn.length();
+            break;
+
+        case NodeType.QTFR:
+            QuantifierNode qn = (QuantifierNode)node;
+            if (qn.lower == qn.upper) {
+                tlen = getCharLengthTree(qn.target, level);
+                if (returnCode == 0) len = MinMaxLen.distanceMultiply(tlen, qn.lower);
+            } else {
+                returnCode = GET_CHAR_LEN_VARLEN;
+            }
+            break;
+
+        case NodeType.CALL:
+            if (Config.USE_SUBEXP_CALL) {
+                CallNode cn = (CallNode)node;
+                if (!cn.isRecursion()) {
+                    len = getCharLengthTree(cn.target, level);
+                } else {
+                    returnCode = GET_CHAR_LEN_VARLEN;
+                }
+            } // USE_SUBEXP_CALL
+            break;
+
+        case NodeType.CTYPE:
+            len = 1;
+
+        case NodeType.CCLASS:
+        case NodeType.CANY:
+            len = 1;
+            break;
+
+        case NodeType.ENCLOSE:
+            EncloseNode en = (EncloseNode)node;
+            switch(en.type) {
+            case EncloseType.MEMORY:
+                if (Config.USE_SUBEXP_CALL) {
+                    if (en.isCLenFixed()) {
+                        len = en.charLength;
+                    } else {
+                        len = getCharLengthTree(en.target, level);
+                        if (returnCode == 0) {
+                            en.charLength = len;
+                            en.setCLenFixed();
+                        }
+                    }
+                } // USE_SUBEXP_CALL
+                break;
+
+            case EncloseType.OPTION:
+            case EncloseType.STOP_BACKTRACK:
+                len = getCharLengthTree(en.target, level);
+                break;
+            } // inner switch
+            break;
+
+        case NodeType.ANCHOR:
+            break;
+
+        default:
+            returnCode = GET_CHAR_LEN_VARLEN;
+        } // switch
+        return len;
+    }
+
+    /* x is not included y ==>  1 : 0 */
+    private boolean isNotIncluded(Node x, Node y) {
+        Node tmp;
+
+        // !retry:!
+        retry: while(true) {
+
+        int yType = y.getType();
+
+        switch(x.getType()) {
+        case NodeType.CTYPE:
+            switch(yType) {
+            case NodeType.CTYPE:
+                CTypeNode cny = (CTypeNode)y;
+                CTypeNode cnx = (CTypeNode)x;
+                return cny.ctype == cnx.ctype && cny.not != cnx.not;
+
+            case NodeType.CCLASS:
+                // !swap:!
+                tmp = x;
+                x = y;
+                y = tmp;
+                // !goto retry;!
+                continue retry;
+
+            case NodeType.STR:
+                // !goto swap;!
+                tmp = x;
+                x = y;
+                y = tmp;
+                continue retry;
+
+            default:
+                break;
+            } // inner switch
+            break;
+
+        case NodeType.CCLASS:
+            CClassNode xc = (CClassNode)x;
+
+            switch(yType) {
+            case NodeType.CTYPE:
+                switch(((CTypeNode)y).ctype) {
+                case CharacterType.WORD:
+                    if (!((CTypeNode)y).not) {
+                        if (xc.mbuf == null && !xc.isNot()) {
+                            for (int i=0; i<BitSet.SINGLE_BYTE_SIZE; i++) {
+                                if (xc.bs.at(i)) {
+                                    if (EncodingHelper.isWord(i)) return false;
+                                }
+                            }
+                            return true;
+                        }
+                        return false;
+                    } else {
+                        for (int i=0; i<BitSet.SINGLE_BYTE_SIZE; i++) {
+                            if (!EncodingHelper.isWord(i)) {
+                                if (!xc.isNot()) {
+                                    if (xc.bs.at(i)) return false;
+                                } else {
+                                    if (!xc.bs.at(i)) return false;
+                                }
+                            }
+                        }
+                        return true;
+                    }
+                    // break; not reached
+
+                default:
+                    break;
+                } // inner switch
+                break;
+
+            case NodeType.CCLASS:
+                CClassNode yc = (CClassNode)y;
+
+                for (int i=0; i<BitSet.SINGLE_BYTE_SIZE; i++) {
+                    boolean v = xc.bs.at(i);
+                    if ((v && !xc.isNot()) || (!v && xc.isNot())) {
+                        v = yc.bs.at(i);
+                        if ((v && !yc.isNot()) || (!v && yc.isNot())) return false;
+                    }
+                }
+                if ((xc.mbuf == null && !xc.isNot()) || yc.mbuf == null && !yc.isNot()) return true;
+                return false;
+                // break; not reached
+
+            case NodeType.STR:
+                // !goto swap;!
+                tmp = x;
+                x = y;
+                y = tmp;
+                continue retry;
+
+            default:
+                break;
+
+            } // inner switch
+            break; // case NodeType.CCLASS
+
+        case NodeType.STR:
+            StringNode xs = (StringNode)x;
+            if (xs.length() == 0) break;
+
+            switch (yType) {
+            case NodeType.CTYPE:
+                CTypeNode cy = ((CTypeNode)y);
+                switch (cy.ctype) {
+                case CharacterType.WORD:
+                    return !cy.not;
+
+                default:
+                    break;
+
+                } // inner switch
+                break;
+
+            case NodeType.CCLASS:
+                CClassNode cc = (CClassNode)y;
+                int code = xs.chars[xs.p];
+                return !cc.isCodeInCC(code);
+
+            case NodeType.STR:
+                StringNode ys = (StringNode)y;
+                int len = xs.length();
+                if (len > ys.length()) len = ys.length();
+                if (xs.isAmbig() || ys.isAmbig()) {
+                    /* tiny version */
+                    return false;
+                } else {
+                    for (int i=0, p=ys.p, q=xs.p; i<len; i++, p++, q++) {
+                        if (ys.chars[p] != xs.chars[q]) return true;
+                    }
+                }
+                break;
+
+            default:
+                break;
+            } // inner switch
+
+            break; // case NodeType.STR
+
+        } // switch
+
+        break;
+        } // retry: while
+        return false;
+    }
+
+    private Node getHeadValueNode(Node node, boolean exact) {
+        Node n = null;
+
+        switch(node.getType()) {
+        case NodeType.BREF:
+        case NodeType.ALT:
+        case NodeType.CANY:
+            break;
+
+        case NodeType.CALL:
+            break; // if (Config.USE_SUBEXP_CALL)
+
+        case NodeType.CTYPE:
+        case NodeType.CCLASS:
+            if (!exact) n = node;
+            break;
+
+        case NodeType.LIST:
+            n = getHeadValueNode(((ConsAltNode)node).car, exact);
+            break;
+
+        case NodeType.STR:
+            StringNode sn = (StringNode)node;
+            if (sn.end <= sn.p) break; // ???
+
+            if (exact && !sn.isRaw() && isIgnoreCase(regex.options)){
+                // nothing
+            } else {
+                n = node;
+            }
+            break;
+
+        case NodeType.QTFR:
+            QuantifierNode qn = (QuantifierNode)node;
+            if (qn.lower > 0) {
+                if (qn.headExact != null) {
+                    n = qn.headExact;
+                } else {
+                    n = getHeadValueNode(qn.target, exact);
+                }
+            }
+            break;
+
+        case NodeType.ENCLOSE:
+            EncloseNode en = (EncloseNode)node;
+
+            switch (en.type) {
+            case EncloseType.OPTION:
+                int options = regex.options;
+                regex.options = en.option;
+                n = getHeadValueNode(en.target, exact);
+                regex.options = options;
+                break;
+
+            case EncloseType.MEMORY:
+            case EncloseType.STOP_BACKTRACK:
+                n = getHeadValueNode(en.target, exact);
+                break;
+            } // inner switch
+            break;
+
+        case NodeType.ANCHOR:
+            AnchorNode an = (AnchorNode)node;
+            if (an.type == AnchorType.PREC_READ) n = getHeadValueNode(an.target, exact);
+            break;
+
+        default:
+            break;
+        } // switch
+
+        return n;
+    }
+
+    // true: invalid
+    private boolean checkTypeTree(Node node, int typeMask, int encloseMask, int anchorMask) {
+        if ((node.getType2Bit() & typeMask) == 0) return true;
+
+        boolean invalid = false;
+
+        switch(node.getType()) {
+        case NodeType.LIST:
+        case NodeType.ALT:
+            ConsAltNode can = (ConsAltNode)node;
+            do {
+                invalid = checkTypeTree(can.car, typeMask, encloseMask, anchorMask);
+            } while (!invalid && (can = can.cdr) != null);
+            break;
+
+        case NodeType.QTFR:
+            invalid = checkTypeTree(((QuantifierNode)node).target, typeMask, encloseMask, anchorMask);
+            break;
+
+        case NodeType.ENCLOSE:
+            EncloseNode en = (EncloseNode)node;
+            if ((en.type & encloseMask) == 0) return true;
+            invalid = checkTypeTree(en.target, typeMask, encloseMask, anchorMask);
+            break;
+
+        case NodeType.ANCHOR:
+            AnchorNode an = (AnchorNode)node;
+            if ((an.type & anchorMask) == 0) return true;
+
+            if (an.target != null) invalid = checkTypeTree(an.target, typeMask, encloseMask, anchorMask);
+            break;
+
+        default:
+            break;
+
+        } // switch
+
+        return invalid;
+    }
+
+    private static final int RECURSION_EXIST       = 1;
+    private static final int RECURSION_INFINITE    = 2;
+    private int subexpInfRecursiveCheck(Node node, boolean head) {
+        int r = 0;
+
+        switch (node.getType()) {
+        case NodeType.LIST:
+            int min;
+            ConsAltNode x = (ConsAltNode)node;
+            do {
+                int ret = subexpInfRecursiveCheck(x.car, head);
+                if (ret == RECURSION_INFINITE) return ret;
+                r |= ret;
+                if (head) {
+                    min = getMinMatchLength(x.car);
+                    if (min != 0) head = false;
+                }
+            } while ((x = x.cdr) != null);
+            break;
+
+        case NodeType.ALT:
+            ConsAltNode can = (ConsAltNode)node;
+            r = RECURSION_EXIST;
+            do {
+                int ret = subexpInfRecursiveCheck(can.car, head);
+                if (ret == RECURSION_INFINITE) return ret;
+                r &= ret;
+            } while ((can = can.cdr) != null);
+            break;
+
+        case NodeType.QTFR:
+            QuantifierNode qn = (QuantifierNode)node;
+            r = subexpInfRecursiveCheck(qn.target, head);
+            if (r == RECURSION_EXIST) {
+                if (qn.lower == 0) r = 0;
+            }
+            break;
+
+        case NodeType.ANCHOR:
+            AnchorNode an = (AnchorNode)node;
+            switch (an.type) {
+            case AnchorType.PREC_READ:
+            case AnchorType.PREC_READ_NOT:
+            case AnchorType.LOOK_BEHIND:
+            case AnchorType.LOOK_BEHIND_NOT:
+                r = subexpInfRecursiveCheck(an.target, head);
+                break;
+            } // inner switch
+            break;
+
+        case NodeType.CALL:
+            r = subexpInfRecursiveCheck(((CallNode)node).target, head);
+            break;
+
+        case NodeType.ENCLOSE:
+            EncloseNode en = (EncloseNode)node;
+            if (en.isMark2()) {
+                return 0;
+            } else if (en.isMark1()) {
+                return !head ? RECURSION_EXIST : RECURSION_INFINITE;
+                // throw exception here ???
+            } else {
+                en.setMark2();
+                r = subexpInfRecursiveCheck(en.target, head);
+                en.clearMark2();
+            }
+            break;
+
+        default:
+            break;
+        } // switch
+        return r;
+    }
+
+    protected final int subexpInfRecursiveCheckTrav(Node node) {
+        int r = 0;
+
+        switch (node.getType()) {
+        case NodeType.LIST:
+        case NodeType.ALT:
+            ConsAltNode can = (ConsAltNode)node;
+            do {
+                r = subexpInfRecursiveCheckTrav(can.car);
+            } while (r == 0 && (can = can.cdr) != null);
+            break;
+
+        case NodeType.QTFR:
+            r = subexpInfRecursiveCheckTrav(((QuantifierNode)node).target);
+            break;
+
+        case NodeType.ANCHOR:
+            AnchorNode an = (AnchorNode)node;
+            switch (an.type) {
+            case AnchorType.PREC_READ:
+            case AnchorType.PREC_READ_NOT:
+            case AnchorType.LOOK_BEHIND:
+            case AnchorType.LOOK_BEHIND_NOT:
+                r = subexpInfRecursiveCheckTrav(an.target);
+                break;
+            } // inner switch
+            break;
+
+        case NodeType.ENCLOSE:
+            EncloseNode en = (EncloseNode)node;
+            if (en.isRecursion()) {
+                en.setMark1();
+                r = subexpInfRecursiveCheck(en.target, true);
+                if (r > 0) newValueException(ERR_NEVER_ENDING_RECURSION);
+                en.clearMark1();
+            }
+            r = subexpInfRecursiveCheckTrav(en.target);
+            break;
+
+        default:
+            break;
+        } // switch
+
+        return r;
+    }
+
+    private int subexpRecursiveCheck(Node node) {
+        int r = 0;
+
+        switch (node.getType()) {
+        case NodeType.LIST:
+        case NodeType.ALT:
+            ConsAltNode can = (ConsAltNode)node;
+            do {
+                r |= subexpRecursiveCheck(can.car);
+            } while ((can = can.cdr) != null);
+            break;
+
+        case NodeType.QTFR:
+            r = subexpRecursiveCheck(((QuantifierNode)node).target);
+            break;
+
+        case NodeType.ANCHOR:
+            AnchorNode an = (AnchorNode)node;
+            switch (an.type) {
+            case AnchorType.PREC_READ:
+            case AnchorType.PREC_READ_NOT:
+            case AnchorType.LOOK_BEHIND:
+            case AnchorType.LOOK_BEHIND_NOT:
+                r = subexpRecursiveCheck(an.target);
+                break;
+            } // inner switch
+            break;
+
+        case NodeType.CALL:
+            CallNode cn = (CallNode)node;
+            r = subexpRecursiveCheck(cn.target);
+            if (r != 0) cn.setRecursion();
+            break;
+
+        case NodeType.ENCLOSE:
+            EncloseNode en = (EncloseNode)node;
+            if (en.isMark2()) {
+                return 0;
+            } else if (en.isMark1()) {
+                return 1; /* recursion */
+            } else {
+                en.setMark2();
+                r = subexpRecursiveCheck(en.target);
+                en.clearMark2();
+            }
+            break;
+
+        default:
+            break;
+        } // switch
+
+        return r;
+    }
+
+    private static final int FOUND_CALLED_NODE  = 1;
+    protected final int subexpRecursiveCheckTrav(Node node) {
+        int r = 0;
+
+        switch (node.getType()) {
+        case NodeType.LIST:
+        case NodeType.ALT:
+            ConsAltNode can = (ConsAltNode)node;
+            do {
+                int ret = subexpRecursiveCheckTrav(can.car);
+                if (ret == FOUND_CALLED_NODE) {
+                    r = FOUND_CALLED_NODE;
+                }
+                // else if (ret < 0) return ret; ???
+            } while ((can = can.cdr) != null);
+            break;
+
+        case NodeType.QTFR:
+            QuantifierNode qn = (QuantifierNode)node;
+            r = subexpRecursiveCheckTrav(qn.target);
+            if (qn.upper == 0) {
+                if (r == FOUND_CALLED_NODE) qn.isRefered = true;
+            }
+            break;
+
+        case NodeType.ANCHOR:
+            AnchorNode an = (AnchorNode)node;
+            switch (an.type) {
+            case AnchorType.PREC_READ:
+            case AnchorType.PREC_READ_NOT:
+            case AnchorType.LOOK_BEHIND:
+            case AnchorType.LOOK_BEHIND_NOT:
+                r = subexpRecursiveCheckTrav(an.target);
+                break;
+            } // inner switch
+            break;
+
+        case NodeType.ENCLOSE:
+            EncloseNode en = (EncloseNode)node;
+            if (!en.isRecursion()) {
+                if (en.isCalled()) {
+                    en.setMark1();
+                    r = subexpRecursiveCheck(en.target);
+                    if (r != 0) en.setRecursion();
+                    en.clearMark1();
+                }
+            }
+            r = subexpRecursiveCheckTrav(en.target);
+            if (en.isCalled()) r |= FOUND_CALLED_NODE;
+            break;
+
+        default:
+            break;
+        } // switch
+
+        return r;
+    }
+
+    private void setCallAttr(CallNode cn) {
+        cn.target = env.memNodes[cn.groupNum]; // no setTarget in call nodes!
+        if (cn.target == null) newValueException(ERR_UNDEFINED_NAME_REFERENCE, cn.nameP, cn.nameEnd);
+
+        ((EncloseNode)cn.target).setCalled();
+        env.btMemStart = BitStatus.bsOnAt(env.btMemStart, cn.groupNum);
+        cn.unsetAddrList = env.unsetAddrList;
+    }
+
+    protected final void setupSubExpCall(Node node) {
+
+        switch(node.getType()) {
+        case NodeType.LIST:
+            ConsAltNode ln = (ConsAltNode)node;
+            do {
+                setupSubExpCall(ln.car);
+            } while ((ln = ln.cdr) != null);
+            break;
+
+        case NodeType.ALT:
+            ConsAltNode can = (ConsAltNode)node;
+            do {
+                setupSubExpCall(can.car);
+            } while ((can = can.cdr) != null);
+            break;
+
+        case NodeType.QTFR:
+            setupSubExpCall(((QuantifierNode)node).target);
+            break;
+
+        case NodeType.ENCLOSE:
+            setupSubExpCall(((EncloseNode)node).target);
+            break;
+
+        case NodeType.CALL:
+            CallNode cn = (CallNode)node;
+
+            if (cn.groupNum != 0) {
+                int gNum = cn.groupNum;
+
+                if (Config.USE_NAMED_GROUP) {
+                    if (env.numNamed > 0 && syntax.captureOnlyNamedGroup() && !isCaptureGroup(env.option)) {
+                        newValueException(ERR_NUMBERED_BACKREF_OR_CALL_NOT_ALLOWED);
+                    }
+                } // USE_NAMED_GROUP
+                if (gNum > env.numMem) newValueException(ERR_UNDEFINED_GROUP_REFERENCE, cn.nameP, cn.nameEnd);
+                setCallAttr(cn);
+            } else {
+                if (Config.USE_NAMED_GROUP) {
+                    NameEntry ne = regex.nameToGroupNumbers(cn.name, cn.nameP, cn.nameEnd);
+
+                    if (ne == null) {
+                        newValueException(ERR_UNDEFINED_NAME_REFERENCE, cn.nameP, cn.nameEnd);
+                    } else if (ne.backNum > 1) {
+                        newValueException(ERR_MULTIPLEX_DEFINITION_NAME_CALL, cn.nameP, cn.nameEnd);
+                    } else {
+                        cn.groupNum = ne.backRef1; // ne.backNum == 1 ? ne.backRef1 : ne.backRefs[0]; // ??? need to check ?
+                        setCallAttr(cn);
+                    }
+                }
+            }
+            break;
+
+        case NodeType.ANCHOR:
+            AnchorNode an = (AnchorNode)node;
+            switch (an.type) {
+            case AnchorType.PREC_READ:
+            case AnchorType.PREC_READ_NOT:
+            case AnchorType.LOOK_BEHIND:
+            case AnchorType.LOOK_BEHIND_NOT:
+                setupSubExpCall(an.target);
+                break;
+            }
+            break;
+
+        } // switch
+    }
+
+    /* divide different length alternatives in look-behind.
+    (?<=A|B) ==> (?<=A)|(?<=B)
+    (?<!A|B) ==> (?<!A)(?<!B)
+     */
+    private Node divideLookBehindAlternatives(Node node) {
+        AnchorNode an = (AnchorNode)node;
+        int anchorType = an.type;
+        Node head = an.target;
+        Node np = ((ConsAltNode)head).car;
+
+        swap(node, head);
+
+        Node tmp = node;
+        node = head;
+        head = tmp;
+
+        ((ConsAltNode)node).setCar(head);
+        ((AnchorNode)head).setTarget(np);
+        np = node;
+
+        while ((np = ((ConsAltNode)np).cdr) != null) {
+            AnchorNode insert = new AnchorNode(anchorType);
+            insert.setTarget(((ConsAltNode)np).car);
+            ((ConsAltNode)np).setCar(insert);
+        }
+
+        if (anchorType == AnchorType.LOOK_BEHIND_NOT) {
+            np = node;
+            do {
+                ((ConsAltNode)np).toListNode(); /* alt -> list */
+            } while ((np = ((ConsAltNode)np).cdr) != null);
+        }
+
+        return node;
+    }
+
+    private Node setupLookBehind(Node node) {
+        AnchorNode an = (AnchorNode)node;
+        int len = getCharLengthTree(an.target);
+        switch(returnCode) {
+        case 0:
+            an.charLength = len;
+            break;
+        case GET_CHAR_LEN_VARLEN:
+            newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
+            break;
+        case GET_CHAR_LEN_TOP_ALT_VARLEN:
+            if (syntax.differentLengthAltLookBehind()) {
+                return divideLookBehindAlternatives(node);
+            } else {
+                newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
+            }
+        }
+        return node;
+    }
+
+    private void nextSetup(Node node, Node nextNode) {
+        // retry:
+        retry: while(true) {
+
+        int type = node.getType();
+        if (type == NodeType.QTFR) {
+            QuantifierNode qn = (QuantifierNode)node;
+            if (qn.greedy && isRepeatInfinite(qn.upper)) {
+                if (Config.USE_QTFR_PEEK_NEXT) {
+                    StringNode n = (StringNode)getHeadValueNode(nextNode, true);
+                    /* '\0': for UTF-16BE etc... */
+                    if (n != null && n.chars[n.p] != 0) { // ?????????
+                        qn.nextHeadExact = n;
+                    }
+                } // USE_QTFR_PEEK_NEXT
+                /* automatic posseivation a*b ==> (?>a*)b */
+                if (qn.lower <= 1) {
+                    if (qn.target.isSimple()) {
+                        Node x = getHeadValueNode(qn.target, false);
+                        if (x != null) {
+                            Node y = getHeadValueNode(nextNode, false);
+                            if (y != null && isNotIncluded(x, y)) {
+                                EncloseNode en = new EncloseNode(EncloseType.STOP_BACKTRACK); //onig_node_new_enclose
+                                en.setStopBtSimpleRepeat();
+                                //en.setTarget(qn.target); // optimize it ??
+                                swap(node, en);
+
+                                en.setTarget(node);
+                            }
+                        }
+                    }
+                }
+            }
+        } else if (type == NodeType.ENCLOSE) {
+            EncloseNode en = (EncloseNode)node;
+            if (en.isMemory()) {
+                node = en.target;
+                // !goto retry;!
+                continue retry;
+            }
+        }
+
+        break;
+        } // while
+    }
+
+    private void updateStringNodeCaseFoldMultiByte(StringNode sn) {
+        char[] chars = sn.chars;
+        int end = sn.end;
+        value = sn.p;
+        int sp = 0;
+        char buf;
+
+        while (value < end) {
+            int ovalue = value;
+            buf = Character.toLowerCase(chars[value++]);
+
+            if (chars[ovalue] != buf) {
+
+                char[] sbuf = new char[sn.length() << 1];
+                System.arraycopy(chars, sn.p, sbuf, 0, ovalue - sn.p);
+                value = ovalue;
+                while (value < end) {
+                    buf = Character.toLowerCase(chars[value++]);
+                    if (sp >= sbuf.length) {
+                        char[]tmp = new char[sbuf.length << 1];
+                        System.arraycopy(sbuf, 0, tmp, 0, sbuf.length);
+                        sbuf = tmp;
+                    }
+                    sbuf[sp++] = buf;
+                }
+                sn.set(sbuf, 0, sp);
+                return;
+            }
+            sp++;
+        }
+    }
+
+    private void updateStringNodeCaseFold(Node node) {
+        StringNode sn = (StringNode)node;
+        updateStringNodeCaseFoldMultiByte(sn);
+    }
+
+    private Node expandCaseFoldMakeRemString(char[] chars, int p, int end) {
+        StringNode node = new StringNode(chars, p, end);
+
+        updateStringNodeCaseFold(node);
+        node.setAmbig();
+        node.setDontGetOptInfo();
+        return node;
+    }
+
+    private boolean expandCaseFoldStringAlt(int itemNum, char[] items,
+                                              char[] chars, int p, int slen, int end, ObjPtr<Node> node) {
+
+        ConsAltNode altNode;
+        node.p = altNode = newAltNode(null, null);
+
+        StringNode snode = new StringNode(chars, p, p + slen);
+        altNode.setCar(snode);
+
+        for (int i=0; i<itemNum; i++) {
+            snode = new StringNode();
+
+            snode.catCode(items[i]);
+
+            ConsAltNode an = newAltNode(null, null);
+            an.setCar(snode);
+            altNode.setCdr(an);
+            altNode = an;
+        }
+        return false;
+    }
+
+    private static final int THRESHOLD_CASE_FOLD_ALT_FOR_EXPANSION = 8;
+    private Node expandCaseFoldString(Node node) {
+        StringNode sn = (StringNode)node;
+
+        if (sn.isAmbig() || sn.length() <= 0) return node;
+
+        char[] chars = sn.chars;
+        int p = sn.p;
+        int end = sn.end;
+        int altNum = 1;
+
+        ConsAltNode topRoot = null, root = null;
+        ObjPtr<Node> prevNode = new ObjPtr<Node>();
+        StringNode stringNode = null;
+
+        while (p < end) {
+            char[] items = EncodingHelper.caseFoldCodesByString(regex.caseFoldFlag, chars[p]);
+
+            if (items.length == 0) {
+                if (stringNode == null) {
+                    if (root == null && prevNode.p != null) {
+                        topRoot = root = ConsAltNode.listAdd(null, prevNode.p);
+                    }
+
+                    prevNode.p = stringNode = new StringNode(); // onig_node_new_str(NULL, NULL);
+
+                    if (root != null) ConsAltNode.listAdd(root, stringNode);
+
+                }
+
+                stringNode.cat(chars, p, p + 1);
+            } else {
+                altNum *= (items.length + 1);
+                if (altNum > THRESHOLD_CASE_FOLD_ALT_FOR_EXPANSION) break;
+
+                if (root == null && prevNode.p != null) {
+                    topRoot = root = ConsAltNode.listAdd(null, prevNode.p);
+                }
+
+                expandCaseFoldStringAlt(items.length, items, chars, p, 1, end, prevNode);
+                if (root != null) ConsAltNode.listAdd(root, prevNode.p);
+                stringNode = null;
+            }
+            p++;
+        }
+
+        if (p < end) {
+            Node srem = expandCaseFoldMakeRemString(chars, p, end);
+
+            if (prevNode.p != null && root == null) {
+                topRoot = root = ConsAltNode.listAdd(null, prevNode.p);
+            }
+
+            if (root == null) {
+                prevNode.p = srem;
+            } else {
+                ConsAltNode.listAdd(root, srem);
+            }
+        }
+        /* ending */
+        Node xnode = topRoot != null ? topRoot : prevNode.p;
+
+        swap(node, xnode);
+        return xnode;
+    }
+
+    private static final int CEC_THRES_NUM_BIG_REPEAT       = 512;
+    private static final int CEC_INFINITE_NUM               = 0x7fffffff;
+
+    private static final int CEC_IN_INFINITE_REPEAT         = (1<<0);
+    private static final int CEC_IN_FINITE_REPEAT           = (1<<1);
+    private static final int CEC_CONT_BIG_REPEAT            = (1<<2);
+
+    protected final int setupCombExpCheck(Node node, int state) {
+        int r = state;
+        int ret;
+
+        switch (node.getType()) {
+        case NodeType.LIST:
+            ConsAltNode ln = (ConsAltNode)node;
+
+            do {
+                r = setupCombExpCheck(ln.car, r);
+                //prev = ((ConsAltNode)node).car;
+            } while (r >= 0 && (ln = ln.cdr) != null);
+            break;
+
+        case NodeType.ALT:
+            ConsAltNode an = (ConsAltNode)node;
+            do {
+                ret = setupCombExpCheck(an.car, state);
+                r |= ret;
+            } while (ret >= 0 && (an = an.cdr) != null);
+            break;
+
+        case NodeType.QTFR:
+            QuantifierNode qn = (QuantifierNode)node;
+            int childState = state;
+            int addState = 0;
+            int varNum;
+
+            if (!isRepeatInfinite(qn.upper)) {
+                if (qn.upper > 1) {
+                    /* {0,1}, {1,1} are allowed */
+                    childState |= CEC_IN_FINITE_REPEAT;
+
+                    /* check (a*){n,m}, (a+){n,m} => (a*){n,n}, (a+){n,n} */
+                    if (env.backrefedMem == 0) {
+                        if (qn.target.getType() == NodeType.ENCLOSE) {
+                            EncloseNode en = (EncloseNode)qn.target;
+                            if (en.type == EncloseType.MEMORY) {
+                                if (en.target.getType() == NodeType.QTFR) {
+                                    QuantifierNode q = (QuantifierNode)en.target;
+                                    if (isRepeatInfinite(q.upper) && q.greedy == qn.greedy) {
+                                        qn.upper = qn.lower == 0 ? 1 : qn.lower;
+                                        if (qn.upper == 1) childState = state;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+
+            if ((state & CEC_IN_FINITE_REPEAT) != 0) {
+                qn.combExpCheckNum = -1;
+            } else {
+                if (isRepeatInfinite(qn.upper)) {
+                    varNum = CEC_INFINITE_NUM;
+                    childState |= CEC_IN_INFINITE_REPEAT;
+                } else {
+                    varNum = qn.upper - qn.lower;
+                }
+
+                if (varNum >= CEC_THRES_NUM_BIG_REPEAT) addState |= CEC_CONT_BIG_REPEAT;
+
+                if (((state & CEC_IN_INFINITE_REPEAT) != 0 && varNum != 0) ||
+                   ((state & CEC_CONT_BIG_REPEAT) != 0 && varNum >= CEC_THRES_NUM_BIG_REPEAT)) {
+                    if (qn.combExpCheckNum == 0) {
+                        env.numCombExpCheck++;
+                        qn.combExpCheckNum = env.numCombExpCheck;
+                        if (env.currMaxRegNum > env.combExpMaxRegNum) {
+                            env.combExpMaxRegNum = env.currMaxRegNum;
+                        }
+                    }
+                }
+            }
+            r = setupCombExpCheck(qn.target, childState);
+            r |= addState;
+            break;
+
+        case NodeType.ENCLOSE:
+            EncloseNode en = (EncloseNode)node;
+            switch( en.type) {
+            case EncloseNode.MEMORY:
+                if (env.currMaxRegNum < en.regNum) {
+                    env.currMaxRegNum = en.regNum;
+                }
+                r = setupCombExpCheck(en.target, state);
+                break;
+
+            default:
+                r = setupCombExpCheck(en.target, state);
+            } // inner switch
+            break;
+
+        case NodeType.CALL:
+            if (Config.USE_SUBEXP_CALL) {
+                CallNode cn = (CallNode)node;
+                if (cn.isRecursion()) {
+                    env.hasRecursion = true;
+                } else {
+                    r = setupCombExpCheck(cn.target, state);
+                }
+            } // USE_SUBEXP_CALL
+            break;
+
+        default:
+            break;
+
+        } // switch
+
+        return r;
+    }
+
+    private static final int IN_ALT                     = (1<<0);
+    private static final int IN_NOT                     = (1<<1);
+    private static final int IN_REPEAT                  = (1<<2);
+    private static final int IN_VAR_REPEAT              = (1<<3);
+    private static final int EXPAND_STRING_MAX_LENGTH   = 100;
+
+    /* setup_tree does the following work.
+    1. check empty loop. (set qn->target_empty_info)
+    2. expand ignore-case in char class.
+    3. set memory status bit flags. (reg->mem_stats)
+    4. set qn->head_exact for [push, exact] -> [push_or_jump_exact1, exact].
+    5. find invalid patterns in look-behind.
+    6. expand repeated string.
+    */
+    protected final Node setupTree(Node node, int state) {
+        restart: while (true) {
+        switch (node.getType()) {
+        case NodeType.LIST:
+            ConsAltNode lin = (ConsAltNode)node;
+            Node prev = null;
+            do {
+                setupTree(lin.car, state);
+                if (prev != null) {
+                    nextSetup(prev, lin.car);
+                }
+                prev = lin.car;
+            } while ((lin = lin.cdr) != null);
+            break;
+
+        case NodeType.ALT:
+            ConsAltNode aln = (ConsAltNode)node;
+            do {
+                setupTree(aln.car, (state | IN_ALT));
+            } while ((aln = aln.cdr) != null);
+            break;
+
+        case NodeType.CCLASS:
+            break;
+
+        case NodeType.STR:
+            if (isIgnoreCase(regex.options) && !((StringNode)node).isRaw()) {
+                node = expandCaseFoldString(node);
+            }
+            break;
+
+        case NodeType.CTYPE:
+        case NodeType.CANY:
+            break;
+
+        case NodeType.CALL: // if (Config.USE_SUBEXP_CALL) ?
+            break;
+
+        case NodeType.BREF:
+            BackRefNode br = (BackRefNode)node;
+            for (int i=0; i<br.backNum; i++) {
+                if (br.back[i] > env.numMem) newValueException(ERR_INVALID_BACKREF);
+                env.backrefedMem = bsOnAt(env.backrefedMem, br.back[i]);
+                env.btMemStart = bsOnAt(env.btMemStart, br.back[i]);
+                if (Config.USE_BACKREF_WITH_LEVEL) {
+                    if (br.isNestLevel()) {
+                        env.btMemEnd = bsOnAt(env.btMemEnd, br.back[i]);
+                    }
+                } // USE_BACKREF_AT_LEVEL
+                ((EncloseNode)env.memNodes[br.back[i]]).setMemBackrefed();
+            }
+            break;
+
+        case NodeType.QTFR:
+            QuantifierNode qn = (QuantifierNode)node;
+            Node target = qn.target;
+
+            if ((state & IN_REPEAT) != 0) qn.setInRepeat();
+
+            if (isRepeatInfinite(qn.upper) || qn.lower >= 1) {
+                int d = getMinMatchLength(target);
+                if (d == 0) {
+                    qn.targetEmptyInfo = TargetInfo.IS_EMPTY;
+                    if (Config.USE_MONOMANIAC_CHECK_CAPTURES_IN_ENDLESS_REPEAT) {
+                        int info = quantifiersMemoryInfo(target);
+                        if (info > 0) qn.targetEmptyInfo = info;
+                    } // USE_INFINITE_REPEAT_MONOMANIAC_MEM_STATUS_CHECK
+                    // strange stuff here (turned off)
+                }
+            }
+
+            state |= IN_REPEAT;
+            if (qn.lower != qn.upper) state |= IN_VAR_REPEAT;
+
+            target = setupTree(target, state);
+
+            /* expand string */
+            if (target.getType() == NodeType.STR) {
+                if (!isRepeatInfinite(qn.lower) && qn.lower == qn.upper &&
+                    qn.lower > 1 && qn.lower <= EXPAND_STRING_MAX_LENGTH) {
+                    StringNode sn = (StringNode)target;
+                    int len = sn.length();
+
+                    if (len * qn.lower <= EXPAND_STRING_MAX_LENGTH) {
+                        StringNode str = qn.convertToString(sn.flag);
+                        int n = qn.lower;
+                        for (int i = 0; i < n; i++) {
+                            str.cat(sn.chars, sn.p, sn.end);
+                        }
+                        break; /* break case NT_QTFR: */
+                    }
+
+                }
+            }
+            if (Config.USE_OP_PUSH_OR_JUMP_EXACT) {
+                if (qn.greedy && qn.targetEmptyInfo != 0) {
+                    if (target.getType() == NodeType.QTFR) {
+                        QuantifierNode tqn = (QuantifierNode)target;
+                        if (tqn.headExact != null) {
+                            qn.headExact = tqn.headExact;
+                            tqn.headExact = null;
+                        }
+                    } else {
+                        qn.headExact = getHeadValueNode(qn.target, true);
+                    }
+                }
+            } // USE_OP_PUSH_OR_JUMP_EXACT
+            break;
+
+        case NodeType.ENCLOSE:
+            EncloseNode en = (EncloseNode)node;
+            switch (en.type) {
+            case EncloseType.OPTION:
+                int options = regex.options;
+                regex.options = en.option;
+                setupTree(en.target, state);
+                regex.options = options;
+                break;
+
+            case EncloseType.MEMORY:
+                if ((state & (IN_ALT | IN_NOT | IN_VAR_REPEAT)) != 0) {
+                    env.btMemStart = bsOnAt(env.btMemStart, en.regNum);
+                    /* SET_ENCLOSE_STATUS(node, NST_MEM_IN_ALT_NOT); */
+
+                }
+                setupTree(en.target, state);
+                break;
+
+            case EncloseType.STOP_BACKTRACK:
+                setupTree(en.target, state);
+                if (en.target.getType() == NodeType.QTFR) {
+                    QuantifierNode tqn = (QuantifierNode)en.target;
+                    if (isRepeatInfinite(tqn.upper) && tqn.lower <= 1 && tqn.greedy) {
+                        /* (?>a*), a*+ etc... */
+                        if (tqn.target.isSimple()) en.setStopBtSimpleRepeat();
+                    }
+                }
+                break;
+
+            } // inner switch
+            break;
+
+        case NodeType.ANCHOR:
+            AnchorNode an = (AnchorNode)node;
+            switch (an.type) {
+            case AnchorType.PREC_READ:
+                setupTree(an.target, state);
+                break;
+
+            case AnchorType.PREC_READ_NOT:
+                setupTree(an.target, (state | IN_NOT));
+                break;
+
+            case AnchorType.LOOK_BEHIND:
+                if (checkTypeTree(an.target, NodeType.ALLOWED_IN_LB, EncloseType.ALLOWED_IN_LB, AnchorType.ALLOWED_IN_LB)) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
+                node = setupLookBehind(node);
+                if (node.getType() != NodeType.ANCHOR) continue restart;
+                setupTree(((AnchorNode)node).target, state);
+                break;
+
+            case AnchorType.LOOK_BEHIND_NOT:
+                if (checkTypeTree(an.target, NodeType.ALLOWED_IN_LB, EncloseType.ALLOWED_IN_LB, AnchorType.ALLOWED_IN_LB)) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
+                node = setupLookBehind(node);
+                if (node.getType() != NodeType.ANCHOR) continue restart;
+                setupTree(((AnchorNode)node).target, (state | IN_NOT));
+                break;
+
+            } // inner switch
+            break;
+        } // switch
+        return node;
+        } // restart: while
+    }
+
+    private static final int MAX_NODE_OPT_INFO_REF_COUNT   = 5;
+    private void optimizeNodeLeft(Node node, NodeOptInfo opt, OptEnvironment oenv) { // oenv remove, pass mmd
+        opt.clear();
+        opt.setBoundNode(oenv.mmd);
+
+        switch (node.getType()) {
+        case NodeType.LIST: {
+            OptEnvironment nenv = new OptEnvironment();
+            NodeOptInfo nopt = new NodeOptInfo();
+            nenv.copy(oenv);
+            ConsAltNode lin = (ConsAltNode)node;
+            do {
+                optimizeNodeLeft(lin.car, nopt, nenv);
+                nenv.mmd.add(nopt.length);
+                opt.concatLeftNode(nopt);
+            } while ((lin = lin.cdr) != null);
+            break;
+        }
+
+        case NodeType.ALT: {
+            NodeOptInfo nopt = new NodeOptInfo();
+            ConsAltNode aln = (ConsAltNode)node;
+            do {
+                optimizeNodeLeft(aln.car, nopt, oenv);
+                if (aln == node) {
+                    opt.copy(nopt);
+                } else {
+                    opt.altMerge(nopt, oenv);
+                }
+            } while ((aln = aln.cdr) != null);
+            break;
+        }
+
+        case NodeType.STR: {
+            StringNode sn = (StringNode)node;
+
+            int slen = sn.length();
+
+            if (!sn.isAmbig()) {
+                opt.exb.concatStr(sn.chars, sn.p, sn.end, sn.isRaw());
+
+                if (slen > 0) {
+                    opt.map.addChar(sn.chars[sn.p]);
+                }
+
+                opt.length.set(slen, slen);
+            } else {
+                int max;
+                if (sn.isDontGetOptInfo()) {
+                    max = sn.length();
+                } else {
+                    opt.exb.concatStr(sn.chars, sn.p, sn.end, sn.isRaw());
+                    opt.exb.ignoreCase = true;
+
+                    if (slen > 0) {
+                        opt.map.addCharAmb(sn.chars, sn.p, sn.end, oenv.caseFoldFlag);
+                    }
+
+                    max = slen;
+                }
+                opt.length.set(slen, max);
+            }
+
+            if (opt.exb.length == slen) {
+                opt.exb.reachEnd = true;
+            }
+            break;
+        }
+
+        case NodeType.CCLASS: {
+            CClassNode cc = (CClassNode)node;
+            /* no need to check ignore case. (setted in setup_tree()) */
+            if (cc.mbuf != null || cc.isNot()) {
+                opt.length.set(1, 1);
+            } else {
+                for (int i=0; i<BitSet.SINGLE_BYTE_SIZE; i++) {
+                    boolean z = cc.bs.at(i);
+                    if ((z && !cc.isNot()) || (!z && cc.isNot())) {
+                        opt.map.addChar(i);
+                    }
+                }
+                opt.length.set(1, 1);
+            }
+            break;
+        }
+
+        case NodeType.CTYPE: {
+            int min;
+            int max = 1;
+            if (max == 1) {
+                min = 1;
+                CTypeNode cn = (CTypeNode)node;
+
+                switch (cn.ctype) {
+                case CharacterType.WORD:
+                    if (cn.not) {
+                        for (int i=0; i<BitSet.SINGLE_BYTE_SIZE; i++) {
+                            if (!EncodingHelper.isWord(i)) {
+                                opt.map.addChar(i);
+                            }
+                        }
+                    } else {
+                        for (int i=0; i<BitSet.SINGLE_BYTE_SIZE; i++) {
+                            if (EncodingHelper.isWord(i)) {
+                                opt.map.addChar(i);
+                            }
+                        }
+                    }
+                    break;
+                } // inner switch
+            } else {
+                min = 1;
+            }
+            opt.length.set(min, max);
+            break;
+        }
+
+        case NodeType.CANY: {
+            opt.length.set(1, 1);
+            break;
+        }
+
+        case NodeType.ANCHOR: {
+            AnchorNode an = (AnchorNode)node;
+            switch (an.type) {
+            case AnchorType.BEGIN_BUF:
+            case AnchorType.BEGIN_POSITION:
+            case AnchorType.BEGIN_LINE:
+            case AnchorType.END_BUF:
+            case AnchorType.SEMI_END_BUF:
+            case AnchorType.END_LINE:
+                opt.anchor.add(an.type);
+                break;
+
+            case AnchorType.PREC_READ:
+                NodeOptInfo nopt = new NodeOptInfo();
+                optimizeNodeLeft(an.target, nopt, oenv);
+                if (nopt.exb.length > 0) {
+                    opt.expr.copy(nopt.exb);
+                } else if (nopt.exm.length > 0) {
+                    opt.expr.copy(nopt.exm);
+                }
+                opt.expr.reachEnd = false;
+                if (nopt.map.value > 0) opt.map.copy(nopt.map);
+                break;
+
+            case AnchorType.PREC_READ_NOT:
+            case AnchorType.LOOK_BEHIND:    /* Sorry, I can't make use of it. */
+            case AnchorType.LOOK_BEHIND_NOT:
+                break;
+
+            } // inner switch
+            break;
+        }
+
+        case NodeType.BREF: {
+            BackRefNode br = (BackRefNode)node;
+
+            if (br.isRecursion()) {
+                opt.length.set(0, MinMaxLen.INFINITE_DISTANCE);
+                break;
+            }
+
+            Node[]nodes = oenv.scanEnv.memNodes;
+
+            int min = getMinMatchLength(nodes[br.back[0]]);
+            int max = getMaxMatchLength(nodes[br.back[0]]);
+
+            for (int i=1; i<br.backNum; i++) {
+                int tmin = getMinMatchLength(nodes[br.back[i]]);
+                int tmax = getMaxMatchLength(nodes[br.back[i]]);
+                if (min > tmin) min = tmin;
+                if (max < tmax) max = tmax;
+            }
+            opt.length.set(min, max);
+            break;
+        }
+
+        case NodeType.CALL: {
+            if (Config.USE_SUBEXP_CALL) {
+                CallNode cn = (CallNode)node;
+                if (cn.isRecursion()) {
+                    opt.length.set(0, MinMaxLen.INFINITE_DISTANCE);
+                } else {
+                    int safe = oenv.options;
+                    oenv.options = ((EncloseNode)cn.target).option;
+                    optimizeNodeLeft(cn.target, opt, oenv);
+                    oenv.options = safe;
+                }
+            } // USE_SUBEXP_CALL
+            break;
+        }
+
+        case NodeType.QTFR: {
+            NodeOptInfo nopt = new NodeOptInfo();
+            QuantifierNode qn = (QuantifierNode)node;
+            optimizeNodeLeft(qn.target, nopt, oenv);
+            if (qn.lower == 0 && isRepeatInfinite(qn.upper)) {
+                if (oenv.mmd.max == 0 && qn.target.getType() == NodeType.CANY && qn.greedy) {
+                    if (isMultiline(oenv.options)) {
+                        opt.anchor.add(AnchorType.ANYCHAR_STAR_ML);
+                    } else {
+                        opt.anchor.add(AnchorType.ANYCHAR_STAR);
+                    }
+                }
+            } else {
+                if (qn.lower > 0) {
+                    opt.copy(nopt);
+                    if (nopt.exb.length > 0) {
+                        if (nopt.exb.reachEnd) {
+                            int i;
+                            for (i = 2; i <= qn.lower && !opt.exb.isFull(); i++) {
+                                opt.exb.concat(nopt.exb);
+                            }
+                            if (i < qn.lower) {
+                                opt.exb.reachEnd = false;
+                            }
+                        }
+                    }
+                    if (qn.lower != qn.upper) {
+                        opt.exb.reachEnd = false;
+                        opt.exm.reachEnd = false;
+                    }
+                    if (qn.lower > 1) {
+                        opt.exm.reachEnd = false;
+                    }
+
+                }
+            }
+            int min = MinMaxLen.distanceMultiply(nopt.length.min, qn.lower);
+            int max;
+            if (isRepeatInfinite(qn.upper)) {
+                max = nopt.length.max > 0 ? MinMaxLen.INFINITE_DISTANCE : 0;
+            } else {
+                max = MinMaxLen.distanceMultiply(nopt.length.max, qn.upper);
+            }
+            opt.length.set(min, max);
+            break;
+        }
+
+        case NodeType.ENCLOSE: {
+            EncloseNode en = (EncloseNode)node;
+            switch (en.type) {
+            case EncloseType.OPTION:
+                int save = oenv.options;
+                oenv.options = en.option;
+                optimizeNodeLeft(en.target, opt, oenv);
+                oenv.options = save;
+                break;
+
+            case EncloseType.MEMORY:
+                if (Config.USE_SUBEXP_CALL && ++en.optCount > MAX_NODE_OPT_INFO_REF_COUNT) {
+                    int min = 0;
+                    int max = MinMaxLen.INFINITE_DISTANCE;
+                    if (en.isMinFixed()) min = en.minLength;
+                    if (en.isMaxFixed()) max = en.maxLength;
+                    opt.length.set(min, max);
+                } else { // USE_SUBEXP_CALL
+                    optimizeNodeLeft(en.target, opt, oenv);
+                    if (opt.anchor.isSet(AnchorType.ANYCHAR_STAR_MASK)) {
+                        if (bsAt(oenv.scanEnv.backrefedMem, en.regNum)) {
+                            opt.anchor.remove(AnchorType.ANYCHAR_STAR_MASK);
+                        }
+                    }
+                }
+                break;
+
+            case EncloseType.STOP_BACKTRACK:
+                optimizeNodeLeft(en.target, opt, oenv);
+                break;
+            } // inner switch
+            break;
+        }
+
+        default:
+            newInternalException(ERR_PARSER_BUG);
+        } // switch
+    }
+
+    protected final void setOptimizedInfoFromTree(Node node) {
+        NodeOptInfo opt = new NodeOptInfo();
+        OptEnvironment oenv = new OptEnvironment();
+
+        oenv.options = regex.options;
+        oenv.caseFoldFlag = regex.caseFoldFlag;
+        oenv.scanEnv = env;
+        oenv.mmd.clear(); // ??
+
+        optimizeNodeLeft(node, opt, oenv);
+
+        regex.anchor = opt.anchor.leftAnchor & (AnchorType.BEGIN_BUF |
+                                                AnchorType.BEGIN_POSITION |
+                                                AnchorType.ANYCHAR_STAR |
+                                                AnchorType.ANYCHAR_STAR_ML);
+
+        regex.anchor |= opt.anchor.rightAnchor & (AnchorType.END_BUF |
+                                                  AnchorType.SEMI_END_BUF);
+
+        if ((regex.anchor & (AnchorType.END_BUF | AnchorType.SEMI_END_BUF)) != 0) {
+            regex.anchorDmin = opt.length.min;
+            regex.anchorDmax = opt.length.max;
+        }
+
+        if (opt.exb.length > 0 || opt.exm.length > 0) {
+            opt.exb.select(opt.exm);
+            if (opt.map.value > 0 && opt.exb.compare(opt.map) > 0) {
+                // !goto set_map;!
+                regex.setOptimizeMapInfo(opt.map);
+                regex.setSubAnchor(opt.map.anchor);
+            } else {
+                regex.setExactInfo(opt.exb);
+                regex.setSubAnchor(opt.exb.anchor);
+            }
+        } else if (opt.map.value > 0) {
+            // !set_map:!
+            regex.setOptimizeMapInfo(opt.map);
+            regex.setSubAnchor(opt.map.anchor);
+        } else {
+            regex.subAnchor |= opt.anchor.leftAnchor & AnchorType.BEGIN_LINE;
+            if (opt.length.max == 0) regex.subAnchor |= opt.anchor.rightAnchor & AnchorType.END_LINE;
+        }
+
+        if (Config.DEBUG_COMPILE || Config.DEBUG_MATCH) {
+            Config.log.println(regex.optimizeInfoToString());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/ApplyCaseFold.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,91 @@
+/*
+ * 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.ast.ConsAltNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.StringNode;
+
+final class ApplyCaseFold {
+
+    // i_apply_case_fold
+    public void apply(int from, int[]to, int length, Object o) {
+        ApplyCaseFoldArg arg = (ApplyCaseFoldArg)o;
+
+        ScanEnvironment env = arg.env;
+        CClassNode cc = arg.cc;
+        BitSet bs = cc.bs;
+
+        if (length == 1) {
+            boolean inCC = cc.isCodeInCC(from);
+
+            if (Config.CASE_FOLD_IS_APPLIED_INSIDE_NEGATIVE_CCLASS) {
+                if ((inCC && !cc.isNot()) || (!inCC && cc.isNot())) {
+                    if (to[0] >= BitSet.SINGLE_BYTE_SIZE) {
+                        cc.addCodeRange(env, to[0], to[0]);
+                    } else {
+                        /* /(?i:[^A-C])/.match("a") ==> fail. */
+                        bs.set(to[0]);
+                    }
+                }
+            } else {
+                if (inCC) {
+                    if (to[0] >= BitSet.SINGLE_BYTE_SIZE) {
+                        if (cc.isNot()) cc.clearNotFlag();
+                        cc.addCodeRange(env, to[0], to[0]);
+                    } else {
+                        if (cc.isNot()) {
+                            bs.clear(to[0]);
+                        } else {
+                            bs.set(to[0]);
+                        }
+                    }
+                }
+            } // CASE_FOLD_IS_APPLIED_INSIDE_NEGATIVE_CCLASS
+
+        } else {
+            if (cc.isCodeInCC(from) && (!Config.CASE_FOLD_IS_APPLIED_INSIDE_NEGATIVE_CCLASS || !cc.isNot())) {
+                StringNode node = null;
+                for (int i=0; i<length; i++) {
+                    if (i == 0) {
+                        node = new StringNode();
+                        /* char-class expanded multi-char only
+                        compare with string folded at match time. */
+                        node.setAmbig();
+                    }
+                    node.catCode(to[i]);
+                }
+
+                ConsAltNode alt = ConsAltNode.newAltNode(node, null);
+
+                if (arg.tail == null) {
+                    arg.altRoot = alt;
+                } else {
+                    arg.tail.setCdr(alt);
+                }
+                arg.tail = alt;
+            }
+
+        }
+
+    }
+
+    static final ApplyCaseFold INSTANCE = new ApplyCaseFold();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/ApplyCaseFoldArg.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,35 @@
+/*
+ * 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.ast.ConsAltNode;
+
+public final class ApplyCaseFoldArg {
+    final ScanEnvironment env;
+    final CClassNode cc;
+    ConsAltNode altRoot;
+    ConsAltNode tail;
+
+    public ApplyCaseFoldArg(ScanEnvironment env, CClassNode cc) {
+        this.env = env;
+        this.cc = cc;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/ArrayCompiler.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,1263 @@
+/*
+ * 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.Option.isDynamic;
+import static jdk.nashorn.internal.runtime.regexp.joni.Option.isIgnoreCase;
+import static jdk.nashorn.internal.runtime.regexp.joni.Option.isMultiline;
+import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.isRepeatInfinite;
+
+import jdk.nashorn.internal.runtime.regexp.joni.ast.AnchorNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.BackRefNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.CTypeNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.CallNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.ConsAltNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.EncloseNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.Node;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.StringNode;
+import jdk.nashorn.internal.runtime.regexp.joni.constants.AnchorType;
+import jdk.nashorn.internal.runtime.regexp.joni.constants.EncloseType;
+import jdk.nashorn.internal.runtime.regexp.joni.constants.NodeType;
+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.constants.TargetInfo;
+import jdk.nashorn.internal.runtime.regexp.joni.encoding.CharacterType;
+
+final class ArrayCompiler extends Compiler {
+    private int[] code;
+    private int codeLength;
+
+    private char[][] templates;
+    private int templateNum;
+
+    ArrayCompiler(Analyser analyser) {
+        super(analyser);
+    }
+
+    @Override
+    protected final void prepare() {
+        int codeSize = Config.USE_STRING_TEMPLATES ? 8 : ((analyser.getEnd() - analyser.getBegin()) * 2 + 2);
+        code = new int[codeSize];
+        codeLength = 0;
+    }
+
+    @Override
+    protected final void finish() {
+        addOpcode(OPCode.END);
+        addOpcode(OPCode.FINISH); // for stack bottom
+
+        regex.code = code;
+        regex.codeLength = codeLength;
+        regex.templates = templates;
+        regex.templateNum = templateNum;
+        regex.factory = MatcherFactory.DEFAULT;
+
+        if (Config.USE_SUBEXP_CALL && analyser.env.unsetAddrList != null) {
+            analyser.env.unsetAddrList.fix(regex);
+            analyser.env.unsetAddrList = null;
+        }
+    }
+
+    @Override
+    protected void compileAltNode(ConsAltNode node) {
+        ConsAltNode aln = node;
+        int len = 0;
+
+        do {
+            len += compileLengthTree(aln.car);
+            if (aln.cdr != null) {
+                len += OPSize.PUSH + OPSize.JUMP;
+            }
+        } while ((aln = aln.cdr) != null);
+
+        int pos = codeLength + len;  /* goal position */
+
+        aln = node;
+        do {
+            len = compileLengthTree(aln.car);
+            if (aln.cdr != null) {
+                addOpcodeRelAddr(OPCode.PUSH, len + OPSize.JUMP);
+            }
+            compileTree(aln.car);
+            if (aln.cdr != null) {
+                len = pos - (codeLength + OPSize.JUMP);
+                addOpcodeRelAddr(OPCode.JUMP, len);
+            }
+        } while ((aln = aln.cdr) != null);
+    }
+
+    private boolean isNeedStrLenOpExact(int op) {
+        return  op == OPCode.EXACTN         ||
+                op == OPCode.EXACTMB2N      ||
+                op == OPCode.EXACTMB3N      ||
+                op == OPCode.EXACTMBN       ||
+                op == OPCode.EXACTN_IC      ||
+                op == OPCode.EXACTN_IC_SB;
+    }
+
+    private boolean opTemplated(int op) {
+        return isNeedStrLenOpExact(op);
+    }
+
+    private int selectStrOpcode(int mbLength, int strLength, boolean ignoreCase) {
+        int op;
+
+        if (ignoreCase) {
+            switch(strLength) {
+            case 1: op = OPCode.EXACT1_IC; break;
+            default:op = OPCode.EXACTN_IC; break;
+            } // switch
+        } else {
+            switch (mbLength) {
+            case 1:
+                switch (strLength) {
+                case 1: op = OPCode.EXACT1; break;
+                case 2: op = OPCode.EXACT2; break;
+                case 3: op = OPCode.EXACT3; break;
+                case 4: op = OPCode.EXACT4; break;
+                case 5: op = OPCode.EXACT5; break;
+                default:op = OPCode.EXACTN; break;
+                } // inner switch
+                break;
+            case 2:
+                switch (strLength) {
+                case 1: op = OPCode.EXACTMB2N1; break;
+                case 2: op = OPCode.EXACTMB2N2; break;
+                case 3: op = OPCode.EXACTMB2N3; break;
+                default:op = OPCode.EXACTMB2N;  break;
+                } // inner switch
+                break;
+            case 3:
+                op = OPCode.EXACTMB3N;
+                break;
+            default:
+                op = OPCode.EXACTMBN;
+            } // switch
+        }
+        return op;
+    }
+
+    private void compileTreeEmptyCheck(Node node, int emptyInfo) {
+        int savedNumNullCheck = regex.numNullCheck;
+
+        if (emptyInfo != 0) {
+            addOpcode(OPCode.NULL_CHECK_START);
+            addMemNum(regex.numNullCheck); /* NULL CHECK ID */
+            regex.numNullCheck++;
+        }
+
+        compileTree(node);
+
+        if (emptyInfo != 0) {
+            switch(emptyInfo) {
+            case TargetInfo.IS_EMPTY:
+                addOpcode(OPCode.NULL_CHECK_END);
+                break;
+            case TargetInfo.IS_EMPTY_MEM:
+                addOpcode(OPCode.NULL_CHECK_END_MEMST);
+                break;
+            case TargetInfo.IS_EMPTY_REC:
+                addOpcode(OPCode.NULL_CHECK_END_MEMST_PUSH);
+                break;
+            } // switch
+
+            addMemNum(savedNumNullCheck); /* NULL CHECK ID */
+        }
+    }
+
+    private int addCompileStringlength(char[] chars, int p, int mbLength, int strLength, boolean ignoreCase) {
+        int op = selectStrOpcode(mbLength, strLength, ignoreCase);
+        int len = OPSize.OPCODE;
+
+        if (Config.USE_STRING_TEMPLATES && opTemplated(op)) {
+            // string length, template index, template string pointer
+            len += OPSize.LENGTH + OPSize.INDEX + OPSize.INDEX;
+        } else {
+            if (isNeedStrLenOpExact(op)) len += OPSize.LENGTH;
+            len += mbLength * strLength;
+        }
+        if (op == OPCode.EXACTMBN) len += OPSize.LENGTH;
+        return len;
+    }
+
+    @Override
+    protected final void addCompileString(char[] chars, int p, int mbLength, int strLength, boolean ignoreCase) {
+        int op = selectStrOpcode(mbLength, strLength, ignoreCase);
+        addOpcode(op);
+
+        if (op == OPCode.EXACTMBN) addLength(mbLength);
+
+        if (isNeedStrLenOpExact(op)) {
+            if (op == OPCode.EXACTN_IC || op == OPCode.EXACTN_IC_SB) {
+                addLength(mbLength * strLength);
+            } else {
+                addLength(strLength);
+            }
+        }
+
+        if (Config.USE_STRING_TEMPLATES && opTemplated(op)) {
+            addInt(templateNum);
+            addInt(p);
+            addTemplate(chars);
+        } else {
+            addChars(chars, p, mbLength * strLength);
+        }
+    }
+
+    private int compileLengthStringNode(Node node) {
+        StringNode sn = (StringNode)node;
+        if (sn.length() <= 0) return 0;
+        boolean ambig = sn.isAmbig();
+
+        int p, prev;
+        p = prev = sn.p;
+        int end = sn.end;
+        char[] chars = sn.chars;
+        p++;
+
+        int slen = 1;
+        int rlen = 0;
+
+        while (p < end) {
+            slen++;
+            p++;
+        }
+        int r = addCompileStringlength(chars, prev, 1, slen, ambig);
+        rlen += r;
+        return rlen;
+    }
+
+    private int compileLengthStringRawNode(StringNode sn) {
+        if (sn.length() <= 0) return 0;
+        return addCompileStringlength(sn.chars, sn.p, 1 /*sb*/, sn.length(), false);
+    }
+
+    private void addMultiByteCClass(CodeRangeBuffer mbuf) {
+        addLength(mbuf.used);
+        addInts(mbuf.p, mbuf.used);
+    }
+
+    private int compileLengthCClassNode(CClassNode cc) {
+        if (cc.isShare()) return OPSize.OPCODE + OPSize.POINTER;
+
+        int len;
+        if (cc.mbuf == null) {
+            len = OPSize.OPCODE + BitSet.BITSET_SIZE;
+        } else {
+            if (cc.bs.isEmpty()) {
+                len = OPSize.OPCODE;
+            } else {
+                len = OPSize.OPCODE + BitSet.BITSET_SIZE;
+            }
+
+            len += OPSize.LENGTH + cc.mbuf.used;
+        }
+        return len;
+    }
+
+    @Override
+    protected void compileCClassNode(CClassNode cc) {
+        if (cc.isShare()) { // shared char class
+            addOpcode(OPCode.CCLASS_NODE);
+            addPointer(cc);
+            return;
+        }
+
+        if (cc.mbuf == null) {
+            if (cc.isNot()) {
+                addOpcode(OPCode.CCLASS_NOT);
+            } else {
+                addOpcode(OPCode.CCLASS);
+            }
+            addInts(cc.bs.bits, BitSet.BITSET_SIZE); // add_bitset
+        } else {
+            if (cc.bs.isEmpty()) {
+                if (cc.isNot()) {
+                    addOpcode(OPCode.CCLASS_MB_NOT);
+                } else {
+                    addOpcode(OPCode.CCLASS_MB);
+                }
+                addMultiByteCClass(cc.mbuf);
+            } else {
+                if (cc.isNot()) {
+                    addOpcode(OPCode.CCLASS_MIX_NOT);
+                } else {
+                    addOpcode(OPCode.CCLASS_MIX);
+                }
+                // store the bit set and mbuf themself!
+                addInts(cc.bs.bits, BitSet.BITSET_SIZE); // add_bitset
+                addMultiByteCClass(cc.mbuf);
+            }
+        }
+    }
+
+    @Override
+    protected void compileCTypeNode(CTypeNode node) {
+        CTypeNode cn = node;
+        int op;
+        switch (cn.ctype) {
+        case CharacterType.WORD:
+            if (cn.not) {
+                op = OPCode.NOT_WORD;
+            } else {
+                op = OPCode.WORD;
+            }
+            break;
+
+        default:
+            newInternalException(ERR_PARSER_BUG);
+            return; // not reached
+        } // inner switch
+        addOpcode(op);
+    }
+
+    @Override
+    protected void compileAnyCharNode() {
+        if (isMultiline(regex.options)) {
+            addOpcode(OPCode.ANYCHAR_ML);
+        } else {
+            addOpcode(OPCode.ANYCHAR);
+        }
+    }
+
+    @Override
+    protected void compileCallNode(CallNode node) {
+        addOpcode(OPCode.CALL);
+        node.unsetAddrList.add(codeLength, node.target);
+        addAbsAddr(0); /*dummy addr.*/
+    }
+
+    @Override
+    protected void compileBackrefNode(BackRefNode node) {
+        BackRefNode br = node;
+        if (Config.USE_BACKREF_WITH_LEVEL && br.isNestLevel()) {
+            addOpcode(OPCode.BACKREF_WITH_LEVEL);
+            addOption(regex.options & Option.IGNORECASE);
+            addLength(br.nestLevel);
+            // !goto add_bacref_mems;!
+            addLength(br.backNum);
+            for (int i=br.backNum-1; i>=0; i--) addMemNum(br.back[i]);
+            return;
+        } else { // USE_BACKREF_AT_LEVEL
+            if (br.backNum == 1) {
+                if (isIgnoreCase(regex.options)) {
+                    addOpcode(OPCode.BACKREFN_IC);
+                    addMemNum(br.back[0]);
+                } else {
+                    switch (br.back[0]) {
+                    case 1:
+                        addOpcode(OPCode.BACKREF1);
+                        break;
+                    case 2:
+                        addOpcode(OPCode.BACKREF2);
+                        break;
+                    default:
+                        addOpcode(OPCode.BACKREFN);
+                        addOpcode(br.back[0]);
+                        break;
+                    } // switch
+                }
+            } else {
+                if (isIgnoreCase(regex.options)) {
+                    addOpcode(OPCode.BACKREF_MULTI_IC);
+                } else {
+                    addOpcode(OPCode.BACKREF_MULTI);
+                }
+                // !add_bacref_mems:!
+                addLength(br.backNum);
+                for (int i=br.backNum-1; i>=0; i--) addMemNum(br.back[i]);
+            }
+        }
+    }
+
+    private static final int REPEAT_RANGE_ALLOC = 8;
+    private void entryRepeatRange(int id, int lower, int upper) {
+        if (regex.repeatRangeLo == null) {
+            regex.repeatRangeLo = new int[REPEAT_RANGE_ALLOC];
+            regex.repeatRangeHi = new int[REPEAT_RANGE_ALLOC];
+        } else if (id >= regex.repeatRangeLo.length){
+            int[]tmp = new int[regex.repeatRangeLo.length + REPEAT_RANGE_ALLOC];
+            System.arraycopy(regex.repeatRangeLo, 0, tmp, 0, regex.repeatRangeLo.length);
+            regex.repeatRangeLo = tmp;
+            tmp = new int[regex.repeatRangeHi.length + REPEAT_RANGE_ALLOC];
+            System.arraycopy(regex.repeatRangeHi, 0, tmp, 0, regex.repeatRangeHi.length);
+            regex.repeatRangeHi = tmp;
+        }
+
+        regex.repeatRangeLo[id] = lower;
+        regex.repeatRangeHi[id] = isRepeatInfinite(upper) ? 0x7fffffff : upper;
+    }
+
+    private void compileRangeRepeatNode(QuantifierNode qn, int targetLen, int emptyInfo) {
+        int numRepeat = regex.numRepeat;
+        addOpcode(qn.greedy ? OPCode.REPEAT : OPCode.REPEAT_NG);
+        addMemNum(numRepeat); /* OP_REPEAT ID */
+        regex.numRepeat++;
+        addRelAddr(targetLen + OPSize.REPEAT_INC);
+
+        entryRepeatRange(numRepeat, qn.lower, qn.upper);
+
+        compileTreeEmptyCheck(qn.target, emptyInfo);
+
+        if ((Config.USE_SUBEXP_CALL && regex.numCall > 0) || qn.isInRepeat()) {
+            addOpcode(qn.greedy ? OPCode.REPEAT_INC_SG : OPCode.REPEAT_INC_NG_SG);
+        } else {
+            addOpcode(qn.greedy ? OPCode.REPEAT_INC : OPCode.REPEAT_INC_NG);
+        }
+
+        addMemNum(numRepeat); /* OP_REPEAT ID */
+    }
+
+    private static final int QUANTIFIER_EXPAND_LIMIT_SIZE   = 50; // was 50
+
+    private static boolean cknOn(int ckn) {
+        return ckn > 0;
+    }
+
+    private int compileCECLengthQuantifierNode(QuantifierNode qn) {
+        boolean infinite = isRepeatInfinite(qn.upper);
+        int emptyInfo = qn.targetEmptyInfo;
+
+        int tlen = compileLengthTree(qn.target);
+        int ckn = regex.numCombExpCheck > 0 ? qn.combExpCheckNum : 0;
+        int cklen = cknOn(ckn) ? OPSize.STATE_CHECK_NUM : 0;
+
+        /* anychar repeat */
+        if (qn.target.getType() == NodeType.CANY) {
+            if (qn.greedy && infinite) {
+                if (qn.nextHeadExact != null && !cknOn(ckn)) {
+                    return OPSize.ANYCHAR_STAR_PEEK_NEXT + tlen * qn.lower + cklen;
+                } else {
+                    return OPSize.ANYCHAR_STAR + tlen * qn.lower + cklen;
+                }
+            }
+        }
+
+        int modTLen;
+        if (emptyInfo != 0) {
+            modTLen = tlen + (OPSize.NULL_CHECK_START + OPSize.NULL_CHECK_END);
+        } else {
+            modTLen = tlen;
+        }
+
+        int len;
+        if (infinite && qn.lower <= 1) {
+            if (qn.greedy) {
+                if (qn.lower == 1) {
+                    len = OPSize.JUMP;
+                } else {
+                    len = 0;
+                }
+                len += OPSize.PUSH + cklen + modTLen + OPSize.JUMP;
+            } else {
+                if (qn.lower == 0) {
+                    len = OPSize.JUMP;
+                } else {
+                    len = 0;
+                }
+                len += modTLen + OPSize.PUSH + cklen;
+            }
+        } else if (qn.upper == 0) {
+            if (qn.isRefered) { /* /(?<n>..){0}/ */
+                len = OPSize.JUMP + tlen;
+            } else {
+                len = 0;
+            }
+        } else if (qn.upper == 1 && qn.greedy) {
+            if (qn.lower == 0) {
+                if (cknOn(ckn)) {
+                    len = OPSize.STATE_CHECK_PUSH + tlen;
+                } else {
+                    len = OPSize.PUSH + tlen;
+                }
+            } else {
+                len = tlen;
+            }
+        } else if (!qn.greedy && qn.upper == 1 && qn.lower == 0) { /* '??' */
+            len = OPSize.PUSH + cklen + OPSize.JUMP + tlen;
+        } else {
+            len = OPSize.REPEAT_INC + modTLen + OPSize.OPCODE + OPSize.RELADDR + OPSize.MEMNUM;
+
+            if (cknOn(ckn)) {
+                len += OPSize.STATE_CHECK;
+            }
+        }
+        return len;
+    }
+
+    @Override
+    protected void compileCECQuantifierNode(QuantifierNode qn) {
+        boolean infinite = isRepeatInfinite(qn.upper);
+        int emptyInfo = qn.targetEmptyInfo;
+
+        int tlen = compileLengthTree(qn.target);
+
+        int ckn = regex.numCombExpCheck > 0 ? qn.combExpCheckNum : 0;
+
+        if (qn.isAnyCharStar()) {
+            compileTreeNTimes(qn.target, qn.lower);
+            if (qn.nextHeadExact != null && !cknOn(ckn)) {
+                if (isMultiline(regex.options)) {
+                    addOpcode(OPCode.ANYCHAR_ML_STAR_PEEK_NEXT);
+                } else {
+                    addOpcode(OPCode.ANYCHAR_STAR_PEEK_NEXT);
+                }
+                if (cknOn(ckn)) {
+                    addStateCheckNum(ckn);
+                }
+                StringNode sn = (StringNode)qn.nextHeadExact;
+                addChars(sn.chars, sn.p, 1);
+                return;
+            } else {
+                if (isMultiline(regex.options)) {
+                    if (cknOn(ckn)) {
+                        addOpcode(OPCode.STATE_CHECK_ANYCHAR_ML_STAR);
+                    } else {
+                        addOpcode(OPCode.ANYCHAR_ML_STAR);
+                    }
+                } else {
+                    if (cknOn(ckn)) {
+                        addOpcode(OPCode.STATE_CHECK_ANYCHAR_STAR);
+                    } else {
+                        addOpcode(OPCode.ANYCHAR_STAR);
+                    }
+                }
+                if (cknOn(ckn)) {
+                    addStateCheckNum(ckn);
+                }
+                return;
+            }
+        }
+
+        int modTLen;
+        if (emptyInfo != 0) {
+            modTLen = tlen + (OPSize.NULL_CHECK_START + OPSize.NULL_CHECK_END);
+        } else {
+            modTLen = tlen;
+        }
+        if (infinite && qn.lower <= 1) {
+            if (qn.greedy) {
+                if (qn.lower == 1) {
+                    addOpcodeRelAddr(OPCode.JUMP, cknOn(ckn) ? OPSize.STATE_CHECK_PUSH :
+                                                                     OPSize.PUSH);
+                }
+                if (cknOn(ckn)) {
+                    addOpcode(OPCode.STATE_CHECK_PUSH);
+                    addStateCheckNum(ckn);
+                    addRelAddr(modTLen + OPSize.JUMP);
+                } else {
+                    addOpcodeRelAddr(OPCode.PUSH, modTLen + OPSize.JUMP);
+                }
+                compileTreeEmptyCheck(qn.target, emptyInfo);
+                addOpcodeRelAddr(OPCode.JUMP, -(modTLen + OPSize.JUMP + (cknOn(ckn) ?
+                                                                               OPSize.STATE_CHECK_PUSH :
+                                                                               OPSize.PUSH)));
+            } else {
+                if (qn.lower == 0) {
+                    addOpcodeRelAddr(OPCode.JUMP, modTLen);
+                }
+                compileTreeEmptyCheck(qn.target, emptyInfo);
+                if (cknOn(ckn)) {
+                    addOpcode(OPCode.STATE_CHECK_PUSH_OR_JUMP);
+                    addStateCheckNum(ckn);
+                    addRelAddr(-(modTLen + OPSize.STATE_CHECK_PUSH_OR_JUMP));
+                } else {
+                    addOpcodeRelAddr(OPCode.PUSH, -(modTLen + OPSize.PUSH));
+                }
+            }
+        } else if (qn.upper == 0) {
+            if (qn.isRefered) { /* /(?<n>..){0}/ */
+                addOpcodeRelAddr(OPCode.JUMP, tlen);
+                compileTree(qn.target);
+            } // else r=0 ???
+        } else if (qn.upper == 1 && qn.greedy) {
+            if (qn.lower == 0) {
+                if (cknOn(ckn)) {
+                    addOpcode(OPCode.STATE_CHECK_PUSH);
+                    addStateCheckNum(ckn);
+                    addRelAddr(tlen);
+                } else {
+                    addOpcodeRelAddr(OPCode.PUSH, tlen);
+                }
+            }
+            compileTree(qn.target);
+        } else if (!qn.greedy && qn.upper == 1 && qn.lower == 0){ /* '??' */
+            if (cknOn(ckn)) {
+                addOpcode(OPCode.STATE_CHECK_PUSH);
+                addStateCheckNum(ckn);
+                addRelAddr(OPSize.JUMP);
+            } else {
+                addOpcodeRelAddr(OPCode.PUSH, OPSize.JUMP);
+            }
+
+            addOpcodeRelAddr(OPCode.JUMP, tlen);
+            compileTree(qn.target);
+        } else {
+            compileRangeRepeatNode(qn, modTLen, emptyInfo);
+            if (cknOn(ckn)) {
+                addOpcode(OPCode.STATE_CHECK);
+                addStateCheckNum(ckn);
+            }
+        }
+    }
+
+    private int compileNonCECLengthQuantifierNode(QuantifierNode qn) {
+        boolean infinite = isRepeatInfinite(qn.upper);
+        int emptyInfo = qn.targetEmptyInfo;
+
+        int tlen = compileLengthTree(qn.target);
+
+        /* anychar repeat */
+        if (qn.target.getType() == NodeType.CANY) {
+            if (qn.greedy && infinite) {
+                if (qn.nextHeadExact != null) {
+                    return OPSize.ANYCHAR_STAR_PEEK_NEXT + tlen * qn.lower;
+                } else {
+                    return OPSize.ANYCHAR_STAR + tlen * qn.lower;
+                }
+            }
+        }
+
+        int modTLen = 0;
+        if (emptyInfo != 0) {
+            modTLen = tlen + (OPSize.NULL_CHECK_START + OPSize.NULL_CHECK_END);
+        } else {
+            modTLen = tlen;
+        }
+
+        int len;
+        if (infinite && (qn.lower <= 1 || tlen * qn.lower <= QUANTIFIER_EXPAND_LIMIT_SIZE)) {
+            if (qn.lower == 1 && tlen > QUANTIFIER_EXPAND_LIMIT_SIZE) {
+                len = OPSize.JUMP;
+            } else {
+                len = tlen * qn.lower;
+            }
+
+            if (qn.greedy) {
+                if (qn.headExact != null) {
+                    len += OPSize.PUSH_OR_JUMP_EXACT1 + modTLen + OPSize.JUMP;
+                } else if (qn.nextHeadExact != null) {
+                    len += OPSize.PUSH_IF_PEEK_NEXT + modTLen + OPSize.JUMP;
+                } else {
+                    len += OPSize.PUSH + modTLen + OPSize.JUMP;
+                }
+            } else {
+                len += OPSize.JUMP + modTLen + OPSize.PUSH;
+            }
+
+        } else if (qn.upper == 0 && qn.isRefered) { /* /(?<n>..){0}/ */
+            len = OPSize.JUMP + tlen;
+        } else if (!infinite && qn.greedy &&
+                  (qn.upper == 1 || (tlen + OPSize.PUSH) * qn.upper <= QUANTIFIER_EXPAND_LIMIT_SIZE )) {
+            len = tlen * qn.lower;
+            len += (OPSize.PUSH + tlen) * (qn.upper - qn.lower);
+        } else if (!qn.greedy && qn.upper == 1 && qn.lower == 0) { /* '??' */
+            len = OPSize.PUSH + OPSize.JUMP + tlen;
+        } else {
+            len = OPSize.REPEAT_INC + modTLen + OPSize.OPCODE + OPSize.RELADDR + OPSize.MEMNUM;
+        }
+        return len;
+    }
+
+    @Override
+    protected void compileNonCECQuantifierNode(QuantifierNode qn) {
+        boolean infinite = isRepeatInfinite(qn.upper);
+        int emptyInfo = qn.targetEmptyInfo;
+
+        int tlen = compileLengthTree(qn.target);
+
+        if (qn.isAnyCharStar()) {
+            compileTreeNTimes(qn.target, qn.lower);
+            if (qn.nextHeadExact != null) {
+                if (isMultiline(regex.options)) {
+                    addOpcode(OPCode.ANYCHAR_ML_STAR_PEEK_NEXT);
+                } else {
+                    addOpcode(OPCode.ANYCHAR_STAR_PEEK_NEXT);
+                }
+                StringNode sn = (StringNode)qn.nextHeadExact;
+                addChars(sn.chars, sn.p, 1);
+                return;
+            } else {
+                if (isMultiline(regex.options)) {
+                    addOpcode(OPCode.ANYCHAR_ML_STAR);
+                } else {
+                    addOpcode(OPCode.ANYCHAR_STAR);
+                }
+                return;
+            }
+        }
+
+        int modTLen;
+        if (emptyInfo != 0) {
+            modTLen = tlen + (OPSize.NULL_CHECK_START + OPSize.NULL_CHECK_END);
+        } else {
+            modTLen = tlen;
+        }
+        if (infinite && (qn.lower <= 1 || tlen * qn.lower <= QUANTIFIER_EXPAND_LIMIT_SIZE)) {
+            if (qn.lower == 1 && tlen > QUANTIFIER_EXPAND_LIMIT_SIZE) {
+                if (qn.greedy) {
+                    if (qn.headExact != null) {
+                        addOpcodeRelAddr(OPCode.JUMP, OPSize.PUSH_OR_JUMP_EXACT1);
+                    } else if (qn.nextHeadExact != null) {
+                        addOpcodeRelAddr(OPCode.JUMP, OPSize.PUSH_IF_PEEK_NEXT);
+                    } else {
+                        addOpcodeRelAddr(OPCode.JUMP, OPSize.PUSH);
+                    }
+                } else {
+                    addOpcodeRelAddr(OPCode.JUMP, OPSize.JUMP);
+                }
+            } else {
+                compileTreeNTimes(qn.target, qn.lower);
+            }
+
+            if (qn.greedy) {
+                if (qn.headExact != null) {
+                    addOpcodeRelAddr(OPCode.PUSH_OR_JUMP_EXACT1, modTLen + OPSize.JUMP);
+                    StringNode sn = (StringNode)qn.headExact;
+                    addChars(sn.chars, sn.p, 1);
+                    compileTreeEmptyCheck(qn.target, emptyInfo);
+                    addOpcodeRelAddr(OPCode.JUMP, -(modTLen + OPSize.JUMP + OPSize.PUSH_OR_JUMP_EXACT1));
+                } else if (qn.nextHeadExact != null) {
+                    addOpcodeRelAddr(OPCode.PUSH_IF_PEEK_NEXT, modTLen + OPSize.JUMP);
+                    StringNode sn = (StringNode)qn.nextHeadExact;
+                    addChars(sn.chars, sn.p, 1);
+                    compileTreeEmptyCheck(qn.target, emptyInfo);
+                    addOpcodeRelAddr(OPCode.JUMP, -(modTLen + OPSize.JUMP + OPSize.PUSH_IF_PEEK_NEXT));
+                } else {
+                    addOpcodeRelAddr(OPCode.PUSH, modTLen + OPSize.JUMP);
+                    compileTreeEmptyCheck(qn.target, emptyInfo);
+                    addOpcodeRelAddr(OPCode.JUMP, -(modTLen + OPSize.JUMP + OPSize.PUSH));
+                }
+            } else {
+                addOpcodeRelAddr(OPCode.JUMP, modTLen);
+                compileTreeEmptyCheck(qn.target, emptyInfo);
+                addOpcodeRelAddr(OPCode.PUSH, -(modTLen + OPSize.PUSH));
+            }
+        } else if (qn.upper == 0 && qn.isRefered) { /* /(?<n>..){0}/ */
+            addOpcodeRelAddr(OPCode.JUMP, tlen);
+            compileTree(qn.target);
+        } else if (!infinite && qn.greedy &&
+                  (qn.upper == 1 || (tlen + OPSize.PUSH) * qn.upper <= QUANTIFIER_EXPAND_LIMIT_SIZE)) {
+            int n = qn.upper - qn.lower;
+            compileTreeNTimes(qn.target, qn.lower);
+
+            for (int i=0; i<n; i++) {
+                addOpcodeRelAddr(OPCode.PUSH, (n - i) * tlen + (n - i - 1) * OPSize.PUSH);
+                compileTree(qn.target);
+            }
+        } else if (!qn.greedy && qn.upper == 1 && qn.lower == 0) { /* '??' */
+            addOpcodeRelAddr(OPCode.PUSH, OPSize.JUMP);
+            addOpcodeRelAddr(OPCode.JUMP, tlen);
+            compileTree(qn.target);
+        } else {
+            compileRangeRepeatNode(qn, modTLen, emptyInfo);
+        }
+    }
+
+    private int compileLengthOptionNode(EncloseNode node) {
+        int prev = regex.options;
+        regex.options = node.option;
+        int tlen = compileLengthTree(node.target);
+        regex.options = prev;
+
+        if (isDynamic(prev ^ node.option)) {
+            return OPSize.SET_OPTION_PUSH + OPSize.SET_OPTION + OPSize.FAIL + tlen + OPSize.SET_OPTION;
+        } else {
+            return tlen;
+        }
+    }
+
+    @Override
+    protected void compileOptionNode(EncloseNode node) {
+        int prev = regex.options;
+
+        if (isDynamic(prev ^ node.option)) {
+            addOpcodeOption(OPCode.SET_OPTION_PUSH, node.option);
+            addOpcodeOption(OPCode.SET_OPTION, prev);
+            addOpcode(OPCode.FAIL);
+        }
+
+        regex.options = node.option;
+        compileTree(node.target);
+        regex.options = prev;
+
+        if (isDynamic(prev ^ node.option)) {
+            addOpcodeOption(OPCode.SET_OPTION, prev);
+        }
+    }
+
+    private int compileLengthEncloseNode(EncloseNode node) {
+        if (node.isOption()) {
+            return compileLengthOptionNode(node);
+        }
+
+        int tlen;
+        if (node.target != null) {
+            tlen = compileLengthTree(node.target);
+        } else {
+            tlen = 0;
+        }
+
+        int len;
+        switch (node.type) {
+        case EncloseType.MEMORY:
+            if (Config.USE_SUBEXP_CALL && node.isCalled()) {
+                len = OPSize.MEMORY_START_PUSH + tlen + OPSize.CALL + OPSize.JUMP + OPSize.RETURN;
+                if (bsAt(regex.btMemEnd, node.regNum)) {
+                    len += node.isRecursion() ? OPSize.MEMORY_END_PUSH_REC : OPSize.MEMORY_END_PUSH;
+                } else {
+                    len += node.isRecursion() ? OPSize.MEMORY_END_REC : OPSize.MEMORY_END;
+                }
+            } else { // USE_SUBEXP_CALL
+                if (bsAt(regex.btMemStart, node.regNum)) {
+                    len = OPSize.MEMORY_START_PUSH;
+                } else {
+                    len = OPSize.MEMORY_START;
+                }
+                len += tlen + (bsAt(regex.btMemEnd, node.regNum) ? OPSize.MEMORY_END_PUSH : OPSize.MEMORY_END);
+            }
+            break;
+
+        case EncloseType.STOP_BACKTRACK:
+            if (node.isStopBtSimpleRepeat()) {
+                QuantifierNode qn = (QuantifierNode)node.target;
+                tlen = compileLengthTree(qn.target);
+                len = tlen * qn.lower + OPSize.PUSH + tlen + OPSize.POP + OPSize.JUMP;
+            } else {
+                len = OPSize.PUSH_STOP_BT + tlen + OPSize.POP_STOP_BT;
+            }
+            break;
+
+        default:
+            newInternalException(ERR_PARSER_BUG);
+            return 0; // not reached
+        } // switch
+        return len;
+    }
+
+    @Override
+    protected void compileEncloseNode(EncloseNode node) {
+        int len;
+        switch (node.type) {
+        case EncloseType.MEMORY:
+            if (Config.USE_SUBEXP_CALL) {
+                if (node.isCalled()) {
+                    addOpcode(OPCode.CALL);
+                    node.callAddr = codeLength + OPSize.ABSADDR + OPSize.JUMP;
+                    node.setAddrFixed();
+                    addAbsAddr(node.callAddr);
+                    len = compileLengthTree(node.target);
+                    len += OPSize.MEMORY_START_PUSH + OPSize.RETURN;
+                    if (bsAt(regex.btMemEnd, node.regNum)) {
+                        len += node.isRecursion() ? OPSize.MEMORY_END_PUSH_REC : OPSize.MEMORY_END_PUSH;
+                    } else {
+                        len += node.isRecursion() ? OPSize.MEMORY_END_REC : OPSize.MEMORY_END;
+                    }
+                    addOpcodeRelAddr(OPCode.JUMP, len);
+                }
+            } // USE_SUBEXP_CALL
+
+            if (bsAt(regex.btMemStart, node.regNum)) {
+                addOpcode(OPCode.MEMORY_START_PUSH);
+            } else {
+                addOpcode(OPCode.MEMORY_START);
+            }
+
+            addMemNum(node.regNum);
+            compileTree(node.target);
+
+            if (Config.USE_SUBEXP_CALL && node.isCalled()) {
+                if (bsAt(regex.btMemEnd, node.regNum)) {
+                    addOpcode(node.isRecursion() ? OPCode.MEMORY_END_PUSH_REC : OPCode.MEMORY_END_PUSH);
+                } else {
+                    addOpcode(node.isRecursion() ? OPCode.MEMORY_END_REC : OPCode.MEMORY_END);
+                }
+                addMemNum(node.regNum);
+                addOpcode(OPCode.RETURN);
+            } else { // USE_SUBEXP_CALL
+                if (bsAt(regex.btMemEnd, node.regNum)) {
+                    addOpcode(OPCode.MEMORY_END_PUSH);
+                } else {
+                    addOpcode(OPCode.MEMORY_END);
+                }
+                addMemNum(node.regNum);
+            }
+            break;
+
+        case EncloseType.STOP_BACKTRACK:
+            if (node.isStopBtSimpleRepeat()) {
+                QuantifierNode qn = (QuantifierNode)node.target;
+
+                compileTreeNTimes(qn.target, qn.lower);
+
+                len = compileLengthTree(qn.target);
+                addOpcodeRelAddr(OPCode.PUSH, len + OPSize.POP + OPSize.JUMP);
+                compileTree(qn.target);
+                addOpcode(OPCode.POP);
+                addOpcodeRelAddr(OPCode.JUMP, -(OPSize.PUSH + len + OPSize.POP + OPSize.JUMP));
+            } else {
+                addOpcode(OPCode.PUSH_STOP_BT);
+                compileTree(node.target);
+                addOpcode(OPCode.POP_STOP_BT);
+            }
+            break;
+
+        default:
+            newInternalException(ERR_PARSER_BUG);
+            break;
+        } // switch
+    }
+
+    private int compileLengthAnchorNode(AnchorNode node) {
+        int tlen;
+        if (node.target != null) {
+            tlen = compileLengthTree(node.target);
+        } else {
+            tlen = 0;
+        }
+
+        int len;
+        switch (node.type) {
+        case AnchorType.PREC_READ:
+            len = OPSize.PUSH_POS + tlen + OPSize.POP_POS;
+            break;
+
+        case AnchorType.PREC_READ_NOT:
+            len = OPSize.PUSH_POS_NOT + tlen + OPSize.FAIL_POS;
+            break;
+
+        case AnchorType.LOOK_BEHIND:
+            len = OPSize.LOOK_BEHIND + tlen;
+            break;
+
+        case AnchorType.LOOK_BEHIND_NOT:
+            len = OPSize.PUSH_LOOK_BEHIND_NOT + tlen + OPSize.FAIL_LOOK_BEHIND_NOT;
+            break;
+
+        default:
+            len = OPSize.OPCODE;
+            break;
+        } // switch
+        return len;
+    }
+
+    @Override
+    protected void compileAnchorNode(AnchorNode node) {
+        int len;
+        int n;
+
+        switch (node.type) {
+        case AnchorType.BEGIN_BUF:          addOpcode(OPCode.BEGIN_BUF);            break;
+        case AnchorType.END_BUF:            addOpcode(OPCode.END_BUF);              break;
+        case AnchorType.BEGIN_LINE:         addOpcode(OPCode.BEGIN_LINE);           break;
+        case AnchorType.END_LINE:           addOpcode(OPCode.END_LINE);             break;
+        case AnchorType.SEMI_END_BUF:       addOpcode(OPCode.SEMI_END_BUF);         break;
+        case AnchorType.BEGIN_POSITION:     addOpcode(OPCode.BEGIN_POSITION);       break;
+
+        case AnchorType.WORD_BOUND:
+            addOpcode(OPCode.WORD_BOUND);
+            break;
+
+        case AnchorType.NOT_WORD_BOUND:
+            addOpcode(OPCode.NOT_WORD_BOUND);
+            break;
+
+        case AnchorType.WORD_BEGIN:
+            if (Config.USE_WORD_BEGIN_END)
+                addOpcode(OPCode.WORD_BEGIN);
+            break;
+
+        case AnchorType.WORD_END:
+            if (Config.USE_WORD_BEGIN_END)
+                addOpcode(OPCode.WORD_END);
+            break;
+
+        case AnchorType.PREC_READ:
+            addOpcode(OPCode.PUSH_POS);
+            compileTree(node.target);
+            addOpcode(OPCode.POP_POS);
+            break;
+
+        case AnchorType.PREC_READ_NOT:
+            len = compileLengthTree(node.target);
+            addOpcodeRelAddr(OPCode.PUSH_POS_NOT, len + OPSize.FAIL_POS);
+            compileTree(node.target);
+            addOpcode(OPCode.FAIL_POS);
+            break;
+
+        case AnchorType.LOOK_BEHIND:
+            addOpcode(OPCode.LOOK_BEHIND);
+            if (node.charLength < 0) {
+                n = analyser.getCharLengthTree(node.target);
+                if (analyser.returnCode != 0) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
+            } else {
+                n = node.charLength;
+            }
+            addLength(n);
+            compileTree(node.target);
+            break;
+
+        case AnchorType.LOOK_BEHIND_NOT:
+            len = compileLengthTree(node.target);
+            addOpcodeRelAddr(OPCode.PUSH_LOOK_BEHIND_NOT, len + OPSize.FAIL_LOOK_BEHIND_NOT);
+            if (node.charLength < 0) {
+                n = analyser.getCharLengthTree(node.target);
+                if (analyser.returnCode != 0) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
+            } else {
+                n = node.charLength;
+            }
+            addLength(n);
+            compileTree(node.target);
+            addOpcode(OPCode.FAIL_LOOK_BEHIND_NOT);
+            break;
+
+        default:
+            newInternalException(ERR_PARSER_BUG);
+        } // switch
+    }
+
+    private int compileLengthTree(Node node) {
+        int len = 0;
+
+        switch (node.getType()) {
+        case NodeType.LIST:
+            ConsAltNode lin = (ConsAltNode)node;
+            do {
+                len += compileLengthTree(lin.car);
+            } while ((lin = lin.cdr) != null);
+            break;
+
+        case NodeType.ALT:
+            ConsAltNode aln = (ConsAltNode)node;
+            int n = 0;
+            do {
+                len += compileLengthTree(aln.car);
+                n++;
+            } while ((aln = aln.cdr) != null);
+            len += (OPSize.PUSH + OPSize.JUMP) * (n - 1);
+            break;
+
+        case NodeType.STR:
+            StringNode sn = (StringNode)node;
+            if (sn.isRaw()) {
+                len = compileLengthStringRawNode(sn);
+            } else {
+                len = compileLengthStringNode(sn);
+            }
+            break;
+
+        case NodeType.CCLASS:
+            len = compileLengthCClassNode((CClassNode)node);
+            break;
+
+        case NodeType.CTYPE:
+        case NodeType.CANY:
+            len = OPSize.OPCODE;
+            break;
+
+        case NodeType.BREF:
+            BackRefNode br = (BackRefNode)node;
+
+            if (Config.USE_BACKREF_WITH_LEVEL && br.isNestLevel()) {
+                len = OPSize.OPCODE + OPSize.OPTION + OPSize.LENGTH +
+                      OPSize.LENGTH + (OPSize.MEMNUM * br.backNum);
+            } else { // USE_BACKREF_AT_LEVEL
+                if (br.backNum == 1) {
+                    len = ((!isIgnoreCase(regex.options) && br.back[0] <= 2)
+                            ? OPSize.OPCODE : (OPSize.OPCODE + OPSize.MEMNUM));
+                } else {
+                    len = OPSize.OPCODE + OPSize.LENGTH + (OPSize.MEMNUM * br.backNum);
+                }
+            }
+            break;
+
+        case NodeType.CALL:
+            if (Config.USE_SUBEXP_CALL) {
+                len = OPSize.CALL;
+                break;
+            } // USE_SUBEXP_CALL
+            break;
+
+        case NodeType.QTFR:
+            if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
+                len = compileCECLengthQuantifierNode((QuantifierNode)node);
+            } else {
+                len = compileNonCECLengthQuantifierNode((QuantifierNode)node);
+            }
+            break;
+
+        case NodeType.ENCLOSE:
+            len = compileLengthEncloseNode((EncloseNode)node);
+            break;
+
+        case NodeType.ANCHOR:
+            len = compileLengthAnchorNode((AnchorNode)node);
+            break;
+
+        default:
+            newInternalException(ERR_PARSER_BUG);
+
+        } //switch
+        return len;
+    }
+
+    private void ensure(int size) {
+        if (size >= code.length) {
+            int length = code.length << 1;
+            while (length <= size) length <<= 1;
+            int[]tmp = new int[length];
+            System.arraycopy(code, 0, tmp, 0, code.length);
+            code = tmp;
+        }
+    }
+
+    private void addInt(int i) {
+        if (codeLength >= code.length) {
+            int[]tmp = new int[code.length << 1];
+            System.arraycopy(code, 0, tmp, 0, code.length);
+            code = tmp;
+        }
+        code[codeLength++] = i;
+    }
+
+    void setInt(int i, int offset) {
+        ensure(offset);
+        regex.code[offset] = i;
+    }
+
+    private void addObject(Object o) {
+        if (regex.operands == null) {
+            regex.operands = new Object[4];
+        } else if (regex.operandLength >= regex.operands.length) {
+            Object[]tmp = new Object[regex.operands.length << 1];
+            System.arraycopy(regex.operands, 0, tmp, 0, regex.operands.length);
+            regex.operands = tmp;
+        }
+        addInt(regex.operandLength);
+        regex.operands[regex.operandLength++] = o;
+    }
+
+    private void addChars(char[] chars, int p ,int length) {
+        ensure(codeLength + length);
+        int end = p + length;
+
+        while (p < end) code[codeLength++] = chars[p++];
+    }
+
+    private void addInts(int[]ints, int length) {
+        ensure(codeLength + length);
+        System.arraycopy(ints, 0, code, codeLength, length);
+        codeLength += length;
+    }
+
+    private void addOpcode(int opcode) {
+        addInt(opcode);
+
+        switch(opcode) {
+        case OPCode.ANYCHAR_STAR:
+        case OPCode.ANYCHAR_STAR_SB:
+        case OPCode.ANYCHAR_ML_STAR:
+        case OPCode.ANYCHAR_ML_STAR_SB:
+        case OPCode.ANYCHAR_STAR_PEEK_NEXT:
+        case OPCode.ANYCHAR_STAR_PEEK_NEXT_SB:
+        case OPCode.ANYCHAR_ML_STAR_PEEK_NEXT:
+        case OPCode.ANYCHAR_ML_STAR_PEEK_NEXT_SB:
+        case OPCode.STATE_CHECK_ANYCHAR_STAR:
+        case OPCode.STATE_CHECK_ANYCHAR_STAR_SB:
+        case OPCode.STATE_CHECK_ANYCHAR_ML_STAR:
+        case OPCode.MEMORY_START_PUSH:
+        case OPCode.MEMORY_END_PUSH:
+        case OPCode.MEMORY_END_PUSH_REC:
+        case OPCode.MEMORY_END_REC:
+        case OPCode.NULL_CHECK_START:
+        case OPCode.NULL_CHECK_END_MEMST_PUSH:
+        case OPCode.PUSH:
+        case OPCode.STATE_CHECK_PUSH:
+        case OPCode.STATE_CHECK_PUSH_OR_JUMP:
+        case OPCode.STATE_CHECK:
+        case OPCode.PUSH_OR_JUMP_EXACT1:
+        case OPCode.PUSH_IF_PEEK_NEXT:
+        case OPCode.REPEAT:
+        case OPCode.REPEAT_NG:
+        case OPCode.REPEAT_INC_SG:
+        case OPCode.REPEAT_INC_NG:
+        case OPCode.REPEAT_INC_NG_SG:
+        case OPCode.PUSH_POS:
+        case OPCode.PUSH_POS_NOT:
+        case OPCode.PUSH_STOP_BT:
+        case OPCode.PUSH_LOOK_BEHIND_NOT:
+        case OPCode.CALL:
+        case OPCode.RETURN: // it will appear only with CALL though
+            regex.stackNeeded = true;
+        }
+    }
+
+    private void addStateCheckNum(int num) {
+        addInt(num);
+    }
+
+    private void addRelAddr(int addr) {
+        addInt(addr);
+    }
+
+    private void addAbsAddr(int addr) {
+        addInt(addr);
+    }
+
+    private void addLength(int length) {
+        addInt(length);
+    }
+
+    private void addMemNum(int num) {
+        addInt(num);
+    }
+
+    private void addPointer(Object o) {
+        addObject(o);
+    }
+
+    private void addOption(int option) {
+        addInt(option);
+    }
+
+    private void addOpcodeRelAddr(int opcode, int addr) {
+        addOpcode(opcode);
+        addRelAddr(addr);
+    }
+
+    private void addOpcodeOption(int opcode, int option) {
+        addOpcode(opcode);
+        addOption(option);
+    }
+
+    private void addTemplate(char[] chars) {
+        if (templateNum == 0) {
+            templates = new char[2][];
+        } else if (templateNum == templates.length) {
+            char[][] tmp = new char[templateNum * 2][];
+            System.arraycopy(templates, 0, tmp, 0, templateNum);
+            templates = tmp;
+        }
+        templates[templateNum++] = chars;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/AsmCompiler.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,109 @@
+/*
+ * 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.AnchorNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.BackRefNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.CTypeNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.CallNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.ConsAltNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.EncloseNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode;
+
+final class AsmCompiler extends AsmCompilerSupport {
+
+    public AsmCompiler(Analyser analyser) {
+        super(analyser);
+    }
+
+    @Override
+    protected void prepare() {
+        REG_NUM++;
+        prepareMachine();
+        prepareMachineInit();
+        prepareMachineMatch();
+
+        prepareFactory();
+        prepareFactoryInit();
+    }
+
+    @Override
+    protected void finish() {
+        setupFactoryInit();
+
+        setupMachineInit();
+        setupMachineMatch();
+
+        setupClasses();
+    }
+
+    @Override
+    protected void compileAltNode(ConsAltNode node) {
+    }
+
+    @Override
+    protected void addCompileString(char[] chars, int p, int mbLength, int strLength, boolean ignoreCase) {
+        String template = installTemplate(chars, p, strLength);
+    }
+
+    @Override
+    protected void compileCClassNode(CClassNode node) {
+        if (node.bs != null) {
+            String bitsetName = installBitSet(node.bs.bits);
+        }
+    }
+
+    @Override
+    protected void compileCTypeNode(CTypeNode node) {
+    }
+
+    @Override
+    protected void compileAnyCharNode() {
+    }
+
+    @Override
+    protected void compileBackrefNode(BackRefNode node) {
+    }
+
+    @Override
+    protected void compileCallNode(CallNode node) {
+    }
+
+    @Override
+    protected void compileCECQuantifierNode(QuantifierNode node) {
+    }
+
+    @Override
+    protected void compileNonCECQuantifierNode(QuantifierNode node) {
+    }
+
+    @Override
+    protected void compileOptionNode(EncloseNode node) {
+    }
+
+    @Override
+    protected void compileEncloseNode(EncloseNode node) {
+    }
+
+    @Override
+    protected void compileAnchorNode(AnchorNode node) {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/AsmCompilerSupport.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,267 @@
+/*
+ * 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 java.io.FileOutputStream;
+import java.io.IOException;
+
+import jdk.nashorn.internal.runtime.regexp.joni.constants.AsmConstants;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Opcodes;
+
+abstract class AsmCompilerSupport extends Compiler implements Opcodes, AsmConstants {
+    protected ClassWriter factory;      // matcher allocator, also bit set, code rage and string template container
+    protected MethodVisitor factoryInit;// factory constructor
+    protected String factoryName;
+
+    protected ClassWriter machine;      // matcher
+    protected MethodVisitor machineInit;// matcher constructor
+    protected MethodVisitor match;      // actual matcher implementation (the matchAt method)
+    protected String machineName;
+
+    // we will? try to manage visitMaxs ourselves for efficiency
+    protected int maxStack = 1;
+    protected int maxVars = LAST_INDEX;
+
+    // for field generation
+    protected int bitsets, ranges, templates;
+
+    // simple class name postfix scheme for now
+    static int REG_NUM = 0;
+
+    // dummy class loader for now
+    private static final class DummyClassLoader extends ClassLoader {
+        public Class<?> defineClass(String name, byte[] bytes) {
+            return super.defineClass(name, bytes, 0, bytes.length);
+        }
+    };
+
+    private static final DummyClassLoader loader = new DummyClassLoader();
+
+    AsmCompilerSupport(Analyser analyser) {
+        super(analyser);
+    }
+
+    protected final void prepareFactory() {
+        factory = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+        factoryName = "jdk/nashorn/internal/runtime/regexp/joni/MatcherFactory" + REG_NUM;
+
+        factory.visit(V1_4, ACC_PUBLIC + ACC_FINAL, factoryName, null, "jdk/nashorn/internal/runtime/regexp/joni/MatcherFactory", null);
+
+        MethodVisitor create = factory.visitMethod(ACC_SYNTHETIC, "create", "(Lorg/joni/Regex;[BII)Lorg/joni/Matcher;", null, null);
+        create.visitTypeInsn(NEW, machineName);
+        create.visitInsn(DUP);          // instance
+        create.visitVarInsn(ALOAD, 1);  // Regex
+        create.visitVarInsn(ALOAD, 2);  // bytes[]
+        create.visitVarInsn(ILOAD, 3);  // p
+        create.visitVarInsn(ILOAD, 4);  // end
+        create.visitMethodInsn(INVOKESPECIAL, machineName, "<init>", "(Lorg/joni/Regex;[BII)V");
+        create.visitInsn(ARETURN);
+        create.visitMaxs(0, 0);
+        //create.visitMaxs(6, 5);
+        create.visitEnd();
+    }
+
+    protected final void prepareFactoryInit() {
+        factoryInit = factory.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+        factoryInit.visitVarInsn(ALOAD, 0);
+        factoryInit.visitMethodInsn(INVOKESPECIAL, "jdk/nashorn/internal/runtime/regexp/joni/MatcherFactory", "<init>", "()V");
+    }
+
+    protected final void setupFactoryInit() {
+        factoryInit.visitInsn(RETURN);
+        factoryInit.visitMaxs(0, 0);
+        //init.visitMaxs(1, 1);
+        factoryInit.visitEnd();
+    }
+
+    protected final void prepareMachine() {
+        machine = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+        machineName = "jdk/nashorn/internal/runtime/regexp/joni/NativeMachine" + REG_NUM;
+    }
+
+    protected final void prepareMachineInit() {
+        machine.visit(V1_4, ACC_PUBLIC + ACC_FINAL, machineName, null, "jdk/nashorn/internal/runtime/regexp/joni/NativeMachine", null);
+        machineInit = machine.visitMethod(ACC_PROTECTED, "<init>", "(Lorg/joni/Regex;[BII)V", null, null);
+        machineInit.visitVarInsn(ALOAD, THIS);  // this
+        machineInit.visitVarInsn(ALOAD, 1);     // Regex
+        machineInit.visitVarInsn(ALOAD, 2);     // bytes[]
+        machineInit.visitVarInsn(ILOAD, 3);     // p
+        machineInit.visitVarInsn(ILOAD, 4);     // end
+        machineInit.visitMethodInsn(INVOKESPECIAL, "jdk/nashorn/internal/runtime/regexp/joni/NativeMachine", "<init>", "(Lorg/joni/Regex;[BII)V");
+    }
+
+    protected final void setupMachineInit() {
+        if (bitsets + ranges + templates > 0) { // ok, some of these are in use, we'd like to cache the factory
+            machine.visitField(ACC_PRIVATE + ACC_FINAL, "factory", "L" + factoryName + ";", null, null);
+            machineInit.visitVarInsn(ALOAD, THIS);  // this
+            machineInit.visitVarInsn(ALOAD, 1);     // this, Regex
+            machineInit.visitFieldInsn(GETFIELD, "jdk/nashorn/internal/runtime/regexp/joni/Regex", "factory", "Lorg/joni/MatcherFactory;"); // this, factory
+            machineInit.visitTypeInsn(CHECKCAST, factoryName);
+            machineInit.visitFieldInsn(PUTFIELD, machineName, "factory", "L" + factoryName + ";"); // []
+        }
+
+        machineInit.visitInsn(RETURN);
+        machineInit.visitMaxs(0, 0);
+        //init.visitMaxs(5, 5);
+        machineInit.visitEnd();
+    }
+
+    protected final void prepareMachineMatch() {
+        match = machine.visitMethod(ACC_SYNTHETIC, "matchAt", "(III)I", null, null);
+        move(S, SSTART);        // s = sstart
+        load("bytes", "[B");    //
+        astore(BYTES);          // byte[]bytes = this.bytes
+    }
+
+    protected final void setupMachineMatch() {
+        match.visitInsn(ICONST_M1);
+        match.visitInsn(IRETURN);
+
+        match.visitMaxs(maxStack, maxVars);
+        match.visitEnd();
+    }
+
+    protected final void setupClasses() {
+        byte[]factoryCode = factory.toByteArray();
+        byte[]machineCode = machine.toByteArray();
+
+        if (Config.DEBUG_ASM) {
+            try {
+                FileOutputStream fos;
+                fos = new FileOutputStream(factoryName.substring(factoryName.lastIndexOf('/') + 1) + ".class");
+                fos.write(factoryCode);
+                fos.close();
+                fos = new FileOutputStream(machineName.substring(machineName.lastIndexOf('/') + 1) + ".class");
+                fos.write(machineCode);
+                fos.close();
+            } catch (IOException ioe) {
+                ioe.printStackTrace(Config.err);
+            }
+        }
+
+        loader.defineClass(machineName.replace('/', '.'), machineCode);
+        Class<?> cls = loader.defineClass(factoryName.replace('/', '.'), factoryCode);
+        try {
+            regex.factory = (MatcherFactory)cls.newInstance();
+        } catch(Exception e) {
+            e.printStackTrace(Config.err);
+        }
+    }
+
+    protected final void aload(int var) {
+        match.visitVarInsn(ALOAD, var);
+    }
+
+    protected final void astore(int var) {
+        match.visitVarInsn(ASTORE, var);
+    }
+
+    protected final void loadThis() {
+        match.visitVarInsn(ALOAD, THIS);
+    }
+
+    protected final void load(int var) {
+        match.visitVarInsn(ILOAD, var);
+    }
+
+    protected final void store(int var) {
+        match.visitVarInsn(ISTORE, var);
+    }
+
+    protected final void move(int to, int from) {
+        load(from);
+        store(to);
+    }
+
+    protected final void load(String field, String singature) {
+        loadThis();
+        match.visitFieldInsn(GETFIELD, machineName, field, singature);
+    }
+
+    protected final void load(String field) {
+        load(field, "I");
+    }
+
+    protected final void store(String field, String singature) {
+        loadThis();
+        match.visitFieldInsn(PUTFIELD, machineName, field, singature);
+    }
+
+    protected final void store(String field) {
+        store(field, "I");
+    }
+
+    protected final String installTemplate(char[] arr, int p, int length) {
+        String templateName = TEMPLATE + ++templates;
+        installArray(templateName, arr, p, length);
+        return templateName;
+    }
+
+    protected final String installCodeRange(int[]arr) {
+        String coreRangeName = CODERANGE + ++ranges;
+        installArray(coreRangeName, arr);
+        return coreRangeName;
+    }
+
+    protected final String installBitSet(int[]arr) {
+        String bitsetName = BITSET + ++bitsets;
+        installArray(bitsetName, arr);
+        return bitsetName;
+    }
+
+    private void installArray(String name, int[]arr) {
+        factory.visitField(ACC_PRIVATE + ACC_FINAL, name, "[I", null, null);
+        factoryInit.visitVarInsn(ALOAD, THIS);          // this;
+        loadInt(factoryInit, arr.length);               // this, length
+        factoryInit.visitIntInsn(NEWARRAY, T_INT);      // this, arr
+        for (int i=0;i < arr.length; i++) buildArray(i, arr[i], IASTORE);
+        factoryInit.visitFieldInsn(PUTFIELD, factoryName, name, "[I");
+    }
+
+    private void installArray(String name, char[]arr, int p, int length) {
+        factory.visitField(ACC_PRIVATE + ACC_FINAL, name, "[B", null, null);
+        factoryInit.visitVarInsn(ALOAD, THIS);          // this;
+        loadInt(factoryInit, arr.length);               // this, length
+        factoryInit.visitIntInsn(NEWARRAY, T_BYTE);     // this, arr
+        for (int i=p, j=0; i < p + length; i++, j++) buildArray(j, arr[i] & 0xff, BASTORE);
+        factoryInit.visitFieldInsn(PUTFIELD, factoryName, name, "[B");
+    }
+
+    private void buildArray(int index, int value, int type) {
+        factoryInit.visitInsn(DUP);     // ... arr, arr
+        loadInt(factoryInit, index);    // ... arr, arr, index
+        loadInt(factoryInit, value);    // ... arr, arr, index, value
+        factoryInit.visitInsn(type);    // ... arr
+    }
+
+    private void loadInt(MethodVisitor mv, int value) {
+        if (value >= -1 && value <= 5) {
+            mv.visitInsn(value + ICONST_0); // ICONST_0 == 3
+        } else if (value >= 6 && value <= 127 || value >= -128 && value <= -2) {
+            mv.visitIntInsn(BIPUSH, value);
+        } else if (value >= 128 && value <= 32767 || value >= -32768 && value <= -129) {
+            mv.visitIntInsn(SIPUSH, value);
+        } else {
+            mv.visitLdcInsn(new Integer(value));
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/BitSet.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,115 @@
+/*
+ * 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;
+
+public final class BitSet {
+    static final int BITS_PER_BYTE = 8;
+    public static final int SINGLE_BYTE_SIZE = (1 << BITS_PER_BYTE);
+    private static final int BITS_IN_ROOM = 4 * BITS_PER_BYTE;
+    static final int BITSET_SIZE = (SINGLE_BYTE_SIZE / BITS_IN_ROOM);
+    static final int ROOM_SHIFT = log2(BITS_IN_ROOM);
+
+    final int[] bits = new int[BITSET_SIZE];
+
+    private static final int BITS_TO_STRING_WRAP = 4;
+    public String toString() {
+        StringBuilder buffer = new StringBuilder();
+        buffer.append("BitSet");
+        for (int i=0; i<SINGLE_BYTE_SIZE; i++) {
+            if ((i % (SINGLE_BYTE_SIZE / BITS_TO_STRING_WRAP)) == 0) buffer.append("\n  ");
+            buffer.append(at(i) ? "1" : "0");
+        }
+        return buffer.toString();
+    }
+
+    public boolean at(int pos) {
+        return (bits[pos >>> ROOM_SHIFT] & bit(pos)) != 0;
+    }
+
+    public void set(int pos) {
+        bits[pos >>> ROOM_SHIFT] |= bit(pos);
+    }
+
+    public void clear(int pos) {
+        bits[pos >>> ROOM_SHIFT] &= ~bit(pos);
+    }
+
+    public void invert(int pos) {
+        bits[pos >>> ROOM_SHIFT] ^= bit(pos);
+    }
+
+    public void clear() {
+        for (int i=0; i<BITSET_SIZE; i++) bits[i]=0;
+    }
+
+    public boolean isEmpty() {
+        for (int i=0; i<BITSET_SIZE; i++) {
+            if (bits[i] != 0) return false;
+        }
+        return true;
+    }
+
+    public void setRange(int from, int to) {
+        for (int i=from; i<=to && i < SINGLE_BYTE_SIZE; i++) set(i);
+    }
+
+    public void setAll() {
+        for (int i=0; i<BITSET_SIZE; i++) bits[i] = ~0;
+    }
+
+    public void invert() {
+        for (int i=0; i<BITSET_SIZE; i++) bits[i] = ~bits[i];
+    }
+
+    public void invertTo(BitSet to) {
+        for (int i=0; i<BITSET_SIZE; i++) to.bits[i] = ~bits[i];
+    }
+
+    public void and(BitSet other) {
+        for (int i=0; i<BITSET_SIZE; i++) bits[i] &= other.bits[i];
+    }
+
+    public void or(BitSet other) {
+        for (int i=0; i<BITSET_SIZE; i++) bits[i] |= other.bits[i];
+    }
+
+    public void copy(BitSet other) {
+        for (int i=0; i<BITSET_SIZE; i++) bits[i] = other.bits[i];
+    }
+
+    public int numOn() {
+        int num = 0;
+        for (int i=0; i<SINGLE_BYTE_SIZE; i++) {
+            if (at(i)) num++;
+        }
+        return num;
+    }
+
+    static int bit(int pos){
+        return 1 << (pos % SINGLE_BYTE_SIZE);
+    }
+
+    private static int log2(int n){
+        int log = 0;
+        while ((n >>>= 1) != 0) log++;
+        return log;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/BitStatus.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+final class BitStatus {
+    public static final int BIT_STATUS_BITS_NUM = 4 * 8;
+
+    public static int bsClear() {
+        return 0;
+    }
+    public static int bsAll() {
+        return -1;
+    }
+    public static boolean bsAt(int stats, int n) {
+        return (n < BIT_STATUS_BITS_NUM ? stats & (1 << n) : (stats & 1)) != 0;
+    }
+    public static int bsOnAt(int stats, int n) {
+        if (n < BIT_STATUS_BITS_NUM) {
+            stats |= (1 << n);
+        } else {
+            stats |= 1;
+        }
+        return stats;
+    }
+    public static int bsOnAtSimple(int stats, int n) {
+        if (n < BIT_STATUS_BITS_NUM) stats |= (1 << n);
+        return stats;
+    }
+
+    public static int bsOnOff(int v, int f, boolean negative) {
+        if (negative) {
+            v &= ~f;
+        } else {
+            v |= f;
+        }
+        return v;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/ByteCodeMachine.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,1462 @@
+/*
+ * 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.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 static jdk.nashorn.internal.runtime.regexp.joni.EncodingHelper.isCrnl;
+import static jdk.nashorn.internal.runtime.regexp.joni.EncodingHelper.isNewLine;
+
+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.constants.OPSize;
+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(Regex regex, char[] chars, int p, int end) {
+        super(regex, chars, p, end);
+        this.code = regex.code;
+    }
+
+    protected int stkp; // a temporary
+    private boolean makeCaptureHistoryTree(CaptureTreeNode node) {
+        //CaptureTreeNode child;
+        int k = stkp;
+        //int k = kp;
+
+        while (k < stk) {
+            StackEntry e = stack[k];
+            if (e.type == MEM_START) {
+                int n = e.getMemNum();
+                if (n <= Config.MAX_CAPTURE_HISTORY_GROUP && bsAt(regex.captureHistory, n)) {
+                    CaptureTreeNode child = new CaptureTreeNode();
+                    child.group = n;
+                    child.beg = e.getMemPStr() - str;
+                    node.addChild(child);
+                    stkp = k + 1;
+                    if (makeCaptureHistoryTree(child)) return true;
+
+                    k = stkp;
+                    child.end = e.getMemPStr() - str;
+                }
+            } else if (e.type == MEM_END) {
+                if (e.getMemNum() == node.group) {
+                    node.end = e.getMemPStr() - str;
+                    stkp = k;
+                    return false;
+                }
+            }
+        }
+        return true; /* 1: root node ending. */
+    }
+
+    private void checkCaptureHistory(Region region) {
+        CaptureTreeNode node;
+        if (region.historyRoot == null) {
+            node = region.historyRoot = new CaptureTreeNode();
+        } else {
+            node = region.historyRoot;
+            node.clear();
+        }
+
+        // was clear ???
+        node.group = 0;
+        node.beg = sstart - str;
+        node.end = s      - str;
+
+        stkp = 0;
+        makeCaptureHistoryTree(region.historyRoot);
+    }
+
+    private boolean stringCmpIC(int caseFlodFlag, int s1, IntHolder ps2, int mbLen, int textEnd) {
+
+        int s2 = ps2.value;
+        int end1 = s1 + mbLen;
+
+        while (s1 < end1) {
+            char c1 = Character.toLowerCase(chars[s1++]);
+            char c2 = Character.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++]}));
+            }
+            String str = q < end ? "...\"" : "\"";
+            q += str.length();
+            Config.log.print(str);
+            for (i=0; i<20-(q-s);i++) Config.log.print(" ");
+            StringBuilder sb = new StringBuilder();
+            new ByteCodePrinter(regex).compiledByteCodeToString(sb, ip);
+            Config.log.println(sb.toString());
+        }
+    }
+
+    protected final int matchAt(int range, int sstart, int sprev) {
+        this.range = range;
+        this.sstart = sstart;
+        this.sprev = sprev;
+
+        stk = 0;
+        ip = 0;
+
+        if (Config.DEBUG_MATCH) debugMatchBegin();
+
+        init();
+
+        bestLen = -1;
+        s = sstart;
+
+        final int[]code = this.code;
+        while (true) {
+            if (Config.DEBUG_MATCH) debugMatchLoop();
+
+            sbegin = s;
+            switch (code[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.EXACTMB2N1:                 opExactMB2N1();            break;
+                case OPCode.EXACTMB2N2:                 opExactMB2N2();            continue;
+                case OPCode.EXACTMB2N3:                 opExactMB2N3();            continue;
+                case OPCode.EXACTMB2N:                  opExactMB2N();             continue;
+                case OPCode.EXACTMB3N:                  opExactMB3N();             continue;
+                case OPCode.EXACTMBN:                   opExactMBN();              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.STATE_CHECK_ANYCHAR_STAR:   opStateCheckAnyCharStar(); break;
+                case OPCode.STATE_CHECK_ANYCHAR_ML_STAR:opStateCheckAnyCharMLStar();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.NULL_CHECK_END_MEMST_PUSH:  opNullCheckEndMemSTPush(); continue;
+
+                case OPCode.JUMP:                       opJump();                  continue;
+                case OPCode.PUSH:                       opPush();                  continue;
+
+                // CEC
+                case OPCode.STATE_CHECK_PUSH:           opStateCheckPush();        continue;
+                case OPCode.STATE_CHECK_PUSH_OR_JUMP:   opStateCheckPushOrJump();  continue;
+                case OPCode.STATE_CHECK:                opStateCheck();            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;
+
+                // USE_SUBEXP_CALL
+                case OPCode.CALL:                       opCall();                  continue;
+                case OPCode.RETURN:                     opReturn();                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() {
+        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;
+                    }
+
+                }
+
+                if (Config.USE_CAPTURE_HISTORY) {
+                    if (regex.captureHistory != 0) checkCaptureHistory(region);
+                }
+            } else {
+                msaBegin = sstart - str;
+                msaEnd   = s      - str;
+            }
+        } else {
+            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) {
+            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 opExactMB2N1() {
+        if (s + 2 > range) {opFail(); return;}
+        if (code[ip] != chars[s]) {opFail(); return;}
+        ip++; s++;
+        if (code[ip] != chars[s]) {opFail(); return;}
+        ip++; s++;
+        sprev = sbegin; // break;
+    }
+
+    private void opExactMB2N2() {
+        if (s + 4 > range) {opFail(); return;}
+        if (code[ip] != chars[s]) {opFail(); return;}
+        ip++; s++;
+        if (code[ip] != chars[s]) {opFail(); return;}
+        ip++; s++;
+        sprev = s;
+        if (code[ip] != chars[s]) {opFail(); return;}
+        ip++; s++;
+        if (code[ip] != chars[s]) {opFail(); return;}
+        ip++; s++;
+   }
+
+    private void opExactMB2N3() {
+        if (s + 6 > 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++;
+        sprev = s;
+        if (code[ip] != chars[s]) {opFail(); return;}
+        ip++; s++;
+        if (code[ip] != chars[s]) {opFail(); return;}
+        ip++; s++;
+    }
+
+    private void opExactMB2N() {
+        int tlen = code[ip++];
+        if (s + tlen * 2 > range) {opFail(); return;}
+
+        if (Config.USE_STRING_TEMPLATES) {
+            char[] bs = regex.templates[code[ip++]];
+            int ps = code[ip++];
+
+            while(tlen-- > 0) {
+                if (bs[ps] != chars[s]) {opFail(); return;}
+                ps++; s++;
+                if (bs[ps] != chars[s]) {opFail(); return;}
+                ps++; s++;
+            }
+        } else {
+            while(tlen-- > 0) {
+                if (code[ip] != chars[s]) {opFail(); return;}
+                ip++; s++;
+                if (code[ip] != chars[s]) {opFail(); return;}
+                ip++; s++;
+            }
+        }
+        sprev = s - 2;
+    }
+
+    private void opExactMB3N() {
+        int tlen = code[ip++];
+        if (s + tlen * 3 > range) {opFail(); return;}
+
+        if (Config.USE_STRING_TEMPLATES) {
+            char[] bs = regex.templates[code[ip++]];
+            int ps = code[ip++];
+
+            while (tlen-- > 0) {
+                if (bs[ps] != chars[s]) {opFail(); return;}
+                ps++; s++;
+                if (bs[ps] != chars[s]) {opFail(); return;}
+                ps++; s++;
+                if (bs[ps] != chars[s]) {opFail(); return;}
+                ps++; s++;
+            }
+        } else {
+            while (tlen-- > 0) {
+                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++;
+            }
+        }
+
+        sprev = s - 3;
+    }
+
+    private void opExactMBN() {
+        int tlen = code[ip++];   /* mb-len */
+        int tlen2= code[ip++];   /* string len */
+
+        tlen2 *= tlen;
+        if (s + tlen2 > range) {opFail(); return;}
+
+        if (Config.USE_STRING_TEMPLATES) {
+            char[] bs = regex.templates[code[ip++]];
+            int ps = code[ip++];
+
+            while (tlen2-- > 0) {
+                if (bs[ps] != chars[s]) {opFail(); return;}
+                ps++; s++;
+            }
+        } else {
+            while (tlen2-- > 0) {
+                if (code[ip] != chars[s]) {opFail(); return;}
+                ip++; s++;
+            }
+        }
+
+        sprev = s - tlen;
+    }
+
+    private void opExact1IC() {
+        if (s >= range || code[ip] != Character.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) {
+            char[] bs = regex.templates[code[ip++]];
+            int ps = code[ip++];
+
+            while (tlen-- > 0) if (bs[ps++] != Character.toLowerCase(chars[s++])) {opFail(); return;}
+        } else {
+
+            while (tlen-- > 0) if (code[ip++] != Character.toLowerCase(chars[s++])) {opFail(); return;}
+        }
+        sprev = s - 1;
+    }
+
+    private boolean isInBitSet() {
+        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() {
+        int tlen = code[ip++];
+        if (s >= range) return false;
+        int ss = s;
+        s++;
+        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;
+            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() {
+        int tlen = code[ip++];
+
+        if (!(s + 1 <= range)) {
+            if (s >= range) return false;
+            s = end;
+            ip += tlen;
+            return true;
+        }
+
+        int ss = s;
+        s++;
+        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++;
+            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;
+            int tlen = code[ip++];
+            ip += tlen;
+            s++;
+        }
+        sprev = sbegin; // break;
+    }
+
+    private void opCClassNode() {
+        if (s >= range) {opFail(); return;}
+        CClassNode cc = (CClassNode)regex.operands[code[ip++]];
+        int ss = s;
+        s++;
+        int c = chars[ss];
+        if (!cc.isCodeInCCLength(c)) {opFail(); return;}
+        sprev = sbegin; // break;
+    }
+
+    private void opAnyChar() {
+        if (s >= range) {opFail(); return;}
+        if (chars[s] == EncodingHelper.NEW_LINE) {opFail(); return;}
+        s++;
+        sprev = sbegin; // break;
+    }
+
+    private void opAnyCharML() {
+        if (s >= range) {opFail(); return;}
+        s++;
+        sprev = sbegin; // break;
+    }
+
+    private void opAnyCharStar() {
+        final char[] chars = this.chars;
+        while (s < range) {
+            pushAlt(ip, s, sprev);
+            if (isNewLine(chars, 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[] chars = this.chars;
+
+        while (s < range) {
+            char b = chars[s];
+            if (c == b) pushAlt(ip + 1, s, sprev);
+            if (b == EncodingHelper.NEW_LINE) {opFail(); return;}
+            sprev = s;
+            s++;
+        }
+        ip++;
+        sprev = sbegin; // break;
+    }
+
+    private void opAnyCharMLStarPeekNext() {
+        final char c = (char)code[ip];
+        final char[] chars = this.chars;
+
+        while (s < range) {
+            if (c == chars[s]) pushAlt(ip + 1, s, sprev);
+            sprev = s;
+            s++;
+        }
+        ip++;
+        sprev = sbegin; // break;
+    }
+
+    // CEC
+    private void opStateCheckAnyCharStar() {
+        int mem = code[ip++];
+        final char[] chars = this.chars;
+
+        while (s < range) {
+            if (stateCheckVal(s, mem)) {opFail(); return;}
+            pushAltWithStateCheck(ip, s, sprev, mem);
+            if (chars[s] == EncodingHelper.NEW_LINE) {opFail(); return;}
+            sprev = s;
+            s++;
+        }
+        sprev = sbegin; // break;
+    }
+
+    // CEC
+    private void opStateCheckAnyCharMLStar() {
+        int mem = code[ip++];
+
+        while (s < range) {
+            if (stateCheckVal(s, mem)) {opFail(); return;}
+            pushAltWithStateCheck(ip, s, sprev, mem);
+            sprev = s;
+            s++;
+        }
+        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 (EncodingHelper.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 || !EncodingHelper.isNewLine(chars, sprev, end)) {
+                    if (isNotEol(msaOptions)) opFail();
+                }
+                return;
+            } else {
+                if (isNotEol(msaOptions)) opFail();
+                return;
+            }
+        } else if (isNewLine(chars, s, end) || (Config.USE_CRNL_AS_LINE_TERMINATOR && isCrnl(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;
+            } else {
+                if (isNotEol(msaOptions)) opFail();
+                return;
+            }
+        } else if (isNewLine(chars, s, end) && s + 1 == end) {
+            return;
+        } else if (Config.USE_CRNL_AS_LINE_TERMINATOR && isCrnl(chars, s, end)) {
+            int ss = s + 2;
+            if (ss == end) return;
+        }
+        opFail();
+    }
+
+    private void opBeginPosition() {
+        if (s != msaStart) opFail();
+    }
+
+    private void opMemoryStartPush() {
+        int mem = code[ip++];
+        pushMemStart(mem, s);
+    }
+
+    private void opMemoryStart() {
+        int mem = code[ip++];
+        repeatStk[memStartStk + mem] = s;
+    }
+
+    private void opMemoryEndPush() {
+        int mem = code[ip++];
+        pushMemEnd(mem, s);
+    }
+
+    private void opMemoryEnd() {
+        int mem = code[ip++];
+        repeatStk[memEndStk + mem] = s;
+    }
+
+    private void opMemoryEndPushRec() {
+        int mem = code[ip++];
+        int stkp = getMemStart(mem); /* should be before push mem-end. */
+        pushMemEnd(mem, s);
+        repeatStk[memStartStk + mem] = stkp;
+    }
+
+    private void opMemoryEndRec() {
+        int mem = code[ip++];
+        repeatStk[memEndStk + mem] = s;
+        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(int mem) {
+        return repeatStk[memEndStk + mem] == INVALID_INDEX || repeatStk[memStartStk + mem] == INVALID_INDEX;
+    }
+
+    private int backrefStart(int mem) {
+        return bsAt(regex.btMemStart, mem) ? stack[repeatStk[memStartStk + mem]].getMemPStr() : repeatStk[memStartStk + mem];
+    }
+
+    private int backrefEnd(int mem) {
+        return bsAt(regex.btMemEnd, mem) ? stack[repeatStk[memEndStk + mem]].getMemPStr() : repeatStk[memEndStk + mem];
+    }
+
+    private void backref(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);
+        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;}
+
+        int len;
+
+        // 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() {
+        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;}
+
+        int pstart = backrefStart(mem);
+        int pend = backrefEnd(mem);
+
+        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;
+
+        int len;
+        // if (sprev < chars.length)
+        while (sprev + 1 < s) sprev++;
+    }
+
+    private void opBackRefMulti() {
+        int tlen = code[ip++];
+
+        int i;
+        loop:for (i=0; i<tlen; i++) {
+            int mem = code[ip++];
+            if (backrefInvalid(mem)) continue;
+
+            int pstart = backrefStart(mem);
+            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;
+
+            int len;
+
+            // 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() {
+        int tlen = code[ip++];
+
+        int i;
+        loop:for (i=0; i<tlen; i++) {
+            int mem = code[ip++];
+            if (backrefInvalid(mem)) continue;
+
+            int pstart = backrefStart(mem);
+            int pend = backrefEnd(mem);
+
+            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;
+
+            int len;
+            // 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(int mem, int num, int memp) {
+        for (int i=0; i<num; i++) {
+            int m = code[memp++];
+            if (mem == m) return true;
+        }
+        return false;
+    }
+
+    // USE_BACKREF_AT_LEVEL // (s) and (end) implicit
+    private boolean backrefMatchAtNestedLevel(boolean ignoreCase, int caseFoldFlag,
+                                              int nest, int memNum, int memp) {
+        int pend = -1;
+        int level = 0;
+        int k = stk - 1;
+
+        while (k >= 0) {
+            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)) {
+                        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() {
+        int ic      = code[ip++];
+        int level   = code[ip++];
+        int tlen    = code[ip++];
+
+        sprev = s;
+        if (backrefMatchAtNestedLevel(ic != 0, regex.caseFoldFlag, level, tlen, ip)) { // (s) and (end) implicit
+            int len;
+            while (sprev + 1 < s) sprev++;
+            ip += tlen; // * SIZE_MEMNUM
+        } else {
+            {opFail(); return;}
+        }
+    }
+
+    /* no need: IS_DYNAMIC_OPTION() == 0 */
+    private void opSetOptionPush() {
+        // option = code[ip++]; // final for now
+        pushAlt(ip, s, sprev);
+        ip += OPSize.SET_OPTION + OPSize.FAIL;
+    }
+
+    private void opSetOption() {
+        // option = code[ip++]; // final for now
+    }
+
+    private void opNullCheckStart() {
+        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() {
+        int mem = code[ip++];
+        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() {
+        int mem = code[ip++];   /* mem: null check id */
+        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();
+        }
+    }
+
+    // USE_SUBEXP_CALL
+    private void opNullCheckEndMemSTPush() {
+        int mem = code[ip++];   /* mem: null check id */
+
+        int isNull;
+        if (Config.USE_MONOMANIAC_CHECK_CAPTURES_IN_ENDLESS_REPEAT) {
+            isNull = nullCheckMemStRec(mem, s);
+        } else {
+            isNull = nullCheckRec(mem, s);
+        }
+
+        if (isNull != 0) {
+            if (Config.DEBUG_MATCH) {
+                Config.log.println("NULL_CHECK_END_MEMST_PUSH: skip  id:" + mem + ", s:" + s);
+            }
+
+            if (isNull == -1) {opFail(); return;}
+            nullCheckFound();
+        } else {
+            pushNullCheckEnd(mem);
+        }
+    }
+
+    private void opJump() {
+        ip += code[ip] + 1;
+    }
+
+    private void opPush() {
+        int addr = code[ip++];
+        pushAlt(ip + addr, s, sprev);
+    }
+
+    // CEC
+    private void opStateCheckPush() {
+        int mem = code[ip++];
+        if (stateCheckVal(s, mem)) {opFail(); return;}
+        int addr = code[ip++];
+        pushAltWithStateCheck(ip + addr, s, sprev, mem);
+    }
+
+    // CEC
+    private void opStateCheckPushOrJump() {
+        int mem = code[ip++];
+        int addr= code[ip++];
+
+        if (stateCheckVal(s, mem)) {
+            ip += addr;
+        } else {
+            pushAltWithStateCheck(ip + addr, s, sprev, mem);
+        }
+    }
+
+    // CEC
+    private void opStateCheck() {
+        int mem = code[ip++];
+        if (stateCheckVal(s, mem)) {opFail(); return;}
+        pushStateCheck(s, mem);
+    }
+
+    private void opPop() {
+        popOne();
+    }
+
+    private void opPushOrJumpExact1() {
+        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() {
+        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() {
+        int mem = code[ip++];   /* mem: OP_REPEAT ID */
+        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() {
+        int mem = code[ip++];   /* mem: OP_REPEAT ID */
+        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(int mem, int si) {
+        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() {
+        int mem = code[ip++];   /* mem: OP_REPEAT ID */
+        int si = repeatStk[mem];
+        repeatInc(mem, si);
+    }
+
+    private void opRepeatIncSG() {
+        int mem = code[ip++];   /* mem: OP_REPEAT ID */
+        int si = getRepeat(mem);
+        repeatInc(mem, si);
+    }
+
+    private void repeatIncNG(int mem, int si) {
+        StackEntry e = stack[si];
+
+        e.increaseRepeatCount();
+
+        if (e.getRepeatCount() < regex.repeatRangeHi[mem]) {
+            if (e.getRepeatCount() >= regex.repeatRangeLo[mem]) {
+                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() {
+        int mem = code[ip++];
+        int si = repeatStk[mem];
+        repeatIncNG(mem, si);
+    }
+
+    private void opRepeatIncNGSG() {
+        int mem = code[ip++];
+        int si = getRepeat(mem);
+        repeatIncNG(mem, si);
+    }
+
+    private void opPushPos() {
+        pushPos(s, sprev);
+    }
+
+    private void opPopPos() {
+        StackEntry e = stack[posEnd()];
+        s    = e.getStatePStr();
+        sprev= e.getStatePStrPrev();
+    }
+
+    private void opPushPosNot() {
+        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() {
+        int tlen = code[ip++];
+        s = EncodingHelper.stepBack(str, s, tlen);
+        if (s == -1) {opFail(); return;}
+        sprev = EncodingHelper.prevCharHead(str, s);
+    }
+
+    private void opLookBehindSb() {
+        int tlen = code[ip++];
+        s -= tlen;
+        if (s < str) {opFail(); return;}
+        sprev = s == str ? -1 : s - 1;
+    }
+
+    private void opPushLookBehindNot() {
+        int addr = code[ip++];
+        int tlen = code[ip++];
+        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 opCall() {
+        int addr = code[ip++];
+        pushCallFrame(ip);
+        ip = addr; // absolute address
+    }
+
+    private void opReturn() {
+        ip = sreturn();
+        pushReturn();
+    }
+
+    private void opFail() {
+        if (stack == null) {
+            ip = regex.codeLength - 1;
+            return;
+        }
+
+
+        StackEntry e = pop();
+        ip    = e.getStatePCode();
+        s     = e.getStatePStr();
+        sprev = e.getStatePStrPrev();
+
+        if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
+            if (e.getStateCheck() != 0) {
+                e.type = STATE_CHECK_MARK;
+                stk++;
+            }
+        }
+    }
+
+    private int finish() {
+        return bestLen;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/ByteCodePrinter.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,416 @@
+/*
+ * 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;
+    int operantCount;
+    WarnCallback warnings;
+
+    public ByteCodePrinter(Regex regex) {
+        code = regex.code;
+        codeLength = regex.codeLength;
+        operands = regex.operands;
+        operantCount = regex.operandLength;
+
+        templates = regex.templates;
+        warnings = regex.warnings;
+    }
+
+    public String byteCodeListToString() {
+        return compiledByteCodeListToString();
+    }
+
+    private void pString(StringBuilder sb, int len, int s) {
+        sb.append(":");
+        while (len-- > 0) sb.append(new String(new byte[]{(byte)code[s++]}));
+    }
+
+    private void pStringFromTemplate(StringBuilder sb, int len, byte[]tm, int idx) {
+        sb.append(":T:");
+        while (len-- > 0) sb.append(new String(new byte[]{tm[idx++]}));
+    }
+
+    private void pLenString(StringBuilder sb, int len, int mbLen, int s) {
+        int x = len * mbLen;
+        sb.append(":" + len + ":");
+        while (x-- > 0) sb.append(new String(new byte[]{(byte)code[s++]}));
+    }
+
+    private void pLenStringFromTemplate(StringBuilder sb, int len, int mbLen, char[] tm, int idx) {
+        int x = len * mbLen;
+        sb.append(":T:" + len + ":");
+        while (x-- > 0) sb.append(new String(new byte[]{(byte)tm[idx++]}));
+    }
+
+    public int compiledByteCodeToString(StringBuilder sb, int bp) {
+        int len, n, mem, addr, scn, cod;
+        BitSet bs;
+        CClassNode cc;
+        int tm, idx;
+
+        sb.append("[" + OPCode.OpCodeNames[code[bp]]);
+        int argType = OPCode.OpCodeArgTypes[code[bp]];
+        int ip = bp;
+        if (argType != Arguments.SPECIAL) {
+            bp++;
+            switch (argType) {
+            case Arguments.NON:
+                break;
+
+            case Arguments.RELADDR:
+                sb.append(":(" + code[bp] + ")");
+                bp += OPSize.RELADDR;
+                break;
+
+            case Arguments.ABSADDR:
+                sb.append(":(" + code[bp] + ")");
+                bp += OPSize.ABSADDR;
+                break;
+
+            case Arguments.LENGTH:
+                sb.append(":" + code[bp]);
+                bp += OPSize.LENGTH;
+                break;
+
+            case Arguments.MEMNUM:
+                sb.append(":" + code[bp]);
+                bp += OPSize.MEMNUM;
+                break;
+
+            case Arguments.OPTION:
+                sb.append(":" + code[bp]);
+                bp += OPSize.OPTION;
+                break;
+
+            case Arguments.STATE_CHECK:
+                sb.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:
+            case OPCode.ANYCHAR_STAR_PEEK_NEXT_SB:
+            case OPCode.ANYCHAR_ML_STAR_PEEK_NEXT_SB:
+                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, 1, templates[tm], idx);
+                } else {
+                    pLenString(sb, len, 1, bp);
+                    bp += len;
+                }
+                break;
+
+            case OPCode.EXACTMB2N1:
+                pString(sb, 2, bp);
+                bp += 2;
+                break;
+
+            case OPCode.EXACTMB2N2:
+                pString(sb, 4, bp);
+                bp += 4;
+                break;
+
+            case OPCode.EXACTMB2N3:
+                pString(sb, 6, bp);
+                bp += 6;
+                break;
+
+            case OPCode.EXACTMB2N:
+                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, 2, templates[tm], idx);
+                } else {
+                    pLenString(sb, len, 2, bp);
+                    bp += len * 2;
+                }
+                break;
+
+            case OPCode.EXACTMB3N:
+                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, 3, templates[tm], idx);
+                } else {
+                    pLenString(sb, len, 3, bp);
+                    bp += len * 3;
+                }
+                break;
+
+            case OPCode.EXACTMBN:
+                int mbLen = code[bp];
+                bp += OPSize.LENGTH;
+                len = code[bp];
+                bp += OPSize.LENGTH;
+                n = len * mbLen;
+
+                if (Config.USE_STRING_TEMPLATES) {
+                    tm = code[bp];
+                    bp += OPSize.INDEX;
+                    idx = code[bp];
+                    bp += OPSize.INDEX;
+                    sb.append(":T:" + mbLen + ":" + len + ":");
+
+                    while (n-- > 0) sb.append(new String(new char[]{templates[tm][idx++]}));
+                } else {
+                    sb.append(":" + mbLen + ":" + len + ":");
+
+                    while (n-- > 0) sb.append(new String(new byte[]{(byte)code[bp++]}));
+                }
+
+                break;
+
+            case OPCode.EXACT1_IC:
+            case OPCode.EXACT1_IC_SB:
+                final int MAX_CHAR_LENGTH = 6;
+                byte[]bytes = new byte[MAX_CHAR_LENGTH];
+                for (int i = 0; bp + i < code.length && i < MAX_CHAR_LENGTH; i++) bytes[i] = (byte)code[bp + i];
+                pString(sb, 1, bp);
+                bp++;
+                break;
+
+            case OPCode.EXACTN_IC:
+            case OPCode.EXACTN_IC_SB:
+                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, 1, templates[tm], idx);
+                } else {
+                    pLenString(sb, len, 1, bp);
+                    bp += len;
+                }
+                break;
+
+            case OPCode.CCLASS:
+            case OPCode.CCLASS_SB:
+                bs = new BitSet();
+                System.arraycopy(code, bp, bs.bits, 0, BitSet.BITSET_SIZE);
+                n = bs.numOn();
+                bp += BitSet.BITSET_SIZE;
+                sb.append(":" + n);
+                break;
+
+            case OPCode.CCLASS_NOT:
+            case OPCode.CCLASS_NOT_SB:
+                bs = new BitSet();
+                System.arraycopy(code, bp, bs.bits, 0, BitSet.BITSET_SIZE);
+                n = bs.numOn();
+                bp += BitSet.BITSET_SIZE;
+                sb.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(":" + cod + ":" + 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(":" + n + ":" + cod + ":" + len);
+                break;
+
+            case OPCode.CCLASS_NODE:
+                cc = (CClassNode)operands[code[bp]];
+                bp += OPSize.POINTER;
+                n = cc.bs.numOn();
+                sb.append(":" + cc + ":" + n);
+                break;
+
+            case OPCode.BACKREFN_IC:
+                mem = code[bp];
+                bp += OPSize.MEMNUM;
+                sb.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: {
+                int option = code[bp];
+                bp += OPSize.OPTION;
+                sb.append(":" + option);
+                int level = code[bp];
+                bp += OPSize.LENGTH;
+                sb.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(":" + mem + ":" + addr);
+                break;
+
+            case OPCode.PUSH_OR_JUMP_EXACT1:
+            case OPCode.PUSH_IF_PEEK_NEXT:
+                addr = code[bp];
+                bp += OPSize.RELADDR;
+                sb.append(":(" + addr + ")");
+                pString(sb, 1, bp);
+                bp++;
+                break;
+
+            case OPCode.LOOK_BEHIND:
+            case OPCode.LOOK_BEHIND_SB:
+                len = code[bp];
+                bp += OPSize.LENGTH;
+                sb.append(":" + len);
+                break;
+
+            case OPCode.PUSH_LOOK_BEHIND_NOT:
+                addr = code[bp];
+                bp += OPSize.RELADDR;
+                len = code[bp];
+                bp += OPSize.LENGTH;
+                sb.append(":" + len + ":(" + addr + ")");
+                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(":" + scn + ":(" + addr + ")");
+                break;
+
+            default:
+                throw new InternalException("undefined code: " + code[--bp]);
+            }
+        }
+
+        sb.append("]");
+
+        // @opcode_address(opcode_size)
+        if (Config.DEBUG_COMPILE_BYTE_CODE_INFO) sb.append("@" + ip + "(" + (bp - ip) + ")");
+
+        return bp;
+    }
+
+    private String compiledByteCodeListToString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("code length: " + codeLength + "\n");
+
+        int ncode = 0;
+        int bp = 0;
+        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();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/CaptureTreeNode.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,74 @@
+/*
+ * 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;
+
+public class CaptureTreeNode {
+
+
+    int group;
+    int beg;
+    int end;
+    // int allocated;
+    int numChildren;
+    CaptureTreeNode[]children;
+
+    CaptureTreeNode() {
+        beg = Region.REGION_NOTPOS;
+        end = Region.REGION_NOTPOS;
+        group = -1;
+    }
+
+    static final int HISTORY_TREE_INIT_ALLOC_SIZE = 8;
+    void addChild(CaptureTreeNode child) {
+        if (children == null) {
+            children = new CaptureTreeNode[HISTORY_TREE_INIT_ALLOC_SIZE];
+        } else if (numChildren >= children.length) {
+            CaptureTreeNode[]tmp = new CaptureTreeNode[children.length << 1];
+            System.arraycopy(children, 0, tmp, 0, children.length);
+            children = tmp;
+        }
+
+        children[numChildren] = child;
+        numChildren++;
+    }
+
+    void clear() {
+        for (int i=0; i<numChildren; i++) {
+            children[i] = null; // ???
+        }
+        numChildren = 0;
+        beg = end = Region.REGION_NOTPOS;
+        group = -1;
+    }
+
+    CaptureTreeNode cloneTree() {
+        CaptureTreeNode clone = new CaptureTreeNode();
+        clone.beg = beg;
+        clone.end = end;
+
+        for (int i=0; i<numChildren; i++) {
+            CaptureTreeNode child = children[i].cloneTree();
+            clone.addChild(child);
+        }
+        return clone;
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/CodeRangeBuffer.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,378 @@
+/*
+ * 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.exception.ErrorMessages;
+import jdk.nashorn.internal.runtime.regexp.joni.exception.ValueException;
+
+public final class CodeRangeBuffer {
+    private static final int INIT_MULTI_BYTE_RANGE_SIZE = 5;
+    private static final int ALL_MULTI_BYTE_RANGE = 0x7fffffff;
+
+    int[]p;
+    int used;
+
+    public CodeRangeBuffer(int[]ranges) {
+        p = ranges;
+        used = ranges[0] + 1;
+    }
+
+    public CodeRangeBuffer() {
+        p = new int[INIT_MULTI_BYTE_RANGE_SIZE];
+        writeCodePoint(0, 0);
+    }
+
+    public int[]getCodeRange() {
+        return p;
+    }
+
+    private CodeRangeBuffer(CodeRangeBuffer orig) {
+        p = new int[orig.p.length];
+        System.arraycopy(orig.p, 0, p, 0, p.length);
+        used = orig.used;
+    }
+
+    public String toString() {
+        StringBuilder buf = new StringBuilder();
+        buf.append("CodeRange");
+        buf.append("\n  used: " + used);
+        buf.append("\n  code point: " + p[0]);
+        buf.append("\n  ranges: ");
+
+        for (int i=0; i<p[0]; i++) {
+            buf.append("[" + rangeNumToString(p[i * 2 + 1]) + ".." + rangeNumToString(p[i * 2 + 2]) + "]");
+            if (i > 0 && i % 6 == 0) buf.append("\n          ");
+        }
+
+        return buf.toString();
+    }
+
+    private static String rangeNumToString(int num){
+        return "0x" + Integer.toString(num, 16);
+    }
+
+    public void expand(int low) {
+        int length = p.length;
+        do { length <<= 1; } while (length < low);
+        int[]tmp = new int[length];
+        System.arraycopy(p, 0, tmp, 0, used);
+        p = tmp;
+    }
+
+    public void ensureSize(int size) {
+        int length = p.length;
+        while (length < size ) { length <<= 1; }
+        if (p.length != length) {
+            int[]tmp = new int[length];
+            System.arraycopy(p, 0, tmp, 0, used);
+            p = tmp;
+        }
+    }
+
+    private void moveRight(int from, int to, int n) {
+        if (to + n > p.length) expand(to + n);
+        System.arraycopy(p, from, p, to, n);
+        if (to + n > used) used = to + n;
+    }
+
+    protected void moveLeft(int from, int to, int n) {
+        System.arraycopy(p, from, p, to, n);
+    }
+
+    private void moveLeftAndReduce(int from, int to) {
+        System.arraycopy(p, from, p, to, used - from);
+        used -= from - to;
+    }
+
+    public void writeCodePoint(int pos, int b) {
+        int u = pos + 1;
+        if (p.length < u) expand(u);
+        p[pos] = b;
+        if (used < u) used = u;
+    }
+
+    public CodeRangeBuffer clone() {
+        return new CodeRangeBuffer(this);
+    }
+
+    // ugly part: these methods should be made OO
+    // add_code_range_to_buf
+    public static CodeRangeBuffer addCodeRangeToBuff(CodeRangeBuffer pbuf, int from, int to) {
+        if (from > to) {
+            int n = from;
+            from = to;
+            to = n;
+        }
+
+        if (pbuf == null) pbuf = new CodeRangeBuffer(); // move to CClassNode
+
+        int[]p = pbuf.p;
+        int n = p[0];
+
+        int low = 0;
+        int bound = n;
+
+        while (low < bound) {
+            int x = (low + bound) >>> 1;
+            if (from > p[x * 2 + 2]) {
+                low = x + 1;
+            } else {
+                bound = x;
+            }
+        }
+
+        int high = low;
+        bound = n;
+
+        while (high < bound) {
+            int x = (high + bound) >>> 1;
+            if (to >= p[x * 2 + 1] - 1) {
+                high = x + 1;
+            } else {
+                bound = x;
+            }
+        }
+
+        int incN = low + 1 - high;
+
+        if (n + incN > Config.MAX_MULTI_BYTE_RANGES_NUM) throw new ValueException(ErrorMessages.ERR_TOO_MANY_MULTI_BYTE_RANGES);
+
+        if (incN != 1) {
+            if (from > p[low * 2 + 1]) from = p[low * 2 + 1];
+            if (to < p[(high - 1) * 2 + 2]) to = p[(high - 1) * 2 + 2];
+        }
+
+        if (incN != 0 && high < n) {
+            int fromPos = 1 + high * 2;
+            int toPos = 1 + (low + 1) * 2;
+            int size = (n - high) * 2;
+
+            if (incN > 0) {
+                pbuf.moveRight(fromPos, toPos, size);
+            } else {
+                pbuf.moveLeftAndReduce(fromPos, toPos);
+            }
+        }
+
+        int pos = 1 + low * 2;
+        // pbuf.ensureSize(pos + 2);
+        pbuf.writeCodePoint(pos, from);
+        pbuf.writeCodePoint(pos + 1, to);
+        n += incN;
+        pbuf.writeCodePoint(0, n);
+
+        return pbuf;
+    }
+
+    // add_code_range, be aware of it returning null!
+    public static CodeRangeBuffer addCodeRange(CodeRangeBuffer pbuf, ScanEnvironment env, int from, int to) {
+        if (from >to) {
+            if (env.syntax.allowEmptyRangeInCC()) {
+                return pbuf;
+            } else {
+                throw new ValueException(ErrorMessages.ERR_EMPTY_RANGE_IN_CHAR_CLASS);
+            }
+        }
+        return addCodeRangeToBuff(pbuf, from, to);
+    }
+
+    // SET_ALL_MULTI_BYTE_RANGE
+    protected static CodeRangeBuffer setAllMultiByteRange(CodeRangeBuffer pbuf) {
+        return addCodeRangeToBuff(pbuf, EncodingHelper.mbcodeStartPosition(), ALL_MULTI_BYTE_RANGE);
+    }
+
+    // ADD_ALL_MULTI_BYTE_RANGE
+    public static CodeRangeBuffer addAllMultiByteRange(CodeRangeBuffer pbuf) {
+        return setAllMultiByteRange(pbuf);
+    }
+
+    // not_code_range_buf
+    public static CodeRangeBuffer notCodeRangeBuff(CodeRangeBuffer bbuf) {
+        CodeRangeBuffer pbuf = null;
+
+        if (bbuf == null) return setAllMultiByteRange(pbuf);
+
+        int[]p = bbuf.p;
+        int n = p[0];
+
+        if (n <= 0) return setAllMultiByteRange(pbuf);
+
+        int pre = EncodingHelper.mbcodeStartPosition();
+
+        int from;
+        int to = 0;
+        for (int i=0; i<n; i++) {
+            from = p[i * 2 + 1];
+            to = p[i * 2 + 2];
+            if (pre <= from - 1) {
+                pbuf = addCodeRangeToBuff(pbuf, pre, from - 1);
+            }
+            if (to == ALL_MULTI_BYTE_RANGE) break;
+            pre = to + 1;
+        }
+
+        if (to < ALL_MULTI_BYTE_RANGE) pbuf = addCodeRangeToBuff(pbuf, to + 1, ALL_MULTI_BYTE_RANGE);
+        return pbuf;
+    }
+
+    // or_code_range_buf
+    public static CodeRangeBuffer orCodeRangeBuff(CodeRangeBuffer bbuf1, boolean not1,
+                                                  CodeRangeBuffer bbuf2, boolean not2) {
+        CodeRangeBuffer pbuf = null;
+
+        if (bbuf1 == null && bbuf2 == null) {
+            if (not1 || not2) {
+                return setAllMultiByteRange(pbuf);
+            }
+            return null;
+        }
+
+        if (bbuf2 == null) {
+            CodeRangeBuffer tbuf;
+            boolean tnot;
+            // swap
+            tnot = not1; not1 = not2; not2 = tnot;
+            tbuf = bbuf1; bbuf1 = bbuf2; bbuf2 = tbuf;
+        }
+
+        if (bbuf1 == null) {
+            if (not1) {
+                return setAllMultiByteRange(pbuf);
+            } else {
+                if (!not2) {
+                    return bbuf2.clone();
+                } else {
+                    return notCodeRangeBuff(bbuf2);
+                }
+            }
+        }
+
+        if (not1) {
+            CodeRangeBuffer tbuf;
+            boolean tnot;
+            // swap
+            tnot = not1; not1 = not2; not2 = tnot;
+            tbuf = bbuf1; bbuf1 = bbuf2; bbuf2 = tbuf;
+        }
+
+        if (!not2 && !not1) { /* 1 OR 2 */
+            pbuf = bbuf2.clone();
+        } else if (!not1) { /* 1 OR (not 2) */
+            pbuf = notCodeRangeBuff(bbuf2);
+        }
+
+        int[]p1 = bbuf1.p;
+        int n1 = p1[0];
+
+        for (int i=0; i<n1; i++) {
+            int from = p1[i * 2 + 1];
+            int to = p1[i * 2 + 2];
+            pbuf = addCodeRangeToBuff(pbuf, from, to);
+        }
+
+        return pbuf;
+    }
+
+    // and_code_range1
+    public static CodeRangeBuffer andCodeRange1(CodeRangeBuffer pbuf, int from1, int to1, int[]data, int n) {
+        for (int i=0; i<n; i++) {
+            int from2 = data[i * 2 + 1];
+            int to2 = data[i * 2 + 2];
+            if (from2 < from1) {
+                if (to2 < from1) {
+                    continue;
+                } else {
+                    from1 = to2 + 1;
+                }
+            } else if (from2 <= to1) {
+                if (to2 < to1) {
+                    if (from1 <= from2 - 1) {
+                        pbuf = addCodeRangeToBuff(pbuf, from1, from2 - 1);
+                    }
+                    from1 = to2 + 1;
+                } else {
+                    to1 = from2 - 1;
+                }
+            } else {
+                from1 = from2;
+            }
+            if (from1 > to1) break;
+        }
+
+        if (from1 <= to1) {
+            pbuf = addCodeRangeToBuff(pbuf, from1, to1);
+        }
+
+        return pbuf;
+    }
+
+    // and_code_range_buf
+    public static CodeRangeBuffer andCodeRangeBuff(CodeRangeBuffer bbuf1, boolean not1,
+                                                   CodeRangeBuffer bbuf2, boolean not2) {
+        CodeRangeBuffer pbuf = null;
+
+        if (bbuf1 == null) {
+            if (not1 && bbuf2 != null) return bbuf2.clone(); /* not1 != 0 -> not2 == 0 */
+            return null;
+        } else if (bbuf2 == null) {
+            if (not2) return bbuf1.clone();
+            return null;
+        }
+
+        if (not1) {
+            CodeRangeBuffer tbuf;
+            boolean tnot;
+            // swap
+            tnot = not1; not1 = not2; not2 = tnot;
+            tbuf = bbuf1; bbuf1 = bbuf2; bbuf2 = tbuf;
+        }
+
+        int[]p1 = bbuf1.p;
+        int n1 = p1[0];
+        int[]p2 = bbuf2.p;
+        int n2 = p2[0];
+
+        if (!not2 && !not1) { /* 1 AND 2 */
+            for (int i=0; i<n1; i++) {
+                int from1 = p1[i * 2 + 1];
+                int to1 = p1[i * 2 + 2];
+
+                for (int j=0; j<n2; j++) {
+                    int from2 = p2[j * 2 + 1];
+                    int to2 = p2[j * 2 + 2];
+
+                    if (from2 > to1) break;
+                    if (to2 < from1) continue;
+                    int from = from1 > from2 ? from1 : from2;
+                    int to = to1 < to2 ? to1 : to2;
+                    pbuf = addCodeRangeToBuff(pbuf, from, to);
+                }
+            }
+        } else if (!not1) { /* 1 AND (not 2) */
+            for (int i=0; i<n1; i++) {
+                int from1 = p1[i * 2 + 1];
+                int to1 = p1[i * 2 + 2];
+                pbuf = andCodeRange1(pbuf, from1, to1, p2, n2);
+            }
+        }
+
+        return pbuf;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/Compiler.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,178 @@
+/*
+ * 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.AnchorNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.BackRefNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.CClassNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.CTypeNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.CallNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.ConsAltNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.EncloseNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.Node;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode;
+import jdk.nashorn.internal.runtime.regexp.joni.ast.StringNode;
+import jdk.nashorn.internal.runtime.regexp.joni.constants.NodeType;
+import jdk.nashorn.internal.runtime.regexp.joni.exception.ErrorMessages;
+import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
+import jdk.nashorn.internal.runtime.regexp.joni.exception.SyntaxException;
+
+abstract class Compiler implements ErrorMessages {
+    protected final Analyser analyser;
+    protected final Regex regex;
+
+    protected Compiler(Analyser analyser) {
+        this.analyser = analyser;
+        this.regex = analyser.regex;
+    }
+
+    final void compile() {
+        prepare();
+        compileTree(analyser.root);
+        finish();
+    }
+
+    protected abstract void prepare();
+    protected abstract void finish();
+
+    protected abstract void compileAltNode(ConsAltNode node);
+
+    private void compileStringRawNode(StringNode sn) {
+        if (sn.length() <= 0) return;
+        addCompileString(sn.chars, sn.p, 1 /*sb*/, sn.length(), false);
+    }
+
+    private void compileStringNode(StringNode node) {
+        StringNode sn = node;
+        if (sn.length() <= 0) return;
+
+        boolean ambig = sn.isAmbig();
+
+        int p, prev;
+        p = prev = sn.p;
+        int end = sn.end;
+        char[] chars = sn.chars;
+        p++;
+        int slen = 1;
+
+        while (p < end) {
+            slen++;
+            p++;
+        }
+        addCompileString(chars, prev, 1, slen, ambig);
+    }
+
+    protected abstract void addCompileString(char[] chars, int p, int mbLength, int strLength, boolean ignoreCase);
+
+    protected abstract void compileCClassNode(CClassNode node);
+    protected abstract void compileCTypeNode(CTypeNode node);
+    protected abstract void compileAnyCharNode();
+    protected abstract void compileCallNode(CallNode node);
+    protected abstract void compileBackrefNode(BackRefNode node);
+    protected abstract void compileCECQuantifierNode(QuantifierNode node);
+    protected abstract void compileNonCECQuantifierNode(QuantifierNode node);
+    protected abstract void compileOptionNode(EncloseNode node);
+    protected abstract void compileEncloseNode(EncloseNode node);
+    protected abstract void compileAnchorNode(AnchorNode node);
+
+    protected final void compileTree(Node node) {
+        switch (node.getType()) {
+        case NodeType.LIST:
+            ConsAltNode lin = (ConsAltNode)node;
+            do {
+                compileTree(lin.car);
+            } while ((lin = lin.cdr) != null);
+            break;
+
+        case NodeType.ALT:
+            compileAltNode((ConsAltNode)node);
+            break;
+
+        case NodeType.STR:
+            StringNode sn = (StringNode)node;
+            if (sn.isRaw()) {
+                compileStringRawNode(sn);
+            } else {
+                compileStringNode(sn);
+            }
+            break;
+
+        case NodeType.CCLASS:
+            compileCClassNode((CClassNode)node);
+            break;
+
+        case NodeType.CTYPE:
+            compileCTypeNode((CTypeNode)node);
+            break;
+
+        case NodeType.CANY:
+            compileAnyCharNode();
+            break;
+
+        case NodeType.BREF:
+            compileBackrefNode((BackRefNode)node);
+            break;
+
+        case NodeType.CALL:
+            if (Config.USE_SUBEXP_CALL) {
+                compileCallNode((CallNode)node);
+                break;
+            } // USE_SUBEXP_CALL
+            break;
+
+        case NodeType.QTFR:
+            if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
+                compileCECQuantifierNode((QuantifierNode)node);
+            } else {
+                compileNonCECQuantifierNode((QuantifierNode)node);
+            }
+            break;
+
+        case NodeType.ENCLOSE:
+            EncloseNode enode = (EncloseNode)node;
+            if (enode.isOption()) {
+                compileOptionNode(enode);
+            } else {
+                compileEncloseNode(enode);
+            }
+            break;
+
+        case NodeType.ANCHOR:
+            compileAnchorNode((AnchorNode)node);
+            break;
+
+        default:
+            // undefined node type
+            newInternalException(ERR_PARSER_BUG);
+        } // switch
+    }
+
+    protected final void compileTreeNTimes(Node node, int n) {
+        for (int i=0; i<n; i++) compileTree(node);
+    }
+
+    protected void newSyntaxException(String message) {
+        throw new SyntaxException(message);
+    }
+
+    protected void newInternalException(String message) {
+        throw new InternalException(message);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/Config.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,100 @@
+/*
+ * 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 java.io.PrintStream;
+
+public interface Config {
+    final int CHAR_TABLE_SIZE = 256;
+
+    /* from jcodings */
+    final boolean VANILLA = false;
+    final int INTERNAL_ENC_CASE_FOLD_MULTI_CHAR = (1<<30);
+    final int ENC_CASE_FOLD_MIN = INTERNAL_ENC_CASE_FOLD_MULTI_CHAR;
+    final int ENC_CASE_FOLD_DEFAULT = ENC_CASE_FOLD_MIN;
+    final boolean USE_CRNL_AS_LINE_TERMINATOR = false;
+
+    final boolean USE_NAMED_GROUP = true;
+    final boolean USE_SUBEXP_CALL = true;
+    final boolean USE_BACKREF_WITH_LEVEL = true;                            /* \k<name+n>, \k<name-n> */
+
+    final boolean USE_MONOMANIAC_CHECK_CAPTURES_IN_ENDLESS_REPEAT = true; /* /(?:()|())*\2/ */
+    final boolean USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE = true;     /* /\n$/ =~ "\n" */
+    final boolean USE_WARNING_REDUNDANT_NESTED_REPEAT_OPERATOR = false;
+
+    final boolean CASE_FOLD_IS_APPLIED_INSIDE_NEGATIVE_CCLASS = true;
+
+    final boolean USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE = false;
+    final boolean USE_CAPTURE_HISTORY = false;
+    final boolean USE_VARIABLE_META_CHARS = true;
+    final boolean USE_WORD_BEGIN_END = true;                                /* "\<": word-begin, "\>": word-end */
+    final boolean USE_POSIX_API_REGION_OPTION = true;                           /* needed for POSIX API support */
+    final boolean USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE = true;
+    final boolean USE_COMBINATION_EXPLOSION_CHECK = false;
+
+    final int NREGION                   = 10;
+    final int MAX_BACKREF_NUM           = 1000;
+    final int MAX_REPEAT_NUM            = 100000;
+    final int MAX_MULTI_BYTE_RANGES_NUM = 10000;
+
+
+    final boolean USE_WARN = true;
+
+    // internal config
+    final boolean USE_PARSE_TREE_NODE_RECYCLE       = true;
+    final boolean USE_OP_PUSH_OR_JUMP_EXACT         = true;
+    final boolean USE_SHARED_CCLASS_TABLE           = false;
+    final boolean USE_QTFR_PEEK_NEXT                = true;
+
+    final int INIT_MATCH_STACK_SIZE                 = 64;
+    final int DEFAULT_MATCH_STACK_LIMIT_SIZE        = 0;        /* unlimited */
+    final int NUMBER_OF_POOLED_STACKS               = 4;
+
+
+
+    final boolean DONT_OPTIMIZE                     = false;
+
+    final boolean USE_STRING_TEMPLATES              = true; // use embeded string templates in Regex object as byte arrays instead of compiling them into int bytecode array
+
+
+    final int MAX_CAPTURE_HISTORY_GROUP             = 31;
+
+
+    final int CHECK_STRING_THRESHOLD_LEN            = 7;
+    final int CHECK_BUFF_MAX_SIZE                   = 0x4000;
+
+    final boolean NON_UNICODE_SDW                   = true;
+
+
+    final PrintStream log = System.out;
+    final PrintStream err = System.err;
+
+    final boolean DEBUG_ALL                         = false;
+
+    final boolean DEBUG                             = DEBUG_ALL;
+    final boolean DEBUG_PARSE_TREE                  = DEBUG_ALL;
+    final boolean DEBUG_PARSE_TREE_RAW              = true;
+    final boolean DEBUG_COMPILE                     = DEBUG_ALL;
+    final boolean DEBUG_COMPILE_BYTE_CODE_INFO      = DEBUG_ALL;
+    final boolean DEBUG_SEARCH                      = DEBUG_ALL;
+    final boolean DEBUG_MATCH                       = DEBUG_ALL;
+    final boolean DEBUG_ASM                         = true;
+    final boolean DEBUG_ASM_EXEC                    = true;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/EncodingHelper.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,285 @@
+/*
+ * 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.encoding.CharacterType;
+import jdk.nashorn.internal.runtime.regexp.joni.encoding.IntHolder;
+
+import java.util.Arrays;
+
+public class EncodingHelper {
+
+    public final static char NEW_LINE = 0xa;
+    public final static char RETURN   = 0xd;
+
+    final static char[] EMPTYCHARS = new char[0];
+    final static int[][] codeRanges = new int[15][];
+
+    public static int digitVal(int code) {
+        return code - '0';
+    }
+
+    public static int odigitVal(int code) {
+        return digitVal(code);
+    }
+
+    public static boolean isXDigit(int code) {
+        return Character.isDigit(code) || (code >= 'a' && code <= 'f') || (code >= 'A' && code <= 'F');
+    }
+
+    public static int xdigitVal(int code) {
+        if (Character.isDigit(code)) {
+            return code - '0';
+        } else if (code >= 'a' && code <= 'f') {
+            return code - 'a' + 10;
+        } else {
+            return code - 'A' + 10;
+        }
+    }
+
+    public static boolean isDigit(int code) {
+        return code >= '0' && code <= '9';
+    }
+
+    public static boolean isWord(int code) {
+        // letter, digit, or '_'
+        return (1 << Character.getType(code) & CharacterType.WORD_MASK) != 0;
+    }
+
+    public static boolean isNewLine(int code) {
+        return code == NEW_LINE;
+    }
+
+    public static boolean isNewLine(char[] chars, int p, int end) {
+        return p < end && chars[p] == NEW_LINE;
+    }
+
+    public static boolean isCrnl(char[] chars, int p, int end) {
+        return p + 1 < end && chars[p] == RETURN && chars[p + 1] == NEW_LINE;
+    }
+
+    // Encoding.prevCharHead
+    public static int prevCharHead(int p, int s) {
+        return s <= p ? -1 : s - 1;
+    }
+
+    /* onigenc_get_right_adjust_char_head_with_prev */
+    public static int rightAdjustCharHeadWithPrev(int s, IntHolder prev) {
+        if (prev != null) prev.value = -1; /* Sorry */
+        return s;
+    }
+
+    // Encoding.stepBack
+    public static int stepBack(int p, int s, int n) {
+       while (s != -1 && n-- > 0) {
+           if (s <= p) return -1;
+           s--;
+       }
+       return s;
+    }
+
+    /* onigenc_with_ascii_strncmp */
+    public static int strNCmp(char[] chars1, int p1, int end, char[] chars2, int p2, int n) {
+        while (n-- > 0) {
+            if (p1 >= end) return chars2[p2];
+            int c = chars1[p1];
+            int x = chars2[p2] - c;
+            if (x != 0) return x;
+
+            p2++;
+            p1++;
+        }
+        return 0;
+    }
+
+    public static int mbcToCode(byte[] bytes, int p, int end) {
+        int code = 0;
+        for (int i = p; i < end; i++) {
+            code = (code << 8) | (bytes[i] & 0xff);
+        }
+        return code;
+    }
+
+    public static int mbcodeStartPosition() {
+        return 0x80;
+    }
+
+    public static char[] caseFoldCodesByString(int flag, char c) {
+        if (Character.isUpperCase(c)) {
+            return new char[] {Character.toLowerCase(c)};
+        } else if (Character.isLowerCase(c)) {
+            return new char[] {Character.toUpperCase(c)};
+        } else {
+            return EMPTYCHARS;
+        }
+    }
+
+    public static void applyAllCaseFold(int flag, ApplyCaseFold fun, Object arg) {
+        int[] code = new int[1];
+
+        for (int c = 0; c < 0xffff; c++) {
+            if (Character.getType(c) == Character.LOWERCASE_LETTER) {
+
+                int upper = code[0] = Character.toUpperCase(c);
+                fun.apply(c, code, 1, arg);
+
+                code[0] = c;
+                fun.apply(upper, code, 1, arg);
+            }
+        }
+    }
+
+    // CodeRange.isInCodeRange
+    public static boolean isInCodeRange(int[]p, int code) {
+        int low = 0;
+        int n = p[0];
+        int high = n;
+
+        while (low < high) {
+            int x = (low + high) >> 1;
+            if (code > p[(x << 1) + 2]) {
+                low = x + 1;
+            } else {
+                high = x;
+            }
+        }
+        return low < n && code >= p[(low << 1) + 1];
+    }
+
+    public static int[] ctypeCodeRange(int ctype, IntHolder sbOut) {
+        sbOut.value = 0x100; // use bitset for codes smaller than 256
+        int[] range = null;
+
+        if (ctype < codeRanges.length) {
+            range = codeRanges[ctype];
+
+            if (range == null) {
+                // format: [numberOfRanges, rangeStart, rangeEnd, ...]
+                range = new int[16];
+                int rangeCount = 0;
+                int lastCode = -2;
+
+                for (int code = 0; code <= 0xffff; code++) {
+                    if (isCodeCType(code, ctype)) {
+                        if (lastCode < code -1) {
+                            if (rangeCount * 2 + 2 >= range.length) {
+                                range = Arrays.copyOf(range, range.length * 2);
+                            }
+                            range[rangeCount * 2 + 1] = code;
+                            rangeCount++;
+                        }
+                        range[rangeCount * 2] = lastCode = code;
+                    }
+                }
+
+                if (rangeCount * 2 + 1 < range.length) {
+                    range = Arrays.copyOf(range, rangeCount * 2 + 1);
+                }
+
+                range[0] = rangeCount;
+                codeRanges[ctype] = range;
+            }
+        }
+
+        return range;
+    }
+
+    // CodeRange.isInCodeRange
+    public static boolean isInCodeRange(int[]p, int offset, int code) {
+        int low = 0;
+        int n = p[offset];
+        int high = n ;
+
+        while (low < high) {
+            int x = (low + high) >> 1;
+            if (code > p[(x << 1) + 2 + offset]) {
+                low = x + 1;
+            } else {
+                high = x;
+            }
+        }
+        return low < n && code >= p[(low << 1) + 1 + offset];
+    }
+
+    /**
+     * @see [http://www.geocities.jp/kosako3/oniguruma/doc/RE.txt]
+     */
+    public static boolean isCodeCType(int code, int ctype) {
+        int type;
+        switch (ctype) {
+            case CharacterType.NEWLINE:
+                return code == EncodingHelper.NEW_LINE;
+            case CharacterType.ALPHA:
+                return (1 << Character.getType(code) & CharacterType.ALPHA_MASK) != 0;
+            case CharacterType.BLANK:
+                return code == 0x09 || Character.getType(code) == Character.SPACE_SEPARATOR;
+            case CharacterType.CNTRL:
+                type = Character.getType(code);
+                return (1 << type & CharacterType.CNTRL_MASK) != 0 || type == Character.UNASSIGNED;
+            case CharacterType.DIGIT:
+                return EncodingHelper.isDigit(code);
+            case CharacterType.GRAPH:
+                switch (code) {
+                    case 0x09:
+                    case 0x0a:
+                    case 0x0b:
+                    case 0x0c:
+                    case 0x0d:
+                        return false;
+                    default:
+                        type = Character.getType(code);
+                        return (1 << type & CharacterType.GRAPH_MASK) == 0 && type != Character.UNASSIGNED;
+                }
+            case CharacterType.LOWER:
+                return Character.isLowerCase(code);
+            case CharacterType.PRINT:
+                type = Character.getType(code);
+                return (1 << type & CharacterType.PRINT_MASK) == 0 && type != Character.UNASSIGNED;
+            case CharacterType.PUNCT:
+                return (1 << Character.getType(code) & CharacterType.PUNCT_MASK) != 0;
+            case CharacterType.SPACE:
+                // ECMA 7.2 and 7.3
+                switch (code) {
+                    case 0x09:
+                    case 0x0a:
+                    case 0x0b:
+                    case 0x0c:
+                    case 0x0d:
+                        return true;
+                    default:
+                        // true if Unicode separator or BOM
+                        return (1 << Character.getType(code) & CharacterType.SPACE_MASK) != 0 || code == 0xfeff;
+                }
+            case CharacterType.UPPER:
+                return Character.isUpperCase(code);
+            case CharacterType.XDIGIT:
+                return EncodingHelper.isXDigit(code);
+            case CharacterType.WORD:
+                return (1 << Character.getType(code) & CharacterType.WORD_MASK) != 0;
+            case CharacterType.ALNUM:
+                return (1 << Character.getType(code) & CharacterType.ALNUM_MASK) != 0;
+            case CharacterType.ASCII:
+                return code < 0x80;
+            default:
+                throw new RuntimeException("illegal character type: " + ctype);
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/Lexer.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,1274 @@
+/*
+ * 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.Option.isSingleline;
+import static jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode.isRepeatInfinite;
+
+import jdk.nashorn.internal.runtime.regexp.joni.ast.QuantifierNode;
+import jdk.nashorn.internal.runtime.regexp.joni.constants.AnchorType;
+import jdk.nashorn.internal.runtime.regexp.joni.constants.MetaChar;
+import jdk.nashorn.internal.runtime.regexp.joni.constants.TokenType;
+import jdk.nashorn.internal.runtime.regexp.joni.encoding.CharacterType;
+import jdk.nashorn.internal.runtime.regexp.joni.encoding.PosixBracket;
+import jdk.nashorn.internal.runtime.regexp.joni.encoding.Ptr;
+import jdk.nashorn.internal.runtime.regexp.joni.exception.ErrorMessages;
+import jdk.nashorn.internal.runtime.regexp.joni.exception.JOniException;
+
+class Lexer extends ScannerSupport {
+    protected final ScanEnvironment env;
+    protected final Syntax syntax;              // fast access to syntax
+    protected final Token token = new Token();  // current token
+
+    protected Lexer(ScanEnvironment env, char[] chars, int p, int end) {
+        super(chars, p, end);
+        this.env = env;
+        this.syntax = env.syntax;
+    }
+
+    /**
+     * @return 0: normal {n,m}, 2: fixed {n}
+     * !introduce returnCode here
+     */
+    private int fetchRangeQuantifier() {
+        mark();
+        boolean synAllow = syntax.allowInvalidInterval();
+
+        if (!left()) {
+            if (synAllow) {
+                return 1; /* "....{" : OK! */
+            } else {
+                newSyntaxException(ERR_END_PATTERN_AT_LEFT_BRACE);
+            }
+        }
+
+        if (!synAllow) {
+            c = peek();
+            if (c == ')' || c == '(' || c == '|') {
+                newSyntaxException(ERR_END_PATTERN_AT_LEFT_BRACE);
+            }
+        }
+
+        int low = scanUnsignedNumber();
+        if (low < 0) newSyntaxException(ErrorMessages.ERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE);
+        if (low > Config.MAX_REPEAT_NUM) newSyntaxException(ErrorMessages.ERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE);
+
+        boolean nonLow = false;
+        if (p == _p) { /* can't read low */
+            if (syntax.allowIntervalLowAbbrev()) {
+                low = 0;
+                nonLow = true;
+            } else {
+                return invalidRangeQuantifier(synAllow);
+            }
+        }
+
+        if (!left()) return invalidRangeQuantifier(synAllow);
+
+        fetch();
+        int up;
+        int ret = 0;
+        if (c == ',') {
+            int prev = p; // ??? last
+            up = scanUnsignedNumber();
+            if (up < 0) newValueException(ERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE);
+            if (up > Config.MAX_REPEAT_NUM) newValueException(ERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE);
+
+            if (p == prev) {
+                if (nonLow) return invalidRangeQuantifier(synAllow);
+                up = QuantifierNode.REPEAT_INFINITE; /* {n,} : {n,infinite} */
+            }
+        } else {
+            if (nonLow) return invalidRangeQuantifier(synAllow);
+            unfetch();
+            up = low; /* {n} : exact n times */
+            ret = 2; /* fixed */
+        }
+
+        if (!left()) return invalidRangeQuantifier(synAllow);
+        fetch();
+
+        if (syntax.opEscBraceInterval()) {
+            if (c != syntax.metaCharTable.esc) return invalidRangeQuantifier(synAllow);
+            fetch();
+        }
+
+        if (c != '}') return invalidRangeQuantifier(synAllow);
+
+        if (!isRepeatInfinite(up) && low > up) {
+            newValueException(ERR_UPPER_SMALLER_THAN_LOWER_IN_REPEAT_RANGE);
+        }
+
+        token.type = TokenType.INTERVAL;
+        token.setRepeatLower(low);
+        token.setRepeatUpper(up);
+
+        return ret; /* 0: normal {n,m}, 2: fixed {n} */
+    }
+
+    private int invalidRangeQuantifier(boolean synAllow) {
+        if (synAllow) {
+            restore();
+            return 1;
+        } else {
+            newSyntaxException(ERR_INVALID_REPEAT_RANGE_PATTERN);
+            return 0; // not reached
+        }
+    }
+
+    /* \M-, \C-, \c, or \... */
+    private int fetchEscapedValue() {
+        if (!left()) newSyntaxException(ERR_END_PATTERN_AT_ESCAPE);
+        fetch();
+
+        switch(c) {
+
+        case 'M':
+            if (syntax.op2EscCapitalMBarMeta()) {
+                if (!left()) newSyntaxException(ERR_END_PATTERN_AT_META);
+                fetch();
+                if (c != '-') newSyntaxException(ERR_META_CODE_SYNTAX);
+                if (!left()) newSyntaxException(ERR_END_PATTERN_AT_META);
+                fetch();
+                if (c == syntax.metaCharTable.esc) {
+                    c = fetchEscapedValue();
+                }
+                c = ((c & 0xff) | 0x80);
+            } else {
+                fetchEscapedValueBackSlash();
+            }
+            break;
+
+        case 'C':
+            if (syntax.op2EscCapitalCBarControl()) {
+                if (!left()) newSyntaxException(ERR_END_PATTERN_AT_CONTROL);
+                fetch();
+                if (c != '-') newSyntaxException(ERR_CONTROL_CODE_SYNTAX);
+                fetchEscapedValueControl();
+            } else {
+                fetchEscapedValueBackSlash();
+            }
+            break;
+
+        case 'c':
+            if (syntax.opEscCControl()) {
+                fetchEscapedValueControl();
+            }
+            /* fall through */
+
+        default:
+            fetchEscapedValueBackSlash();
+        } // switch
+
+        return c; // ???
+    }
+
+    private void fetchEscapedValueBackSlash() {
+        c = env.convertBackslashValue(c);
+    }
+
+    private void fetchEscapedValueControl() {
+        if (!left()) newSyntaxException(ERR_END_PATTERN_AT_CONTROL);
+        fetch();
+        if (c == '?') {
+            c = 0177;
+        } else {
+            if (c == syntax.metaCharTable.esc) {
+                c = fetchEscapedValue();
+            }
+            c &= 0x9f;
+        }
+    }
+
+    private int nameEndCodePoint(int start) {
+        switch(start) {
+        case '<':
+            return '>';
+        case '\'':
+            return '\'';
+        default:
+            return 0;
+        }
+    }
+
+    // USE_NAMED_GROUP && USE_BACKREF_AT_LEVEL
+    /*
+        \k<name+n>, \k<name-n>
+        \k<num+n>,  \k<num-n>
+        \k<-num+n>, \k<-num-n>
+     */
+
+    // value implicit (rnameEnd)
+    private boolean fetchNameWithLevel(int startCode, Ptr rbackNum, Ptr rlevel) {
+        int src = p;
+        boolean existLevel = false;
+        int isNum = 0;
+        int sign = 1;
+
+        int endCode = nameEndCodePoint(startCode);
+        int pnumHead = p;
+        int nameEnd = stop;
+
+        String err = null;
+        if (!left()) {
+            newValueException(ERR_EMPTY_GROUP_NAME);
+        } else {
+            fetch();
+            if (c == endCode) newValueException(ERR_EMPTY_GROUP_NAME);
+            if (Character.isDigit(c)) {
+                isNum = 1;
+            } else if (c == '-') {
+                isNum = 2;
+                sign = -1;
+                pnumHead = p;
+            } else if (!EncodingHelper.isWord(c)) {
+                err = ERR_INVALID_GROUP_NAME;
+            }
+        }
+
+        while (left()) {
+            nameEnd = p;
+            fetch();
+            if (c == endCode || c == ')' || c == '+' || c == '-') {
+                if (isNum == 2) err = ERR_INVALID_GROUP_NAME;
+                break;
+            }
+
+            if (isNum != 0) {
+                if (EncodingHelper.isDigit(c)) {
+                    isNum = 1;
+                } else {
+                    err = ERR_INVALID_GROUP_NAME;
+                    // isNum = 0;
+                }
+            } else if (!EncodingHelper.isWord(c)) {
+                err = ERR_INVALID_CHAR_IN_GROUP_NAME;
+            }
+        }
+
+        boolean isEndCode = false;
+        if (err == null && c != endCode) {
+            if (c == '+' || c == '-') {
+                int flag = c == '-' ? -1 : 1;
+
+                fetch();
+                if (!EncodingHelper.isDigit(c)) newValueException(ERR_INVALID_GROUP_NAME, src, stop);
+                unfetch();
+                int level = scanUnsignedNumber();
+                if (level < 0) newValueException(ERR_TOO_BIG_NUMBER);
+                rlevel.p = level * flag;
+                existLevel = true;
+
+                fetch();
+                isEndCode = c == endCode;
+            }
+
+            if (!isEndCode) {
+                err = ERR_INVALID_GROUP_NAME;
+                nameEnd = stop;
+            }
+        }
+
+        if (err == null) {
+            if (isNum != 0) {
+                mark();
+                p = pnumHead;
+                int backNum = scanUnsignedNumber();
+                restore();
+                if (backNum < 0) {
+                    newValueException(ERR_TOO_BIG_NUMBER);
+                } else if (backNum == 0) {
+                    newValueException(ERR_INVALID_GROUP_NAME, src, stop);
+                }
+                rbackNum.p = backNum * sign;
+            }
+            value = nameEnd;
+            return existLevel;
+        } else {
+            newValueException(ERR_INVALID_GROUP_NAME, src, nameEnd);
+            return false; // not reached
+        }
+    }
+
+    // USE_NAMED_GROUP
+    // ref: 0 -> define name    (don't allow number name)
+    //      1 -> reference name (allow number name)
+    private int fetchNameForNamedGroup(int startCode, boolean ref) {
+        int src = p;
+        value = 0;
+
+        int isNum = 0;
+        int sign = 1;
+
+        int endCode = nameEndCodePoint(startCode);
+        int pnumHead = p;
+        int nameEnd = stop;
+
+        String err = null;
+        if (!left()) {
+            newValueException(ERR_EMPTY_GROUP_NAME);
+        } else {
+            fetch();
+            if (c == endCode) newValueException(ERR_EMPTY_GROUP_NAME);
+            if (EncodingHelper.isDigit(c)) {
+                if (ref) {
+                    isNum = 1;
+                } else {
+                    err = ERR_INVALID_GROUP_NAME;
+                    // isNum = 0;
+                }
+            } else if (c == '-') {
+                if (ref) {
+                    isNum = 2;
+                    sign = -1;
+                    pnumHead = p;
+                } else {
+                    err = ERR_INVALID_GROUP_NAME;
+                    // isNum = 0;
+                }
+            } else if (!EncodingHelper.isWord(c)) {
+                err = ERR_INVALID_CHAR_IN_GROUP_NAME;
+            }
+        }
+
+        if (err == null) {
+            while (left()) {
+                nameEnd = p;
+                fetch();
+                if (c == endCode || c == ')') {
+                    if (isNum == 2) err = ERR_INVALID_GROUP_NAME;
+                    break;
+                }
+
+                if (isNum != 0) {
+                    if (EncodingHelper.isDigit(c)) {
+                        isNum = 1;
+                    } else {
+                        if (!EncodingHelper.isWord(c)) {
+                            err = ERR_INVALID_CHAR_IN_GROUP_NAME;
+                        } else {
+                            err = ERR_INVALID_GROUP_NAME;
+                        }
+                        // isNum = 0;
+                    }
+                } else {
+                    if (!EncodingHelper.isWord(c)) {
+                        err = ERR_INVALID_CHAR_IN_GROUP_NAME;
+                    }
+                }
+            }
+
+            if (c != endCode) {
+                err = ERR_INVALID_GROUP_NAME;
+                nameEnd = stop;
+            }
+
+            int backNum = 0;
+            if (isNum != 0) {
+                mark();
+                p = pnumHead;
+                backNum = scanUnsignedNumber();
+                restore();
+                if (backNum < 0) {
+                    newValueException(ERR_TOO_BIG_NUMBER);
+                } else if (backNum == 0) {
+                    newValueException(ERR_INVALID_GROUP_NAME, src, nameEnd);
+                }
+                backNum *= sign;
+            }
+            value = nameEnd;
+            return backNum;
+        } else {
+            while (left()) {
+                nameEnd = p;
+                fetch();
+                if (c == endCode || c == ')') break;
+            }
+            if (!left()) nameEnd = stop;
+            newValueException(err, src, nameEnd);
+            return 0; // not reached
+        }
+    }
+
+    // #else USE_NAMED_GROUP
+    // make it return nameEnd!
+    private final int fetchNameForNoNamedGroup(int startCode, boolean ref) {
+        int src = p;
+        value = 0;
+
+        int isNum = 0;
+        int sign = 1;
+
+        int endCode = nameEndCodePoint(startCode);
+        int pnumHead = p;
+        int nameEnd = stop;
+
+        String err = null;
+        if (!left()) {
+            newValueException(ERR_EMPTY_GROUP_NAME);
+        } else {
+            fetch();
+            if (c == endCode) newValueException(ERR_EMPTY_GROUP_NAME);
+
+            if (EncodingHelper.isDigit(c)) {
+                isNum = 1;
+            } else if (c == '-') {
+                isNum = 2;
+                sign = -1;
+                pnumHead = p;
+            } else {
+                err = ERR_INVALID_CHAR_IN_GROUP_NAME;
+            }
+        }
+
+        while(left()) {
+            nameEnd = p;
+
+            fetch();
+            if (c == endCode || c == ')') break;
+            if (!EncodingHelper.isDigit(c)) err = ERR_INVALID_CHAR_IN_GROUP_NAME;
+        }
+
+        if (err == null && c != endCode) {
+            err = ERR_INVALID_GROUP_NAME;
+            nameEnd = stop;
+        }
+
+        if (err == null) {
+            mark();
+            p = pnumHead;
+            int backNum = scanUnsignedNumber();
+            restore();
+            if (backNum < 0) {
+                newValueException(ERR_TOO_BIG_NUMBER);
+            } else if (backNum == 0){
+                newValueException(ERR_INVALID_GROUP_NAME, src, nameEnd);
+            }
+            backNum *= sign;
+
+            value = nameEnd;
+            return backNum;
+        } else {
+            newValueException(err, src, nameEnd);
+            return 0; // not reached
+        }
+    }
+
+    protected final int fetchName(int startCode, boolean ref) {
+        if (Config.USE_NAMED_GROUP) {
+            return fetchNameForNamedGroup(startCode, ref);
+        } else {
+            return fetchNameForNoNamedGroup(startCode, ref);
+        }
+    }
+
+    private boolean strExistCheckWithEsc(int[]s, int n, int bad) {
+        int p = this.p;
+        int to = this.stop;
+
+        boolean inEsc = false;
+        int i=0;
+        while(p < to) {
+            if (inEsc) {
+                inEsc = false;
+                p ++;
+            } else {
+                int x = chars[p];
+                int q = p + 1;
+                if (x == s[0]) {
+                    for (i=1; i<n && q < to; i++) {
+                        x = chars[q];
+                        if (x != s[i]) break;
+                        q++;
+                    }
+                    if (i >= n) return true;
+                    p++;
+                } else {
+                    x = chars[p];
+                    if (x == bad) return false;
+                    else if (x == syntax.metaCharTable.esc) inEsc = true;
+                    p = q;
+                }
+            }
+        }
+        return false;
+    }
+
+    private static final int send[] = new int[]{':', ']'};
+
+    private void fetchTokenInCCFor_charType(boolean flag, int type) {
+        token.type = TokenType.CHAR_TYPE;
+        token.setPropCType(type);
+        token.setPropNot(flag);
+    }
+
+    private void fetchTokenInCCFor_p() {
+        int c2 = peek(); // !!! migrate to peekIs
+        if (c2 == '{' && syntax.op2EscPBraceCharProperty()) {
+            inc();
+            token.type = TokenType.CHAR_PROPERTY;
+            token.setPropNot(c == 'P');
+
+            if (syntax.op2EscPBraceCircumflexNot()) {
+                c2 = fetchTo();
+                if (c2 == '^') {
+                    token.setPropNot(!token.getPropNot());
+                } else {
+                    unfetch();
+                }
+            }
+        } else {
+            syntaxWarn(Warnings.INVALID_UNICODE_PROPERTY, (char)c);
+        }
+    }
+
+    private void fetchTokenInCCFor_x() {
+        if (!left()) return;
+        int last = p;
+
+        if (peekIs('{') && syntax.opEscXBraceHex8()) {
+            inc();
+            int num = scanUnsignedHexadecimalNumber(8);
+            if (num < 0) newValueException(ERR_TOO_BIG_WIDE_CHAR_VALUE);
+            if (left()) {
+                int c2 = peek();
+                if (EncodingHelper.isXDigit(c2)) newValueException(ERR_TOO_LONG_WIDE_CHAR_VALUE);
+            }
+
+            if (p > last + 1 && left() && peekIs('}')) {
+                inc();
+                token.type = TokenType.CODE_POINT;
+                token.base = 16;
+                token.setCode(num);
+            } else {
+                /* can't read nothing or invalid format */
+                p = last;
+            }
+        } else if (syntax.opEscXHex2()) {
+            int num = scanUnsignedHexadecimalNumber(2);
+            if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
+            if (p == last) { /* can't read nothing. */
+                num = 0; /* but, it's not error */
+            }
+            token.type = TokenType.RAW_BYTE;
+            token.base = 16;
+            token.setC(num);
+        }
+    }
+
+    private void fetchTokenInCCFor_u() {
+        if (!left()) return;
+        int last = p;
+
+        if (syntax.op2EscUHex4()) {
+            int num = scanUnsignedHexadecimalNumber(4);
+            if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
+            if (p == last) {  /* can't read nothing. */
+                num = 0; /* but, it's not error */
+            }
+            token.type = TokenType.CODE_POINT;
+            token.base = 16;
+            token.setCode(num);
+        }
+    }
+
+    private void fetchTokenInCCFor_digit() {
+        if (syntax.opEscOctal3()) {
+            unfetch();
+            int last = p;
+            int num = scanUnsignedOctalNumber(3);
+            if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
+            if (p == last) {  /* can't read nothing. */
+                num = 0; /* but, it's not error */
+            }
+            token.type = TokenType.RAW_BYTE;
+            token.base = 8;
+            token.setC(num);
+        }
+    }
+
+    private void fetchTokenInCCFor_posixBracket() {
+        if (syntax.opPosixBracket() && peekIs(':')) {
+            token.backP = p; /* point at '[' is readed */
+            inc();
+            if (strExistCheckWithEsc(send, send.length, ']')) {
+                token.type = TokenType.POSIX_BRACKET_OPEN;
+            } else {
+                unfetch();
+                // remove duplication, goto cc_in_cc;
+                if (syntax.op2CClassSetOp()) {
+                    token.type = TokenType.CC_CC_OPEN;
+                } else {
+                    env.ccEscWarn("[");
+                }
+            }
+        } else { // cc_in_cc:
+            if (syntax.op2CClassSetOp()) {
+                token.type = TokenType.CC_CC_OPEN;
+            } else {
+                env.ccEscWarn("[");
+            }
+        }
+    }
+
+    private void fetchTokenInCCFor_and() {
+        if (syntax.op2CClassSetOp() && left() && peekIs('&')) {
+            inc();
+            token.type = TokenType.CC_AND;
+        }
+    }
+
+    protected final TokenType fetchTokenInCC() {
+        if (!left()) {
+            token.type = TokenType.EOT;
+            return token.type;
+        }
+
+        fetch();
+        token.type = TokenType.CHAR;
+        token.base = 0;
+        token.setC(c);
+        token.escaped = false;
+
+        if (c == ']') {
+            token.type = TokenType.CC_CLOSE;
+        } else if (c == '-') {
+            token.type = TokenType.CC_RANGE;
+        } else if (c == syntax.metaCharTable.esc) {
+            if (!syntax.backSlashEscapeInCC()) return token.type;
+            if (!left()) newSyntaxException(ERR_END_PATTERN_AT_ESCAPE);
+            fetch();
+            token.escaped = true;
+            token.setC(c);
+
+            switch (c) {
+            case 'w':
+                fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.W : CharacterType.WORD);
+                break;
+            case 'W':
+                fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.W : CharacterType.WORD);
+                break;
+            case 'd':
+                fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.D : CharacterType.DIGIT);
+                break;
+            case 'D':
+                fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.D : CharacterType.DIGIT);
+                break;
+            case 's':
+                fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
+                break;
+            case 'S':
+                fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
+                break;
+            case 'h':
+                if (syntax.op2EscHXDigit()) fetchTokenInCCFor_charType(false, CharacterType.XDIGIT);
+                break;
+            case 'H':
+                if (syntax.op2EscHXDigit()) fetchTokenInCCFor_charType(true, CharacterType.XDIGIT);
+                break;
+            case 'p':
+            case 'P':
+                fetchTokenInCCFor_p();
+                break;
+            case 'x':
+                fetchTokenInCCFor_x();
+                break;
+            case 'u':
+                fetchTokenInCCFor_u();
+                break;
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+                fetchTokenInCCFor_digit();
+                break;
+
+            default:
+                unfetch();
+                int num = fetchEscapedValue();
+                if (token.getC() != num) {
+                    token.setCode(num);
+                    token.type = TokenType.CODE_POINT;
+                }
+                break;
+            } // switch
+
+        } else if (c == '[') {
+            fetchTokenInCCFor_posixBracket();
+        } else if (c == '&') {
+            fetchTokenInCCFor_and();
+        }
+        return token.type;
+    }
+
+    protected final int backrefRelToAbs(int relNo) {
+        return env.numMem + 1 + relNo;
+    }
+
+    private void fetchTokenFor_repeat(int lower, int upper) {
+        token.type = TokenType.OP_REPEAT;
+        token.setRepeatLower(lower);
+        token.setRepeatUpper(upper);
+        greedyCheck();
+    }
+
+    private void fetchTokenFor_openBrace() {
+        switch (fetchRangeQuantifier()) {
+        case 0:
+            greedyCheck();
+            break;
+        case 2:
+            if (syntax.fixedIntervalIsGreedyOnly()) {
+                possessiveCheck();
+            } else {
+                greedyCheck();
+            }
+            break;
+        default: /* 1 : normal char */
+        } // inner switch
+    }
+
+    private void fetchTokenFor_anchor(int subType) {
+        token.type = TokenType.ANCHOR;
+        token.setAnchor(subType);
+    }
+
+    private void fetchTokenFor_xBrace() {
+        if (!left()) return;
+
+        int last = p;
+        if (peekIs('{') && syntax.opEscXBraceHex8()) {
+            inc();
+            int num = scanUnsignedHexadecimalNumber(8);
+            if (num < 0) newValueException(ERR_TOO_BIG_WIDE_CHAR_VALUE);
+            if (left()) {
+                if (EncodingHelper.isXDigit(peek())) newValueException(ERR_TOO_LONG_WIDE_CHAR_VALUE);
+            }
+
+            if (p > last + 1 && left() && peekIs('}')) {
+                inc();
+                token.type = TokenType.CODE_POINT;
+                token.setCode(num);
+            } else {
+                /* can't read nothing or invalid format */
+                p = last;
+            }
+        } else if (syntax.opEscXHex2()) {
+            int num = scanUnsignedHexadecimalNumber(2);
+            if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
+            if (p == last) { /* can't read nothing. */
+                num = 0; /* but, it's not error */
+            }
+            token.type = TokenType.RAW_BYTE;
+            token.base = 16;
+            token.setC(num);
+        }
+    }
+
+    private void fetchTokenFor_uHex() {
+        if (!left()) return;
+        int last = p;
+
+        if (syntax.op2EscUHex4()) {
+            int num = scanUnsignedHexadecimalNumber(4);
+            if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
+            if (p == last) { /* can't read nothing. */
+                num = 0; /* but, it's not error */
+            }
+            token.type = TokenType.CODE_POINT;
+            token.base = 16;
+            token.setCode(num);
+        }
+    }
+
+    private void fetchTokenFor_digit() {
+        unfetch();
+        int last = p;
+        int num = scanUnsignedNumber();
+        if (num < 0 || num > Config.MAX_BACKREF_NUM) { // goto skip_backref
+        } else if (syntax.opDecimalBackref() && (num <= env.numMem || num <= 9)) { /* This spec. from GNU regex */
+            if (syntax.strictCheckBackref()) {
+                if (num > env.numMem || env.memNodes == null || env.memNodes[num] == null) newValueException(ERR_INVALID_BACKREF);
+            }
+            token.type = TokenType.BACKREF;
+            token.setBackrefNum(1);
+            token.setBackrefRef1(num);
+            token.setBackrefByName(false);
+            if (Config.USE_BACKREF_WITH_LEVEL) token.setBackrefExistLevel(false);
+            return;
+        }
+
+        if (c == '8' || c == '9') { /* normal char */ // skip_backref:
+            p = last;
+            inc();
+            return;
+        }
+        p = last;
+
+        fetchTokenFor_zero(); /* fall through */
+    }
+
+    private void fetchTokenFor_zero() {
+        if (syntax.opEscOctal3()) {
+            int last = p;
+            int num = scanUnsignedOctalNumber(c == '0' ? 2 : 3);
+            if (num < 0) newValueException(ERR_TOO_BIG_NUMBER);
+            if (p == last) { /* can't read nothing. */
+                num = 0; /* but, it's not error */
+            }
+            token.type = TokenType.RAW_BYTE;
+            token.base = 8;
+            token.setC(num);
+        } else if (c != '0') {
+            inc();
+        }
+    }
+
+    private void fetchTokenFor_namedBackref() {
+        if (syntax.op2EscKNamedBackref()) {
+            if (left()) {
+                fetch();
+                if (c =='<' || c == '\'') {
+                    int last = p;
+                    int backNum;
+                    if (Config.USE_BACKREF_WITH_LEVEL) {
+                        Ptr rbackNum = new Ptr();
+                        Ptr rlevel = new Ptr();
+                        token.setBackrefExistLevel(fetchNameWithLevel(c, rbackNum, rlevel));
+                        token.setBackrefLevel(rlevel.p);
+                        backNum = rbackNum.p;
+                    } else {
+                        backNum = fetchName(c, true);
+                    } // USE_BACKREF_AT_LEVEL
+                    int nameEnd = value; // set by fetchNameWithLevel/fetchName
+
+                    if (backNum != 0) {
+                        if (backNum < 0) {
+                            backNum = backrefRelToAbs(backNum);
+                            if (backNum <= 0) newValueException(ERR_INVALID_BACKREF);
+                        }
+
+                        if (syntax.strictCheckBackref() && (backNum > env.numMem || env.memNodes == null)) {
+                            newValueException(ERR_INVALID_BACKREF);
+                        }
+                        token.type = TokenType.BACKREF;
+                        token.setBackrefByName(false);
+                        token.setBackrefNum(1);
+                        token.setBackrefRef1(backNum);
+                    } else {
+                        NameEntry e = env.reg.nameToGroupNumbers(chars, last, nameEnd);
+                        if (e == null) newValueException(ERR_UNDEFINED_NAME_REFERENCE, last, nameEnd);
+
+                        if (syntax.strictCheckBackref()) {
+                            if (e.backNum == 1) {
+                                if (e.backRef1 > env.numMem ||
+                                    env.memNodes == null ||
+                                    env.memNodes[e.backRef1] == null) newValueException(ERR_INVALID_BACKREF);
+                            } else {
+                                for (int i=0; i<e.backNum; i++) {
+                                    if (e.backRefs[i] > env.numMem ||
+                                        env.memNodes == null ||
+                                        env.memNodes[e.backRefs[i]] == null) newValueException(ERR_INVALID_BACKREF);
+                                }
+                            }
+                        }
+
+                        token.type = TokenType.BACKREF;
+                        token.setBackrefByName(true);
+
+                        if (e.backNum == 1) {
+                            token.setBackrefNum(1);
+                            token.setBackrefRef1(e.backRef1);
+                        } else {
+                            token.setBackrefNum(e.backNum);
+                            token.setBackrefRefs(e.backRefs);
+                        }
+                    }
+                } else {
+                    unfetch();
+                    syntaxWarn(Warnings.INVALID_BACKREFERENCE);
+                }
+            } else {
+                syntaxWarn(Warnings.INVALID_BACKREFERENCE);
+            }
+        }
+    }
+
+    private void fetchTokenFor_subexpCall() {
+        if (syntax.op2EscGSubexpCall()) {
+            if (left()) {
+                fetch();
+                if (c == '<' || c == '\'') {
+                    int last = p;
+                    int gNum = fetchName(c, true);
+                    int nameEnd = value;
+                    token.type = TokenType.CALL;
+                    token.setCallNameP(last);
+                    token.setCallNameEnd(nameEnd);
+                    token.setCallGNum(gNum);
+                } else {
+                    unfetch();
+                    syntaxWarn(Warnings.INVALID_SUBEXP_CALL);
+                }
+            } else {
+                syntaxWarn(Warnings.INVALID_SUBEXP_CALL);
+            }
+        }
+    }
+
+    private void fetchTokenFor_charProperty() {
+        if (peekIs('{') && syntax.op2EscPBraceCharProperty()) {
+            inc();
+            token.type = TokenType.CHAR_PROPERTY;
+            token.setPropNot(c == 'P');
+
+            if (syntax.op2EscPBraceCircumflexNot()) {
+                fetch();
+                if (c == '^') {
+                    token.setPropNot(!token.getPropNot());
+                } else {
+                    unfetch();
+                }
+            }
+        } else {
+            syntaxWarn(Warnings.INVALID_UNICODE_PROPERTY, (char)c);
+        }
+    }
+
+    private void fetchTokenFor_metaChars() {
+        if (c == syntax.metaCharTable.anyChar) {
+            token.type = TokenType.ANYCHAR;
+        } else if (c == syntax.metaCharTable.anyTime) {
+            fetchTokenFor_repeat(0, QuantifierNode.REPEAT_INFINITE);
+        }  else if (c == syntax.metaCharTable.zeroOrOneTime) {
+            fetchTokenFor_repeat(0, 1);
+        } else if (c == syntax.metaCharTable.oneOrMoreTime) {
+            fetchTokenFor_repeat(1, QuantifierNode.REPEAT_INFINITE);
+        } else if (c == syntax.metaCharTable.anyCharAnyTime) {
+            token.type = TokenType.ANYCHAR_ANYTIME;
+            // goto out
+        }
+    }
+
+    protected final TokenType fetchToken() {
+        // mark(); // out
+        start:
+        while(true) {
+            if (!left()) {
+                token.type = TokenType.EOT;
+                return token.type;
+            }
+
+            token.type = TokenType.STRING;
+            token.base = 0;
+            token.backP = p;
+
+            fetch();
+
+            if (c == syntax.metaCharTable.esc && !syntax.op2IneffectiveEscape()) { // IS_MC_ESC_CODE(code, syn)
+                if (!left()) newSyntaxException(ERR_END_PATTERN_AT_ESCAPE);
+
+                token.backP = p;
+                fetch();
+
+                token.setC(c);
+                token.escaped = true;
+                switch(c) {
+
+                case '*':
+                    if (syntax.opEscAsteriskZeroInf()) fetchTokenFor_repeat(0, QuantifierNode.REPEAT_INFINITE);
+                    break;
+                case '+':
+                    if (syntax.opEscPlusOneInf()) fetchTokenFor_repeat(1, QuantifierNode.REPEAT_INFINITE);
+                    break;
+                case '?':
+                    if (syntax.opEscQMarkZeroOne()) fetchTokenFor_repeat(0, 1);
+                    break;
+                case '{':
+                    if (syntax.opEscBraceInterval()) fetchTokenFor_openBrace();
+                    break;
+                case '|':
+                    if (syntax.opEscVBarAlt()) token.type = TokenType.ALT;
+                    break;
+                case '(':
+                    if (syntax.opEscLParenSubexp()) token.type = TokenType.SUBEXP_OPEN;
+                    break;
+                case ')':
+                    if (syntax.opEscLParenSubexp()) token.type = TokenType.SUBEXP_CLOSE;
+                    break;
+                case 'w':
+                    if (syntax.opEscWWord()) fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.W : CharacterType.WORD);
+                    break;
+                case 'W':
+                    if (syntax.opEscWWord()) fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.W : CharacterType.WORD);
+                    break;
+                case 'b':
+                    if (syntax.opEscBWordBound()) fetchTokenFor_anchor(AnchorType.WORD_BOUND);
+                    break;
+                case 'B':
+                    if (syntax.opEscBWordBound()) fetchTokenFor_anchor(AnchorType.NOT_WORD_BOUND);
+                    break;
+                case '<':
+                    if (Config.USE_WORD_BEGIN_END && syntax.opEscLtGtWordBeginEnd()) fetchTokenFor_anchor(AnchorType.WORD_BEGIN);
+                    break;
+                case '>':
+                    if (Config.USE_WORD_BEGIN_END && syntax.opEscLtGtWordBeginEnd()) fetchTokenFor_anchor(AnchorType.WORD_END);
+                    break;
+                case 's':
+                    if (syntax.opEscSWhiteSpace()) fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
+                    break;
+                case 'S':
+                    if (syntax.opEscSWhiteSpace()) fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.S : CharacterType.SPACE);
+                    break;
+                case 'd':
+                    if (syntax.opEscDDigit()) fetchTokenInCCFor_charType(false, Config.NON_UNICODE_SDW ? CharacterType.D : CharacterType.DIGIT);
+                    break;
+                case 'D':
+                    if (syntax.opEscDDigit()) fetchTokenInCCFor_charType(true, Config.NON_UNICODE_SDW ? CharacterType.D : CharacterType.DIGIT);
+                    break;
+                case 'h':
+                    if (syntax.op2EscHXDigit()) fetchTokenInCCFor_charType(false, CharacterType.XDIGIT);
+                    break;
+                case 'H':
+                    if (syntax.op2EscHXDigit()) fetchTokenInCCFor_charType(true, CharacterType.XDIGIT);
+                    break;
+                case 'A':
+                    if (syntax.opEscAZBufAnchor()) fetchTokenFor_anchor(AnchorType.BEGIN_BUF);
+                    break;
+                case 'Z':
+                    if (syntax.opEscAZBufAnchor()) fetchTokenFor_anchor(AnchorType.SEMI_END_BUF);
+                    break;
+                case 'z':
+                    if (syntax.opEscAZBufAnchor()) fetchTokenFor_anchor(AnchorType.END_BUF);
+                    break;
+                case 'G':
+                    if (syntax.opEscCapitalGBeginAnchor()) fetchTokenFor_anchor(AnchorType.BEGIN_POSITION);
+                    break;
+                case '`':
+                    if (syntax.op2EscGnuBufAnchor()) fetchTokenFor_anchor(AnchorType.BEGIN_BUF);
+                    break;
+                case '\'':
+                    if (syntax.op2EscGnuBufAnchor()) fetchTokenFor_anchor(AnchorType.END_BUF);
+                    break;
+                case 'x':
+                    fetchTokenFor_xBrace();
+                    break;
+                case 'u':
+                    fetchTokenFor_uHex();
+                    break;
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                case '8':
+                case '9':
+                    fetchTokenFor_digit();
+                    break;
+                case '0':
+                    fetchTokenFor_zero();
+                    break;
+                case 'k':
+                    if (Config.USE_NAMED_GROUP) fetchTokenFor_namedBackref();
+                    break;
+                case 'g':
+                    if (Config.USE_SUBEXP_CALL) fetchTokenFor_subexpCall();
+                    break;
+                case 'Q':
+                    if (syntax.op2EscCapitalQQuote()) token.type = TokenType.QUOTE_OPEN;
+                    break;
+                case 'p':
+                case 'P':
+                    fetchTokenFor_charProperty();
+                    break;
+
+                default:
+                    unfetch();
+                    int num = fetchEscapedValue();
+
+                    /* set_raw: */
+                    if (token.getC() != num) {
+                        token.type = TokenType.CODE_POINT;
+                        token.setCode(num);
+                    } else { /* string */
+                        p = token.backP + 1;
+                    }
+                    break;
+
+                } // switch (c)
+
+            } else {
+                token.setC(c);
+                token.escaped = false;
+
+                if (Config.USE_VARIABLE_META_CHARS && (c != MetaChar.INEFFECTIVE_META_CHAR && syntax.opVariableMetaCharacters())) {
+                    fetchTokenFor_metaChars();
+                    break;
+                }
+
+                {
+                    switch(c) {
+                    case '.':
+                        if (syntax.opDotAnyChar()) token.type = TokenType.ANYCHAR;
+                        break;
+                    case '*':
+                        if (syntax.opAsteriskZeroInf()) fetchTokenFor_repeat(0, QuantifierNode.REPEAT_INFINITE);
+                        break;
+                    case '+':
+                        if (syntax.opPlusOneInf()) fetchTokenFor_repeat(1, QuantifierNode.REPEAT_INFINITE);
+                        break;
+                    case '?':
+                        if (syntax.opQMarkZeroOne()) fetchTokenFor_repeat(0, 1);
+                        break;
+                    case '{':
+                        if (syntax.opBraceInterval()) fetchTokenFor_openBrace();
+                        break;
+                    case '|':
+                        if (syntax.opVBarAlt()) token.type = TokenType.ALT;
+                        break;
+
+                    case '(':
+                        if (peekIs('?') && syntax.op2QMarkGroupEffect()) {
+                            inc();
+                            if (peekIs('#')) {
+                                fetch();
+                                while (true) {
+                                    if (!left()) newSyntaxException(ERR_END_PATTERN_IN_GROUP);
+                                    fetch();
+                                    if (c == syntax.metaCharTable.esc) {
+                                        if (left()) fetch();
+                                    } else {
+                                        if (c == ')') break;
+                                    }
+                                }
+                                continue start; // goto start
+                            }
+                            unfetch();
+                        }
+
+                        if (syntax.opLParenSubexp()) token.type = TokenType.SUBEXP_OPEN;
+                        break;
+                    case ')':
+                        if (syntax.opLParenSubexp()) token.type = TokenType.SUBEXP_CLOSE;
+                        break;
+                    case '^':
+                        if (syntax.opLineAnchor()) fetchTokenFor_anchor(isSingleline(env.option) ? AnchorType.BEGIN_BUF : AnchorType.BEGIN_LINE);
+                        break;
+                    case '$':
+                        if (syntax.opLineAnchor()) fetchTokenFor_anchor(isSingleline(env.option) ? AnchorType.SEMI_END_BUF : AnchorType.END_LINE);
+                        break;
+                    case '[':
+                        if (syntax.opBracketCC()) token.type = TokenType.CC_CC_OPEN;
+                        break;
+                    case ']':
+                        //if (*src > env->pattern)   /* /].../ is allowed. */
+                        //CLOSE_BRACKET_WITHOUT_ESC_WARN(env, (UChar* )"]");
+                        break;
+                    case '#':
+                        if (Option.isExtend(env.option)) {
+                            while (left()) {
+                                fetch();
+                                if (EncodingHelper.isNewLine(c)) break;
+                            }
+                            continue start; // goto start
+                        }
+                        break;
+
+                    case ' ':
+                    case '\t':
+                    case '\n':
+                    case '\r':
+                    case '\f':
+                        if (Option.isExtend(env.option)) continue start; // goto start
+                        break;
+
+                    default: // string
+                        break;
+
+                    } // switch
+                }
+            }
+
+            break;
+        } // while
+        return token.type;
+    }
+
+    private void greedyCheck() {
+        if (left() && peekIs('?') && syntax.opQMarkNonGreedy()) {
+
+            fetch();
+
+            token.setRepeatGreedy(false);
+            token.setRepeatPossessive(false);
+        } else {
+            possessiveCheck();
+        }
+    }
+
+    private void possessiveCheck() {
+        if (left() && peekIs('+') &&
+            (syntax.op2PlusPossessiveRepeat() && token.type != TokenType.INTERVAL ||
+             syntax.op2PlusPossessiveInterval() && token.type == TokenType.INTERVAL)) {
+
+            fetch();
+
+            token.setRepeatGreedy(true);
+            token.setRepeatPossessive(true);
+        } else {
+            token.setRepeatGreedy(true);
+            token.setRepeatPossessive(false);
+        }
+    }
+
+    protected final int fetchCharPropertyToCType() {
+        mark();
+
+        while (left()) {
+            int last = p;
+            fetch();
+            if (c == '}') {
+                String name = new String(chars, _p, last - _p);
+                return PosixBracket.propertyNameToCType(name);
+            } else if (c == '(' || c == ')' || c == '{' || c == '|') {
+                String name = new String(chars, _p, last - _p);
+                throw new JOniException(ERR_INVALID_CHAR_PROPERTY_NAME.replaceAll("%n", name));
+            }
+        }
+        newInternalException(ERR_PARSER_BUG);
+        return 0; // not reached
+    }
+
+    protected final void syntaxWarn(String message, char c) {
+        syntaxWarn(message.replace("<%n>", Character.toString(c)));
+    }
+
+    protected final void syntaxWarn(String message) {
+        if (Config.USE_WARN) {
+            env.reg.warnings.warn(message + ": /" + new String(chars, getBegin(), getEnd()) + "/");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/Matcher.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,556 @@
+/*
+ * 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.Option.isFindLongest;
+
+import jdk.nashorn.internal.runtime.regexp.joni.constants.AnchorType;
+import jdk.nashorn.internal.runtime.regexp.joni.encoding.IntHolder;
+
+public abstract class Matcher extends IntHolder {
+    protected final Regex regex;
+
+    protected final char[] chars;
+    protected final int str;
+    protected final int end;
+
+    protected int msaStart;
+    protected int msaOptions;
+    protected final Region msaRegion;
+    protected int msaBestLen;
+    protected int msaBestS;
+
+    protected int msaBegin;
+    protected int msaEnd;
+
+    public Matcher(Regex regex, char[] chars) {
+        this(regex, chars, 0, chars.length);
+    }
+
+    public Matcher(Regex regex, char[] chars, int p, int end) {
+        this.regex = regex;
+
+        this.chars = chars;
+        this.str = p;
+        this.end = end;
+
+        this.msaRegion = regex.numMem == 0 ? null : new Region(regex.numMem + 1);
+    }
+
+    // main matching method
+    protected abstract int matchAt(int range, int sstart, int sprev);
+
+    protected abstract void stateCheckBuffInit(int strLength, int offset, int stateNum);
+    protected abstract void stateCheckBuffClear();
+
+    public final Region getRegion() {
+        return msaRegion;
+    }
+
+    public final Region getEagerRegion() {
+        return msaRegion != null ? msaRegion : new Region(msaBegin, msaEnd);
+    }
+
+    public final int getBegin() {
+        return msaBegin;
+    }
+
+    public final int getEnd() {
+        return msaEnd;
+    }
+
+    protected final void msaInit(int option, int start) {
+        msaOptions = option;
+        msaStart = start;
+        if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) msaBestLen = -1;
+    }
+
+    public final int match(int at, int range, int option) {
+        msaInit(option, at);
+
+        if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
+            int offset = at = str;
+            stateCheckBuffInit(end - str, offset, regex.numCombExpCheck); // move it to construction?
+        } // USE_COMBINATION_EXPLOSION_CHECK
+
+        int prev = EncodingHelper.prevCharHead(str, at);
+
+        if (Config.USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE) {
+            return matchAt(end /*range*/, at, prev);
+        } else {
+            return matchAt(range /*range*/, at, prev);
+        }
+    }
+
+    int low, high; // these are the return values
+    private boolean forwardSearchRange(char[] chars, int str, int end, int s, int range, IntHolder lowPrev) {
+        int pprev = -1;
+        int p = s;
+
+        if (Config.DEBUG_SEARCH) {
+            Config.log.println("forward_search_range: "+
+                                "str: " + str +
+                                ", end: " + end +
+                                ", s: " + s +
+                                ", range: " + range);
+        }
+
+        if (regex.dMin > 0) {
+            p += regex.dMin;
+        }
+
+        retry:while (true) {
+            p = regex.searchAlgorithm.search(regex, chars, p, end, range);
+
+            if (p != -1 && p < range) {
+                if (p - regex.dMin < s) {
+                    // retry_gate:
+                    pprev = p;
+                    p++;
+                    continue retry;
+                }
+
+                if (regex.subAnchor != 0) {
+                    switch (regex.subAnchor) {
+                    case AnchorType.BEGIN_LINE:
+                        if (p != str) {
+                            int prev = EncodingHelper.prevCharHead((pprev != -1) ? pprev : str, p);
+                            if (!EncodingHelper.isNewLine(chars, prev, end)) {
+                                // goto retry_gate;
+                                pprev = p;
+                                p++;
+                                continue retry;
+                            }
+                        }
+                        break;
+
+                    case AnchorType.END_LINE:
+                        if (p == end) {
+                            if (!Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
+                                int prev = EncodingHelper.prevCharHead((pprev != -1) ? pprev : str, p);
+                                if (prev != -1 && EncodingHelper.isNewLine(chars, prev, end)) {
+                                    // goto retry_gate;
+                                    pprev = p;
+                                    p++;
+                                    continue retry;
+                                }
+                            }
+                        } else if (!EncodingHelper.isNewLine(chars, p, end) && (!Config.USE_CRNL_AS_LINE_TERMINATOR || !EncodingHelper.isCrnl(chars, p, end))) {
+                            //if () break;
+                            // goto retry_gate;
+                            pprev = p;
+                            p++;
+                            continue retry;
+                        }
+                        break;
+                    } // switch
+                }
+
+                if (regex.dMax == 0) {
+                    low = p;
+                    if (lowPrev != null) { // ??? // remove null checks
+                        if (low > s) {
+                            lowPrev.value = EncodingHelper.prevCharHead(s, p);
+                        } else {
+                            lowPrev.value = EncodingHelper.prevCharHead((pprev != -1) ? pprev : str, p);
+                        }
+                    }
+                } else {
+                    if (regex.dMax != MinMaxLen.INFINITE_DISTANCE) {
+                        low = p - regex.dMax;
+
+                        if (low > s) {
+                            low = EncodingHelper.rightAdjustCharHeadWithPrev(low, lowPrev);
+                            if (lowPrev != null && lowPrev.value == -1) {
+                                lowPrev.value = EncodingHelper.prevCharHead((pprev != -1) ? pprev : s, low);
+                            }
+                        } else {
+                            if (lowPrev != null) {
+                                lowPrev.value = EncodingHelper.prevCharHead((pprev != -1) ? pprev : str, low);
+                            }
+                        }
+                    }
+                }
+                /* no needs to adjust *high, *high is used as range check only */
+                high = p - regex.dMin;
+
+                if (Config.DEBUG_SEARCH) {
+                    Config.log.println("forward_search_range success: "+
+                                        "low: " + (low - str) +
+                                        ", high: " + (high - str) +
+                                        ", dmin: " + regex.dMin +
+                                        ", dmax: " + regex.dMax);
+                }
+
+                return true;    /* success */
+            }
+
+            return false;   /* fail */
+        } //while
+    }
+
+    // low, high
+    private boolean backwardSearchRange(char[] chars, int str, int end, int s, int range, int adjrange) {
+        range += regex.dMin;
+        int p = s;
+
+        retry:while (true) {
+            p = regex.searchAlgorithm.searchBackward(regex, chars, range, adjrange, end, p, s, range);
+
+            if (p != -1) {
+                if (regex.subAnchor != 0) {
+                    switch (regex.subAnchor) {
+                    case AnchorType.BEGIN_LINE:
+                        if (p != str) {
+                            int prev = EncodingHelper.prevCharHead(str, p);
+                            if (!EncodingHelper.isNewLine(chars, prev, end)) {
+                                p = prev;
+                                continue retry;
+                            }
+                        }
+                        break;
+
+                    case AnchorType.END_LINE:
+                        if (p == end) {
+                            if (!Config.USE_NEWLINE_AT_END_OF_STRING_HAS_EMPTY_LINE) {
+                                int prev = EncodingHelper.prevCharHead(adjrange, p);
+                                if (prev == -1) return false;
+                                if (EncodingHelper.isNewLine(chars, prev, end)) {
+                                    p = prev;
+                                    continue retry;
+                                }
+                            }
+                        } else if (!EncodingHelper.isNewLine(chars, p, end) && (!Config.USE_CRNL_AS_LINE_TERMINATOR || !EncodingHelper.isCrnl(chars, p, end))) {
+                            p = EncodingHelper.prevCharHead(adjrange, p);
+                            if (p == -1) return false;
+                            continue retry;
+                        }
+                        break;
+                    } // switch
+                }
+
+                /* no needs to adjust *high, *high is used as range check only */
+                if (regex.dMax != MinMaxLen.INFINITE_DISTANCE) {
+                    low = p - regex.dMax;
+                    high = p - regex.dMin;
+                }
+
+                if (Config.DEBUG_SEARCH) {
+                    Config.log.println("backward_search_range: "+
+                                        "low: " + (low - str) +
+                                        ", high: " + (high - str));
+                }
+
+                return true;
+            }
+
+            if (Config.DEBUG_SEARCH) Config.log.println("backward_search_range: fail.");
+            return false;
+        } // while
+    }
+
+    // MATCH_AND_RETURN_CHECK
+    private boolean matchCheck(int upperRange, int s, int prev) {
+        if (Config.USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE) {
+            if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) {
+                //range = upperRange;
+                if (matchAt(upperRange, s, prev) != -1) {
+                    if (!isFindLongest(regex.options)) return true;
+                }
+            } else {
+                //range = upperRange;
+                if (matchAt(upperRange, s, prev) != -1) return true;
+            }
+        } else {
+            if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) {
+                if (matchAt(end, s, prev) != -1) {
+                    //range = upperRange;
+                    if (!isFindLongest(regex.options)) return true;
+                }
+            } else {
+                //range = upperRange;
+                if (matchAt(end, s, prev) != -1) return true;
+            }
+        }
+        return false;
+    }
+
+    public final int search(int start, int range, int option) {
+        int s, prev;
+        int origStart = start;
+        int origRange = range;
+
+        if (Config.DEBUG_SEARCH) {
+            Config.log.println("onig_search (entry point): "+
+                    "str: " + str +
+                    ", end: " + (end - str) +
+                    ", start: " + (start - str) +
+                    ", range " + (range - str));
+        }
+
+        if (start > end || start < str) return -1;
+
+        /* anchor optimize: resume search range */
+        if (regex.anchor != 0 && str < end) {
+            int minSemiEnd, maxSemiEnd;
+
+            if ((regex.anchor & AnchorType.BEGIN_POSITION) != 0) {
+                /* search start-position only */
+                // !begin_position:!
+                if (range > start) {
+                    range = start + 1;
+                } else {
+                    range = start;
+                }
+            } else if ((regex.anchor & AnchorType.BEGIN_BUF) != 0) {
+                /* search str-position only */
+                if (range > start) {
+                    if (start != str) return -1; // mismatch_no_msa;
+                    range = str + 1;
+                } else {
+                    if (range <= str) {
+                        start = str;
+                        range = str;
+                    } else {
+                        return -1; // mismatch_no_msa;
+                    }
+                }
+            } else if ((regex.anchor & AnchorType.END_BUF) != 0) {
+                minSemiEnd = maxSemiEnd = end;
+                // !end_buf:!
+                if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return -1; // mismatch_no_msa;
+            } else if ((regex.anchor & AnchorType.SEMI_END_BUF) != 0) {
+                int preEnd = EncodingHelper.stepBack(str, end, 1);
+                maxSemiEnd = end;
+                if (EncodingHelper.isNewLine(chars, preEnd, end)) {
+                    minSemiEnd = preEnd;
+                    if (Config.USE_CRNL_AS_LINE_TERMINATOR) {
+                        preEnd = EncodingHelper.stepBack(str, preEnd, 1);
+                        if (preEnd != -1 && EncodingHelper.isCrnl(chars, preEnd, end)) {
+                            minSemiEnd = preEnd;
+                        }
+                    }
+                    if (minSemiEnd > str && start <= minSemiEnd) {
+                        // !goto end_buf;!
+                        if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return -1; // mismatch_no_msa;
+                    }
+                } else {
+                    minSemiEnd = end;
+                    // !goto end_buf;!
+                    if (endBuf(start, range, minSemiEnd, maxSemiEnd)) return -1; // mismatch_no_msa;
+                }
+            } else if ((regex.anchor & AnchorType.ANYCHAR_STAR_ML) != 0) {
+                // goto !begin_position;!
+                if (range > start) {
+                    range = start + 1;
+                } else {
+                    range = start;
+                }
+            }
+
+        } else if (str == end) { /* empty string */
+            // empty address ?
+            if (Config.DEBUG_SEARCH) {
+                Config.log.println("onig_search: empty string.");
+            }
+
+            if (regex.thresholdLength == 0) {
+                s = start = str;
+                prev = -1;
+                msaInit(option, start);
+
+                if (Config.USE_COMBINATION_EXPLOSION_CHECK) stateCheckBuffClear();
+
+                if (matchCheck(end, s, prev)) return match(s);
+                return mismatch();
+            }
+            return -1; // goto mismatch_no_msa;
+        }
+
+        if (Config.DEBUG_SEARCH) {
+            Config.log.println("onig_search(apply anchor): " +
+                                "end: " + (end - str) +
+                                ", start " + (start - str) +
+                                ", range " + (range - str));
+        }
+
+        msaInit(option, origStart);
+        if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
+            int offset = Math.min(start, range) - str;
+            stateCheckBuffInit(end - str, offset, regex.numCombExpCheck);
+        }
+
+        s = start;
+        if (range > start) {    /* forward search */
+            if (s > str) {
+                prev = EncodingHelper.prevCharHead(str, s);
+            } else {
+                prev = 0; // -1
+            }
+
+            if (regex.searchAlgorithm != SearchAlgorithm.NONE) {
+                int schRange = range;
+                if (regex.dMax != 0) {
+                    if (regex.dMax == MinMaxLen.INFINITE_DISTANCE) {
+                        schRange = end;
+                    } else {
+                        schRange += regex.dMax;
+                        if (schRange > end) schRange = end;
+                    }
+                }
+                if ((end - start) < regex.thresholdLength) return mismatch();
+
+                if (regex.dMax != MinMaxLen.INFINITE_DISTANCE) {
+                    do {
+                        if (!forwardSearchRange(chars, str, end, s, schRange, this)) return mismatch(); // low, high, lowPrev
+                        if (s < low) {
+                            s = low;
+                            prev = value;
+                        }
+                        while (s <= high) {
+                            if (matchCheck(origRange, s, prev)) return match(s); // ???
+                            prev = s;
+                            s++;
+                        }
+                    } while (s < range);
+                    return mismatch();
+
+                } else { /* check only. */
+                    if (!forwardSearchRange(chars, str, end, s, schRange, null)) return mismatch();
+
+                    if ((regex.anchor & AnchorType.ANYCHAR_STAR) != 0) {
+                        do {
+                            if (matchCheck(origRange, s, prev)) return match(s);
+                            prev = s;
+                            s++;
+                        } while (s < range);
+                        return mismatch();
+                    }
+
+                }
+            }
+
+            do {
+                if (matchCheck(origRange, s, prev)) return match(s);
+                prev = s;
+                s++;
+            } while (s < range);
+
+            if (s == range) { /* because empty match with /$/. */
+                if (matchCheck(origRange, s, prev)) return match(s);
+            }
+        } else { /* backward search */
+            if (Config.USE_MATCH_RANGE_MUST_BE_INSIDE_OF_SPECIFIED_RANGE) {
+                if (origStart < end) {
+                    origStart++; // /* is upper range */
+                }
+            }
+
+            if (regex.searchAlgorithm != SearchAlgorithm.NONE) {
+                int adjrange;
+                if (range < end) {
+                    adjrange = range;
+                } else {
+                    adjrange = end;
+                }
+                if (regex.dMax != MinMaxLen.INFINITE_DISTANCE && (end - range) >= regex.thresholdLength) {
+                    do {
+                        int schStart = s + regex.dMax;
+                        if (schStart > end) schStart = end;
+                        if (!backwardSearchRange(chars, str, end, schStart, range, adjrange)) return mismatch(); // low, high
+                        if (s > high) s = high;
+                        while (s != -1 && s >= low) {
+                            prev = EncodingHelper.prevCharHead(str, s);
+                            if (matchCheck(origStart, s, prev)) return match(s);
+                            s = prev;
+                        }
+                    } while (s >= range);
+                    return mismatch();
+                } else { /* check only. */
+                    if ((end - range) < regex.thresholdLength) return mismatch();
+
+                    int schStart = s;
+                    if (regex.dMax != 0) {
+                        if (regex.dMax == MinMaxLen.INFINITE_DISTANCE) {
+                            schStart = end;
+                        } else {
+                            schStart += regex.dMax;
+                            if (schStart > end) {
+                                schStart = end;
+                            }
+                        }
+                    }
+                    if (!backwardSearchRange(chars, str, end, schStart, range, adjrange)) return mismatch();
+                }
+            }
+
+            do {
+                prev = EncodingHelper.prevCharHead(str, s);
+                if (matchCheck(origStart, s, prev)) return match(s);
+                s = prev;
+            } while (s >= range);
+
+        }
+        return mismatch();
+    }
+
+    private boolean endBuf(int start, int range, int minSemiEnd, int maxSemiEnd) {
+        if ((maxSemiEnd - str) < regex.anchorDmin) return true; // mismatch_no_msa;
+
+        if (range > start) {
+            if ((minSemiEnd - start) > regex.anchorDmax) {
+                start = minSemiEnd - regex.anchorDmax;
+                if (start >= end) {
+                    /* match with empty at end */
+                    start = EncodingHelper.prevCharHead(str, end);
+                }
+            }
+            if ((maxSemiEnd - (range - 1)) < regex.anchorDmin) {
+                range = maxSemiEnd - regex.anchorDmin + 1;
+            }
+            if (start >= range) return true; // mismatch_no_msa;
+        } else {
+            if ((minSemiEnd - range) > regex.anchorDmax) {
+                range = minSemiEnd - regex.anchorDmax;
+            }
+            if ((maxSemiEnd - start) < regex.anchorDmin) {
+                start = maxSemiEnd - regex.anchorDmin;
+            }
+            if (range > start) return true; // mismatch_no_msa;
+        }
+        return false;
+    }
+
+    private int match(int s) {
+        return s - str; // sstart ???
+    }
+
+    private int mismatch() {
+        if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) {
+            if (msaBestLen >= 0) {
+                int s = msaBestS;
+                return match(s);
+            }
+        }
+        // falls through finish:
+        return -1;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/MatcherFactory.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,31 @@
+/*
+ * 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;
+
+public abstract class MatcherFactory {
+    public abstract Matcher create(Regex regex, char[] chars, int p, int end);
+
+    static final MatcherFactory DEFAULT = new MatcherFactory() {
+        @Override
+        public Matcher create(Regex regex, char[] chars, int p, int end) {
+            return new ByteCodeMachine(regex, chars, p, end);
+        }
+    };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/MinMaxLen.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,139 @@
+/*
+ * 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;
+
+final class MinMaxLen {
+    int min; /* min byte length */
+    int max; /* max byte length */
+
+    MinMaxLen() {
+    }
+
+    MinMaxLen(int min, int max) {
+        this.min = min;
+        this.max = max;
+    }
+
+    /* 1000 / (min-max-dist + 1) */
+    private static final short distValues[] = {
+        1000,  500,  333,  250,  200,  167,  143,  125,  111,  100,
+          91,   83,   77,   71,   67,   63,   59,   56,   53,   50,
+          48,   45,   43,   42,   40,   38,   37,   36,   34,   33,
+          32,   31,   30,   29,   29,   28,   27,   26,   26,   25,
+          24,   24,   23,   23,   22,   22,   21,   21,   20,   20,
+          20,   19,   19,   19,   18,   18,   18,   17,   17,   17,
+          16,   16,   16,   16,   15,   15,   15,   15,   14,   14,
+          14,   14,   14,   14,   13,   13,   13,   13,   13,   13,
+          12,   12,   12,   12,   12,   12,   11,   11,   11,   11,
+          11,   11,   11,   11,   11,   10,   10,   10,   10,   10
+    };
+
+    int distanceValue() {
+        if (max == INFINITE_DISTANCE) return 0;
+        int d = max - min;
+        /* return dist_vals[d] * 16 / (mm->min + 12); */
+        return d < distValues.length ? distValues[d] : 1;
+    }
+
+    int compareDistanceValue(MinMaxLen other, int v1, int v2) {
+        if (v2 <= 0) return -1;
+        if (v1 <= 0) return 1;
+
+        v1 *= distanceValue();
+        v2 *= other.distanceValue();
+
+        if (v2 > v1) return 1;
+        if (v2 < v1) return -1;
+
+        if (other.min < min) return 1;
+        if (other.min > min) return -1;
+        return 0;
+    }
+
+    boolean equal(MinMaxLen other) {
+        return min == other.min && max == other.max;
+    }
+
+    void set(int min, int max) {
+        this.min = min;
+        this.max = max;
+    }
+
+    void clear() {
+        min = max = 0;
+    }
+
+    void copy(MinMaxLen other) {
+        min = other.min;
+        max = other.max;
+    }
+
+    void add(MinMaxLen other) {
+        min = distanceAdd(min, other.min);
+        max = distanceAdd(max, other.max);
+    }
+
+    void addLength(int len) {
+        min = distanceAdd(min, len);
+        max = distanceAdd(max, len);
+    }
+
+    void altMerge(MinMaxLen other) {
+        if (min > other.min) min = other.min;
+        if (max < other.max) max = other.max;
+    }
+
+    static final int INFINITE_DISTANCE = 0x7FFFFFFF;
+    static int distanceAdd(int d1, int d2) {
+        if (d1 == INFINITE_DISTANCE || d2 == INFINITE_DISTANCE) {
+            return INFINITE_DISTANCE;
+        } else {
+            if (d1 <= INFINITE_DISTANCE - d2) return d1 + d2;
+            else return INFINITE_DISTANCE;
+        }
+    }
+
+    static int distanceMultiply(int d, int m) {
+        if (m == 0) return 0;
+        if (d < INFINITE_DISTANCE / m) {
+            return d * m;
+        } else {
+            return INFINITE_DISTANCE;
+        }
+    }
+
+    static String distanceRangeToString(int a, int b) {
+        String s = "";
+        if (a == INFINITE_DISTANCE) {
+            s += "inf";
+        } else {
+            s += "(" + a + ")";
+        }
+
+        s += "-";
+
+        if (b == INFINITE_DISTANCE) {
+            s += "inf";
+        } else {
+            s += "(" + b + ")";
+        }
+        return s;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/NameEntry.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,97 @@
+/*
+ * 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;
+
+public final class NameEntry {
+    static final int INIT_NAME_BACKREFS_ALLOC_NUM = 8;
+
+    public final char[] name;
+    public final int nameP;
+    public final int nameEnd;
+
+    int backNum;
+    int backRef1;
+    int backRefs[];
+
+    public NameEntry(char[] chars, int p, int end) {
+        name = chars;
+        nameP = p;
+        nameEnd = end;
+    }
+
+    public int[] getBackRefs() {
+        switch (backNum) {
+        case 0:
+            return new int[]{};
+        case 1:
+            return new int[]{backRef1};
+        default:
+            int[]result = new int[backNum];
+            System.arraycopy(backRefs, 0, result, 0, backNum);
+            return result;
+        }
+    }
+
+    private void alloc() {
+        backRefs = new int[INIT_NAME_BACKREFS_ALLOC_NUM];
+    }
+
+    private void ensureSize() {
+        if (backNum > backRefs.length) {
+            int[]tmp = new int[backRefs.length << 1];
+            System.arraycopy(backRefs, 0, tmp, 0, backRefs.length);
+            backRefs = tmp;
+        }
+    }
+
+    public void addBackref(int backRef) {
+        backNum++;
+
+        switch (backNum) {
+            case 1:
+                backRef1 = backRef;
+                break;
+            case 2:
+                alloc();
+                backRefs[0] = backRef1;
+                backRefs[1] = backRef;
+                break;
+            default:
+                ensureSize();
+                backRefs[backNum - 1] = backRef;
+        }
+    }
+
+    public String toString() {
+        StringBuilder buff = new StringBuilder(new String(name, nameP, nameEnd - nameP) + " ");
+        if (backNum == 0) {
+            buff.append("-");
+        } else if (backNum == 1){
+            buff.append(backRef1);
+        } else {
+            for (int i=0; i<backNum; i++){
+                if (i > 0) buff.append(", ");
+                buff.append(backRefs[i]);
+            }
+        }
+        return buff.toString();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/NativeMachine.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+public abstract class NativeMachine extends Matcher {
+
+    protected NativeMachine(Regex regex, char[] chars, int p, int end) {
+        super(regex, chars, p, end);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/NodeOptInfo.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,125 @@
+/*
+ * 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;
+
+public final class NodeOptInfo {
+    final MinMaxLen length = new  MinMaxLen();
+    final OptAnchorInfo anchor = new OptAnchorInfo();
+    final OptExactInfo exb = new OptExactInfo();            /* boundary */
+    final OptExactInfo exm = new OptExactInfo();            /* middle */
+    final OptExactInfo expr = new OptExactInfo();           /* prec read (?=...) */
+    final OptMapInfo map = new OptMapInfo();                /* boundary */
+
+    public void setBoundNode(MinMaxLen mmd) {
+        exb.mmd.copy(mmd);
+        expr.mmd.copy(mmd);
+        map.mmd.copy(mmd);
+    }
+
+    public void clear() {
+        length.clear();
+        anchor.clear();
+        exb.clear();
+        exm.clear();
+        expr.clear();
+        map.clear();
+    }
+
+    public void copy(NodeOptInfo other) {
+        length.copy(other.length);
+        anchor.copy(other.anchor);
+        exb.copy(other.exb);
+        exm.copy(other.exm);
+        expr.copy(other.expr);
+        map.copy(other.map);
+    }
+
+    public void concatLeftNode(NodeOptInfo other) {
+        OptAnchorInfo tanchor = new OptAnchorInfo(); // remove it somehow ?
+        tanchor.concat(anchor, other.anchor, length.max, other.length.max);
+        anchor.copy(tanchor);
+
+        if (other.exb.length > 0 && length.max == 0) {
+            tanchor.concat(anchor, other.exb.anchor, length.max, other.length.max);
+            other.exb.anchor.copy(tanchor);
+        }
+
+        if (other.map.value > 0 && length.max == 0) {
+            if (other.map.mmd.max == 0) {
+                other.map.anchor.leftAnchor |= anchor.leftAnchor;
+            }
+        }
+
+        boolean exbReach = exb.reachEnd;
+        boolean exmReach = exm.reachEnd;
+
+        if (other.length.max != 0) {
+            exb.reachEnd = exm.reachEnd = false;
+        }
+
+        if (other.exb.length > 0) {
+            if (exbReach) {
+                exb.concat(other.exb);
+                other.exb.clear();
+            } else if (exmReach) {
+                exm.concat(other.exb);
+                other.exb.clear();
+            }
+        }
+
+        exm.select(other.exb);
+        exm.select(other.exm);
+
+        if (expr.length > 0) {
+            if (other.length.max > 0) {
+                // TODO: make sure it is not an Oniguruma bug (casting unsigned int to int for arithmetic comparison)
+                int otherLengthMax = other.length.max;
+                if (otherLengthMax == MinMaxLen.INFINITE_DISTANCE) otherLengthMax = -1;
+                if (expr.length > otherLengthMax) expr.length = otherLengthMax;
+                if (expr.mmd.max == 0) {
+                    exb.select(expr);
+                } else {
+                    exm.select(expr);
+                }
+            }
+        } else if (other.expr.length > 0) {
+            expr.copy(other.expr);
+        }
+
+        map.select(other.map);
+        length.add(other.length);
+    }
+
+    public void altMerge(NodeOptInfo other, OptEnvironment env) {
+        anchor.altMerge(other.anchor);
+        exb.altMerge(other.exb, env);
+        exm.altMerge(other.exm, env);
+        expr.altMerge(other.expr, env);
+        map.altMerge(other.map);
+        length.altMerge(other.length);
+    }
+
+    public void setBound(MinMaxLen mmd) {
+        exb.mmd.copy(mmd);
+        expr.mmd.copy(mmd);
+        map.mmd.copy(mmd);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/OptAnchorInfo.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,92 @@
+/*
+ * 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.constants.AnchorType;
+
+final class OptAnchorInfo implements AnchorType {
+    int leftAnchor;
+    int rightAnchor;
+
+    void clear() {
+        leftAnchor = rightAnchor = 0;
+    }
+
+    void copy(OptAnchorInfo other) {
+        leftAnchor = other.leftAnchor;
+        rightAnchor = other.rightAnchor;
+    }
+
+    void concat(OptAnchorInfo left, OptAnchorInfo right, int leftLength, int rightLength) {
+        leftAnchor = left.leftAnchor;
+        if (leftLength == 0) leftAnchor |= right.leftAnchor;
+
+        rightAnchor = right.rightAnchor;
+        if (rightLength == 0) rightAnchor |= left.rightAnchor;
+    }
+
+    boolean isSet(int anchor) {
+        if ((leftAnchor & anchor) != 0) return true;
+        return (rightAnchor & anchor) != 0;
+    }
+
+    void add(int anchor) {
+        if (isLeftAnchor(anchor)) {
+            leftAnchor |= anchor;
+        } else {
+            rightAnchor |= anchor;
+        }
+    }
+
+    void remove(int anchor) {
+        if (isLeftAnchor(anchor)) {
+            leftAnchor &= ~anchor;
+        } else {
+            rightAnchor &= ~anchor;
+        }
+    }
+
+    void altMerge(OptAnchorInfo other) {
+        leftAnchor &= other.leftAnchor;
+        rightAnchor &= other.rightAnchor;
+    }
+
+    static boolean isLeftAnchor(int anchor) { // make a mask for it ?
+        return !(anchor == END_BUF || anchor == SEMI_END_BUF ||
+                 anchor == END_LINE || anchor == PREC_READ ||
+                 anchor == PREC_READ_NOT);
+    }
+
+    static String anchorToString(int anchor) {
+        StringBuffer s = new StringBuffer("[");
+
+        if ((anchor & AnchorType.BEGIN_BUF) !=0 ) s.append("begin-buf ");
+        if ((anchor & AnchorType.BEGIN_LINE) !=0 ) s.append("begin-line ");
+        if ((anchor & AnchorType.BEGIN_POSITION) !=0 ) s.append("begin-pos ");
+        if ((anchor & AnchorType.END_BUF) !=0 ) s.append("end-buf ");
+        if ((anchor & AnchorType.SEMI_END_BUF) !=0 ) s.append("semi-end-buf ");
+        if ((anchor & AnchorType.END_LINE) !=0 ) s.append("end-line ");
+        if ((anchor & AnchorType.ANYCHAR_STAR) !=0 ) s.append("anychar-star ");
+        if ((anchor & AnchorType.ANYCHAR_STAR_ML) !=0 ) s.append("anychar-star-pl ");
+        s.append("]");
+
+        return s.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/OptEnvironment.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+// remove this one in future and pass mmd directly
+final class OptEnvironment {
+    final MinMaxLen mmd = new MinMaxLen();
+    int options;
+    int caseFoldFlag;
+    ScanEnvironment scanEnv;
+
+    void copy(OptEnvironment other) {
+        mmd.copy(other.mmd);
+        options = other.options;
+        caseFoldFlag = other.caseFoldFlag;
+        scanEnv = other.scanEnv;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk/nashorn/internal/runtime/regexp/joni/OptExactInfo.java	Fri Feb 22 17:00:22 2013 +0100
@@ -0,0 +1,153 @@
+/*
+ * 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;
+
+final class OptExactInfo {
+    static final int OPT_EXACT_MAXLEN = 24;
+
+    final MinMaxLen mmd = new MinMaxLen();
+    final OptAnchorInfo anchor = new OptAnchorInfo();
+
+    boolean reachEnd;
+    boolean ignoreCase;
+
+    final char chars[] = new char[OPT_EXACT_MAXLEN];
+    int length;
+
+    boolean isFull() {
+        return length >= OPT_EXACT_MAXLEN;
+    }
+
+    void clear() {
+        mmd.clear();
+        anchor.clear();
+
+        reachEnd = false;
+        ignoreCase = false;
+        length = 0;
+    }
+
+    void copy(OptExactInfo other) {
+        mmd.copy(other.mmd);
+        anchor.copy(other.anchor);
+        reachEnd = other.reachEnd;
+        ignoreCase = other.ignoreCase;
+        length = other.length;
+
+        System.arraycopy(other.chars, 0, chars, 0, OPT_EXACT_MAXLEN);
+    }
+
+    void concat(OptExactInfo other) {
+        if (!ignoreCase && other.ignoreCase) {
+            if (length >= other.length) return; /* avoid */
+            ignoreCase = true;
+        }