[jruby-joni] 27/223: Merge asmified branch into joni trunk. The asm compiler is not yet implemented (only parts of asm infrastructure exist now) though the original one is abstracted via visitor pattern and won't be affected by asm compiler work (it won't even touch asm classes). This branch also includes lots of performance improvements and memory savings already, also code ranges are now loaded in thread safe way. The main reason for the merge are 1.9 oniguruma changes that need to be incorporated into joni relatively soon.

Hideki Yamane henrich at moszumanska.debian.org
Mon Nov 16 11:21:42 UTC 2015


This is an automated email from the git hooks/post-receive script.

henrich pushed a commit to branch debian/sid
in repository jruby-joni.

commit 929b9fd92377e630d47138b2b478c3b79bab0243
Author: Marcin Mielżyński <lopx at gazeta.pl>
Date:   Thu Aug 7 18:07:06 2008 +0000

    Merge asmified branch into joni trunk. The asm compiler is not yet implemented (only parts of asm infrastructure exist now) though the original one is abstracted via visitor pattern and won't be affected by asm compiler work (it won't even touch asm classes). This branch also includes lots of performance improvements and memory savings already, also code ranges are now loaded in thread safe way. The main reason for the merge are 1.9 oniguruma changes that need to be incorporated into  [...]
    
    git-svn-id: http://svn.codehaus.org/jruby/joni/trunk@7403 961051c9-f516-0410-bf72-c9f7e237a7b7
---
 pom.xml                                            |    8 +-
 src/org/joni/Analyser.java                         |  122 +-
 src/org/joni/{Compiler.java => ArrayCompiler.java} |  631 ++++------
 src/org/joni/AsmCompiler.java                      |  109 ++
 src/org/joni/AsmCompilerSupport.java               |  267 ++++
 src/org/joni/ByteCodeMachine.java                  |  133 +-
 src/org/joni/ByteCodePrinter.java                  |    8 +-
 src/org/joni/Compiler.java                         | 1330 +-------------------
 src/org/joni/Config.java                           |    7 +-
 src/org/joni/Matcher.java                          |   63 +-
 .../ReturnCodes.java => MatcherFactory.java}       |   15 +-
 src/org/joni/NameEntry.java                        |    2 +-
 .../ReturnCodes.java => NativeMachine.java}        |   11 +-
 src/org/joni/Regex.java                            |   63 +-
 src/org/joni/ScanEnvironment.java                  |   21 +-
 src/org/joni/ScannerSupport.java                   |    4 +-
 src/org/joni/StackMachine.java                     |  150 ++-
 src/org/joni/UnsetAddrList.java                    |    2 +-
 .../{ReturnCodes.java => AsmConstants.java}        |   31 +-
 .../joni/encoding/unicode/UnicodeCTypeNames.java   |   43 +-
 .../joni/encoding/unicode/UnicodeCaseFolds.java    |   72 +-
 .../joni/encoding/unicode/UnicodeCodeRanges.java   |  313 +++--
 src/org/joni/encoding/unicode/UnicodeEncoding.java |   26 +-
 .../joni/encoding/unicode/UnicodeProperties.java   |  158 +--
 .../encoding/unicode/UnicodePropertiesScripts.java |  246 ++--
 25 files changed, 1496 insertions(+), 2339 deletions(-)

diff --git a/pom.xml b/pom.xml
index 8475514..4cf3fb1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
   <groupId>org.jruby.joni</groupId>
   <artifactId>joni</artifactId>
   <packaging>jar</packaging>
-  <version>1.0.2</version>
+  <version>1.0.3</version>
   <name>Joni</name>
   <description>
     Java port of Oniguruma: http://www.geocities.jp/kosako3/oniguruma
@@ -78,6 +78,12 @@
       <version>3.8.1</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>asm</groupId>
+      <artifactId>asm</artifactId>
+      <version>3.0</version>
+      <scope>provided</scope>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/src/org/joni/Analyser.java b/src/org/joni/Analyser.java
index e5c1477..9cfe0dd 100644
--- a/src/org/joni/Analyser.java
+++ b/src/org/joni/Analyser.java
@@ -19,17 +19,21 @@
  */
 package org.joni;
 
+import static org.joni.BitStatus.bsAll;
 import static org.joni.BitStatus.bsAt;
 import static org.joni.BitStatus.bsClear;
 import static org.joni.BitStatus.bsOnAt;
 import static org.joni.BitStatus.bsOnAtSimple;
 import static org.joni.Option.isCaptureGroup;
+import static org.joni.Option.isFindCondition;
 import static org.joni.Option.isIgnoreCase;
 import static org.joni.Option.isMultiline;
 import static org.joni.ast.ConsAltNode.newAltNode;
 import static org.joni.ast.ConsAltNode.newListNode;
 import static org.joni.ast.QuantifierNode.isRepeatInfinite;
 
+import java.util.HashSet;
+
 import org.joni.ast.AnchorNode;
 import org.joni.ast.BackRefNode;
 import org.joni.ast.CClassNode;
@@ -44,15 +48,129 @@ import org.joni.constants.AnchorType;
 import org.joni.constants.CharacterType;
 import org.joni.constants.EncloseType;
 import org.joni.constants.NodeType;
+import org.joni.constants.RegexState;
+import org.joni.constants.StackPopLevel;
 import org.joni.constants.TargetInfo;
-import org.joni.exception.SyntaxException;
 
-class Analyser extends Parser {
+final class Analyser extends Parser {
     
     protected Analyser(ScanEnvironment env, byte[]bytes, int p, int end) {
         super(env, bytes, p, end);
     }
     
+    protected final void compile() {
+        regex.state = RegexState.COMPILING;
+        
+        if (Config.DEBUG) {
+            Config.log.println(regex.encStringToString(bytes, 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
+        
+        setupTree(root, 0);        
+        if (Config.DEBUG_PARSE_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;
+        
+        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;
+            }
+        }
+
+        new ArrayCompiler(this).compile();
+        //new AsmCompiler(this).compile();
+
+        if (Config.DEBUG_COMPILE) {
+            if (Config.USE_NAMED_GROUP) Config.log.print(regex.nameTableToString());
+            Config.log.println("stack used: " + regex.stackNeeded);
+            Config.log.println(new ByteCodePrinter(regex).byteCodeListToString());
+        } // DEBUG_COMPILE
+        
+        regex.state = RegexState.NORMAL;
+    }
+    
     private Node noNameDisableMap(Node node, int[]map, int[]counter) {
         
         switch (node.getType()) {
diff --git a/src/org/joni/Compiler.java b/src/org/joni/ArrayCompiler.java
similarity index 74%
copy from src/org/joni/Compiler.java
copy to src/org/joni/ArrayCompiler.java
index b59006e..430aa11 100644
--- a/src/org/joni/Compiler.java
+++ b/src/org/joni/ArrayCompiler.java
@@ -19,17 +19,12 @@
  */
 package org.joni;
 
-import static org.joni.BitStatus.bsAll;
 import static org.joni.BitStatus.bsAt;
-import static org.joni.Option.isCaptureGroup;
 import static org.joni.Option.isDynamic;
-import static org.joni.Option.isFindCondition;
 import static org.joni.Option.isIgnoreCase;
 import static org.joni.Option.isMultiline;
 import static org.joni.ast.QuantifierNode.isRepeatInfinite;
 
-import java.util.HashSet;
-
 import org.joni.ast.AnchorNode;
 import org.joni.ast.BackRefNode;
 import org.joni.ast.CClassNode;
@@ -46,139 +41,61 @@ import org.joni.constants.EncloseType;
 import org.joni.constants.NodeType;
 import org.joni.constants.OPCode;
 import org.joni.constants.OPSize;
-import org.joni.constants.RegexState;
-import org.joni.constants.StackPopLevel;
 import org.joni.constants.TargetInfo;
 
-final class Compiler extends Analyser {
+final class ArrayCompiler extends Compiler {
 
-    protected Compiler(ScanEnvironment env, byte[]bytes, int p, int end) {
-        super(env, bytes, p, end);
+    ArrayCompiler(Analyser analyser) {
+        super(analyser);
     }
-    
-    protected final void compile() {
-        regex.state = RegexState.COMPILING;
-        
-        if (Config.DEBUG) {
-            Config.log.println(regex.encStringToString(bytes, getBegin(), getEnd()));
-        }
-        
-        reset();
 
-        regex.code = new int[(stop - p) * 2 + 1]; // +1: empty regex ??        
+    @Override
+    protected final void prepare() {
+        regex.code = new int[(analyser.stop - analyser.p) * 2 + 1]; // (+1: empty regex)        
         regex.codeLength = 0;
-        //regex.operands = new Object[10];
+    }
+
+    @Override
+    protected final void finish() {
+        addOpcode(OPCode.END);
+        addOpcode(OPCode.FINISH); // for stack bottom
         
-        regex.numMem = 0;
-        regex.numRepeat = 0;
-        regex.numNullCheck = 0;
-        //regex.repeatRangeAlloc = 0;
-        regex.repeatRangeLo = null;
-        regex.repeatRangeHi = null;        
-        regex.numCombExpCheck = 0;
+        if (Config.USE_SUBEXP_CALL && analyser.env.unsetAddrList != null) {
+            analyser.env.unsetAddrList.fix(regex);
+            analyser.env.unsetAddrList = null;
+        }
 
-        if (Config.USE_COMBINATION_EXPLOSION_CHECK) regex.numCombExpCheck = 0;
+        regex.factory = MatcherFactory.DEFAULT;
+    }
 
-        parse();
+    @Override
+    protected void compileAltNode(ConsAltNode node) {
+        ConsAltNode aln = node;
+        int len = 0;
 
-        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;
+        do {
+            len += compileLengthTree(aln.car);
+            if (aln.cdr != null) {
+                len += OPSize.PUSH + OPSize.JUMP;
             }
-        } // USE_NAMED_GROUP
-        
-        setupTree(root, 0);        
-        if (Config.DEBUG_PARSE_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);
+        } while ((aln = aln.cdr) != null);
 
-        env.memNodes = null;
-        
-        compileTree(root);
-        addOpcode(OPCode.END);
-        addOpcode(OPCode.FINISH); // for stack bottom
-        
-        if (Config.USE_SUBEXP_CALL && env.unsetAddrList != null) {
-            env.unsetAddrList.fix(regex);
-            env.unsetAddrList = null; /// ???            
-        }
-        
-        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;
+        int pos = regex.codeLength + len;  /* goal position */
+
+        aln = node;
+        do {
+            len = compileLengthTree(aln.car);
+            if (aln.cdr != null) {
+                addOpcodeRelAddr(OPCode.PUSH, len + OPSize.JUMP);
             }
-        }
-        
-        if (Config.DEBUG_COMPILE) {
-            if (Config.USE_NAMED_GROUP) Config.log.print(regex.nameTableToString());
-            Config.log.println("stack used: " + regex.stackNeeded);
-            Config.log.println(new ByteCodePrinter(regex).byteCodeListToString());
-        } // DEBUG_COMPILE
-        
-        regex.state = RegexState.NORMAL;
+            compileTree(aln.car);
+            if (aln.cdr != null) {
+                len = pos - (regex.codeLength + OPSize.JUMP);
+                addOpcodeRelAddr(OPCode.JUMP, len);
+            }
+        } while ((aln = aln.cdr) != null);
     }
-    
+
     private boolean isNeedStrLenOpExact(int op) {
         return  op == OPCode.EXACTN         ||
                 op == OPCode.EXACTMB2N      ||
@@ -187,7 +104,7 @@ final class Compiler extends Analyser {
                 op == OPCode.EXACTN_IC      ||
                 op == OPCode.EXACTN_IC_SB;
     }
-    
+
     private int selectStrOpcode(int mbLength, int strLength, boolean ignoreCase) {
         int op;
         
@@ -224,7 +141,7 @@ final class Compiler extends Analyser {
         }
         return op;
     }
-    
+
     private void compileTreeEmptyCheck(Node node, int emptyInfo) {
         int savedNumNullCheck = regex.numNullCheck;
         
@@ -252,17 +169,7 @@ final class Compiler extends Analyser {
             addMemNum(savedNumNullCheck); /* NULL CHECK ID */
         }
     }
-    
-    private void compileCall(CallNode node) {
-        addOpcode(OPCode.CALL);
-        node.unsetAddrList.add(regex.codeLength, node.target);
-        addAbsAddr(0); /*dummy addr.*/
-    }
-    
-    private void compileTreeNTimes(Node node, int n) {
-        for (int i=0; i<n; i++) compileTree(node);
-    }
-    
+
     private int addCompileStringlength(byte[]bytes, int p, int mbLength, int strLength, boolean ignoreCase) {
         int op = selectStrOpcode(mbLength, strLength, ignoreCase);
         
@@ -275,7 +182,8 @@ final class Compiler extends Analyser {
         return len;
     }
 
-    private void addCompileString(byte[]bytes, int p, int mbLength, int strLength, boolean ignoreCase) {
+    @Override
+    protected final void addCompileString(byte[]bytes, int p, int mbLength, int strLength, boolean ignoreCase) {
         int op = selectStrOpcode(mbLength, strLength, ignoreCase);
         addOpcode(op);
         
@@ -288,12 +196,12 @@ final class Compiler extends Analyser {
                 addLength(strLength);                
             }
         }
-        regex.addBytes(bytes, p, mbLength * strLength);
+        addBytes(bytes, p, mbLength * strLength);
     }
-    
+
     private int compileLengthStringNode(Node node) {
         StringNode sn = (StringNode)node;
-        if (sn.length() <= 0) return 0; // ??? out
+        if (sn.length() <= 0) return 0;
         boolean ambig = sn.isAmbig();
         
         int p, prev;
@@ -325,46 +233,13 @@ final class Compiler extends Analyser {
     }
     
     private int compileLengthStringRawNode(StringNode sn) {
-        if (sn.length() <= 0) return 0; // ??? throw an exception ??
+        if (sn.length() <= 0) return 0;        
         return addCompileStringlength(sn.bytes, sn.p, 1 /*sb*/, sn.length(), false);
     }
-    
-    private void compileStringNode(Node node) {
-        StringNode sn = (StringNode)node;
-        if (sn.length() <= 0) return; // out ?
-        boolean ambig = sn.isAmbig();
-        
-        int p, prev;
-        p = prev = sn.p;
-        int end = sn.end;
-        byte[]bytes = sn.bytes;
-        int prevLen = enc.length(bytes[p]);
-        p += prevLen;
-        int slen = 1;
-        
-        while (p < end) {            
-            int len = enc.length(bytes[p]);
-            if (len == prevLen) {
-                slen++;
-            } else {
-                addCompileString(bytes, prev, prevLen, slen, ambig);
-                prev = p;
-                slen = 1;
-                prevLen = len;
-            }
-            p += len;
-        }
-        addCompileString(bytes, prev, prevLen, slen, ambig);
-    }
-    
-    private void compileStringRawNode(StringNode sn) {
-        if (sn.length() <= 0) return; // ??
-        addCompileString(sn.bytes, sn.p, 1 /*sb*/, sn.length(), false);
-    }
-    
+
     private void addMultiByteCClass(CodeRangeBuffer mbuf) {
         addLength(mbuf.used);
-        regex.addInts(mbuf.p, mbuf.used);
+        addInts(mbuf.p, mbuf.used);
     }
     
     private int compileLengthCClassNode(CClassNode cc) {
@@ -384,8 +259,9 @@ final class Compiler extends Analyser {
         }
         return len;
     }
-    
-    private void compileCClassNode(CClassNode cc) {
+
+    @Override
+    protected void compileCClassNode(CClassNode cc) {
         if (cc.isShare()) { // shared char class
             addOpcode(OPCode.CCLASS_NODE);
             addPointer(cc);
@@ -398,7 +274,7 @@ final class Compiler extends Analyser {
             } else {
                 addOpcode(enc.isSingleByte() ? OPCode.CCLASS_SB : OPCode.CCLASS);
             }
-            regex.addInts(cc.bs.bits, BitSet.BITSET_SIZE); // add_bitset
+            addInts(cc.bs.bits, BitSet.BITSET_SIZE); // add_bitset
         } else {
             if (enc.minLength() > 1 || cc.bs.isEmpty()) {
                 if (cc.isNot()) {
@@ -414,12 +290,91 @@ final class Compiler extends Analyser {
                     addOpcode(OPCode.CCLASS_MIX);
                 }
                 // store the bit set and mbuf themself!
-                regex.addInts(cc.bs.bits, BitSet.BITSET_SIZE); // add_bitset
+                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 = enc.isSingleByte() ? OPCode.NOT_WORD_SB : OPCode.NOT_WORD;
+            } else {
+                op = enc.isSingleByte() ? OPCode.WORD_SB : OPCode.WORD;
+            }
+            break;
+            
+        default:
+            newInternalException(ERR_PARSER_BUG);
+            return; // not reached  
+        } // inner switch
+        addOpcode(op);
+    }
+
+    @Override
+    protected void compileAnyCharNode() {
+        if (isMultiline(regex.options)) {            
+            addOpcode(enc.isSingleByte() ? OPCode.ANYCHAR_ML_SB : OPCode.ANYCHAR_ML);
+        } else {
+            addOpcode(enc.isSingleByte() ? OPCode.ANYCHAR_SB : OPCode.ANYCHAR);
+        }
+    }
+
+    @Override
+    protected void compileCallNode(CallNode node) {
+        addOpcode(OPCode.CALL);
+        node.unsetAddrList.add(regex.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) {
@@ -534,8 +489,9 @@ final class Compiler extends Analyser {
         }
         return len;
     }
-    
-    private void compileCECQuantifierNode(QuantifierNode qn) {
+
+    @Override
+    protected void compileCECQuantifierNode(QuantifierNode qn) {
         boolean infinite = isRepeatInfinite(qn.upper);
         int emptyInfo = qn.targetEmptyInfo;
         
@@ -555,7 +511,7 @@ final class Compiler extends Analyser {
                     addStateCheckNum(ckn);
                 }
                 StringNode sn = (StringNode)qn.nextHeadExact;
-                regex.addBytes(sn.bytes, sn.p, 1);
+                addBytes(sn.bytes, sn.p, 1);
                 return;
             } else {
                 if (isMultiline(regex.options)) {
@@ -649,13 +605,7 @@ final class Compiler extends Analyser {
             }
         }
     }
-    
-    private int compileLengthQuantifierNode(QuantifierNode qn) {
-        return Config.USE_COMBINATION_EXPLOSION_CHECK ?
-                compileCECLengthQuantifierNode(qn) :
-                compileNonCECLengthQuantifierNode(qn);    
-    }
-    
+
     private int compileNonCECLengthQuantifierNode(QuantifierNode qn) {
         boolean infinite = isRepeatInfinite(qn.upper);
         int emptyInfo = qn.targetEmptyInfo;
@@ -714,15 +664,8 @@ final class Compiler extends Analyser {
         return len;
     }
     
-    private void compileQuantifierNode(QuantifierNode qn) {
-        if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
-            compileCECQuantifierNode(qn);
-        } else {
-            compileNonCECQuantifierNode(qn);
-        }
-    }
-    
-    private void compileNonCECQuantifierNode(QuantifierNode qn) {
+    @Override
+    protected void compileNonCECQuantifierNode(QuantifierNode qn) {
         boolean infinite = isRepeatInfinite(qn.upper);
         int emptyInfo = qn.targetEmptyInfo;
         
@@ -737,7 +680,7 @@ final class Compiler extends Analyser {
                     addOpcode(enc.isSingleByte() ? OPCode.ANYCHAR_STAR_PEEK_NEXT_SB : OPCode.ANYCHAR_STAR_PEEK_NEXT);
                 }
                 StringNode sn = (StringNode)qn.nextHeadExact;
-                regex.addBytes(sn.bytes, sn.p, 1);
+                addBytes(sn.bytes, sn.p, 1);
                 return;
             } else {
                 if (isMultiline(regex.options)) {
@@ -776,13 +719,13 @@ final class Compiler extends Analyser {
                 if (qn.headExact != null) {
                     addOpcodeRelAddr(OPCode.PUSH_OR_JUMP_EXACT1, modTLen + OPSize.JUMP);
                     StringNode sn = (StringNode)qn.headExact;
-                    regex.addBytes(sn.bytes, sn.p, 1);
+                    addBytes(sn.bytes, 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;
-                    regex.addBytes(sn.bytes, sn.p, 1);
+                    addBytes(sn.bytes, sn.p, 1);
                     compileTreeEmptyCheck(qn.target, emptyInfo);
                     addOpcodeRelAddr(OPCode.JUMP, -(modTLen + OPSize.JUMP + OPSize.PUSH_IF_PEEK_NEXT));
                 } else {
@@ -828,8 +771,9 @@ final class Compiler extends Analyser {
             return tlen;
         }
     }
-    
-    private void compileOptionNode(EncloseNode node) {
+
+    @Override
+    protected void compileOptionNode(EncloseNode node) {
         int prev = regex.options;
         
         if (isDynamic(prev ^ node.option)) {
@@ -895,13 +839,9 @@ final class Compiler extends Analyser {
         } // switch
         return len;
     }
-    
-    private void compileEncloseNode(EncloseNode node) {
-        if (node.isOption()) {
-            compileOptionNode(node);
-            return;
-        }
-        
+
+    @Override
+    protected void compileEncloseNode(EncloseNode node) {
         int len;
         switch (node.type) {
         case EncloseType.MEMORY:
@@ -1005,8 +945,9 @@ final class Compiler extends Analyser {
         } // switch
         return len;
     }
-    
-    private void compileAnchorNode(AnchorNode node) {
+
+    @Override
+    protected void compileAnchorNode(AnchorNode node) {
         int len;
         int n;
         
@@ -1052,8 +993,8 @@ final class Compiler extends Analyser {
         case AnchorType.LOOK_BEHIND:
             addOpcode(enc.isSingleByte() ? OPCode.LOOK_BEHIND_SB : OPCode.LOOK_BEHIND);
             if (node.charLength < 0) {
-                n = getCharLengthTree(node.target);
-                if (returnCode != 0) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
+                n = analyser.getCharLengthTree(node.target);
+                if (analyser.returnCode != 0) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
             } else {
                 n = node.charLength;
             }
@@ -1065,8 +1006,8 @@ final class Compiler extends Analyser {
             len = compileLengthTree(node.target);
             addOpcodeRelAddr(OPCode.PUSH_LOOK_BEHIND_NOT, len + OPSize.FAIL_LOOK_BEHIND_NOT);
             if (node.charLength < 0) {
-                n = getCharLengthTree(node.target);
-                if (returnCode != 0) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
+                n = analyser.getCharLengthTree(node.target);
+                if (analyser.returnCode != 0) newSyntaxException(ERR_INVALID_LOOK_BEHIND_PATTERN);
             } else {
                 n = node.charLength;
             }
@@ -1143,7 +1084,11 @@ final class Compiler extends Analyser {
             break;
             
         case NodeType.QTFR:
-            len = compileLengthQuantifierNode((QuantifierNode)node);
+            if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
+                len = compileCECLengthQuantifierNode((QuantifierNode)node);
+            } else {
+                len = compileNonCECLengthQuantifierNode((QuantifierNode)node);
+            }
             break;
             
         case NodeType.ENCLOSE:
@@ -1160,152 +1105,58 @@ final class Compiler extends Analyser {
         } //switch
         return len;
     }
+
+    private void ensure(int size) {
+        if (size >= regex.code.length) {
+            int length = regex.code.length << 1;
+            while (length <= size) length <<= 1;
+            int[]tmp = new int[length];
+            System.arraycopy(regex.code, 0, tmp, 0, regex.code.length);
+            regex.code = tmp;
+        }
+    }
     
-    private void compileTree(Node node) {
-        int len = 0;
+    private void addInt(int i) {
+        if (regex.codeLength >= regex.code.length) {
+            int[]tmp = new int[regex.code.length << 1];
+            System.arraycopy(regex.code, 0, tmp, 0, regex.code.length);
+            regex.code = tmp;
+        }
+        regex.code[regex.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 addBytes(byte[]bytes, int p ,int length) {
+        ensure(regex.codeLength + length);
+        int end = p + length;
         
-        switch (node.getType()) {
-        case NodeType.LIST:
-            ConsAltNode lin = (ConsAltNode)node;
-            do {
-                compileTree(lin.car);
-            } while ((lin = lin.cdr) != null);
-            break;
-            
-        case NodeType.ALT:
-            ConsAltNode aln = (ConsAltNode)node;
-            do {
-                len += compileLengthTree(aln.car);
-                if (aln.cdr != null) {
-                    len += OPSize.PUSH + OPSize.JUMP;
-                }
-            } while ((aln = aln.cdr) != null);
-            int pos = regex.codeLength + len;  /* goal position */
-            
-            aln = (ConsAltNode)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 - (regex.codeLength + OPSize.JUMP);
-                    addOpcodeRelAddr(OPCode.JUMP, len);
-                }
-                
-            } while ((aln = aln.cdr) != null);
-            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:
-            CTypeNode cn = (CTypeNode)node;
-            int op;
-            switch (cn.ctype) {
-            case CharacterType.WORD:
-                if (cn.not) {
-                    op = enc.isSingleByte() ? OPCode.NOT_WORD_SB : OPCode.NOT_WORD;
-                } else {
-                    op = enc.isSingleByte() ? OPCode.WORD_SB : OPCode.WORD;
-                }
-                break;
-                
-            default:
-                newInternalException(ERR_PARSER_BUG);
-                return; // not reached  
-            } // inner switch
-            addOpcode(op);
-            break;
-            
-        case NodeType.CANY:
-            if (isMultiline(regex.options)) {
-                addOpcode(enc.isSingleByte() ? OPCode.ANYCHAR_ML_SB : OPCode.ANYCHAR_ML);
-            } else {
-                addOpcode(enc.isSingleByte() ? OPCode.ANYCHAR_SB : OPCode.ANYCHAR);
-            }
-            break;
-
-        case NodeType.BREF:
-            BackRefNode br = (BackRefNode)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]);                        
-                break;
-            } 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]);                        
-                }
-            }
-            break;
-            
-        case NodeType.CALL:
-            if (Config.USE_SUBEXP_CALL) {
-                compileCall((CallNode)node);
-                break;
-            } // USE_SUBEXP_CALL
-            break;
-            
-        case NodeType.QTFR:
-            compileQuantifierNode((QuantifierNode)node);
-            break;
-            
-        case NodeType.ENCLOSE:
-            compileEncloseNode((EncloseNode)node);
-            break;
-            
-        case NodeType.ANCHOR:
-            compileAnchorNode((AnchorNode)node);
-            break;
-
-        default:
-            // undefined node type
-            newInternalException(ERR_PARSER_BUG);
-        } // switch
+        while (p < end) regex.code[regex.codeLength++] = bytes[p++];
+    }
+    
+    private void addInts(int[]ints, int length) { 
+        ensure(regex.codeLength + length);
+        System.arraycopy(ints, 0, regex.code, regex.codeLength, length);
+        regex.codeLength += length;
     }
     
-    void addOpcode(int opcode) {
-        regex.addInt(opcode);
+    private void addOpcode(int opcode) {
+        addInt(opcode);
         
         switch(opcode) {
         case OPCode.ANYCHAR_STAR:
@@ -1344,47 +1195,43 @@ final class Compiler extends Analyser {
         case OPCode.RETURN: // it will appear only with CALL though
             regex.stackNeeded = true;
         }
-        
     }
-    
-    void addStateCheckNum(int num) {
-        regex.addInt(num);
+
+    private void addStateCheckNum(int num) {
+        addInt(num);
     }
-    
-    void addRelAddr(int addr) {
-        regex.addInt(addr);
+
+    private void addRelAddr(int addr) {
+        addInt(addr);
     }
-    
-    void addAbsAddr(int addr) {
-        regex.addInt(addr);
+
+    private void addAbsAddr(int addr) {
+        addInt(addr);
     }
-    
-    void addLength(int length) {
-        regex.addInt(length);
+
+    private void addLength(int length) {
+        addInt(length);
     }
-    
-    void addMemNum(int num) {
-        regex.addInt(num);
+
+    private void addMemNum(int num) {
+        addInt(num);
     }
-    
-    void addPointer(Object o) {
-        regex.addObject(o);
+
+    private void addPointer(Object o) {
+        addObject(o);
     }
-    
-    void addOption(int option) {
-        regex.addInt(option);
+
+    private void addOption(int option) {
+        addInt(option);
     }
-    
-    void addOpcodeRelAddr(int opcode, int addr) {
+
+    private void addOpcodeRelAddr(int opcode, int addr) {
         addOpcode(opcode);
         addRelAddr(addr);
     }
-    
-    void addOpcodeOption(int opcode, int option) {
+
+    private void addOpcodeOption(int opcode, int option) {
         addOpcode(opcode);
         addOption(option);
-    }
-    
-    
-    
+    }    
 }
diff --git a/src/org/joni/AsmCompiler.java b/src/org/joni/AsmCompiler.java
new file mode 100644
index 0000000..433d324
--- /dev/null
+++ b/src/org/joni/AsmCompiler.java
@@ -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 org.joni;
+
+import org.joni.ast.AnchorNode;
+import org.joni.ast.BackRefNode;
+import org.joni.ast.CClassNode;
+import org.joni.ast.CTypeNode;
+import org.joni.ast.CallNode;
+import org.joni.ast.ConsAltNode;
+import org.joni.ast.EncloseNode;
+import org.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(byte[]bytes, int p, int mbLength, int strLength, boolean ignoreCase) {
+        String template = installTemplate(bytes, 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) {
+    }
+}
diff --git a/src/org/joni/AsmCompilerSupport.java b/src/org/joni/AsmCompilerSupport.java
new file mode 100644
index 0000000..217aa8b
--- /dev/null
+++ b/src/org/joni/AsmCompilerSupport.java
@@ -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 org.joni;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.joni.constants.AsmConstants;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import 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 = "org/joni/MatcherFactory" + REG_NUM;
+
+        factory.visit(V1_4, ACC_PUBLIC + ACC_FINAL, factoryName, null, "org/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, "org/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 = "org/joni/NativeMachine" + REG_NUM;
+    }
+    
+    protected final void prepareMachineInit() {
+        machine.visit(V1_4, ACC_PUBLIC + ACC_FINAL, machineName, null, "org/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, "org/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, "org/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(byte[]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, byte[]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));                
+        }
+    }
+}
diff --git a/src/org/joni/ByteCodeMachine.java b/src/org/joni/ByteCodeMachine.java
index 503d10a..0aa1bf8 100644
--- a/src/org/joni/ByteCodeMachine.java
+++ b/src/org/joni/ByteCodeMachine.java
@@ -29,82 +29,28 @@ import static org.joni.Option.isPosixRegion;
 
 import org.joni.ast.CClassNode;
 import org.joni.constants.OPCode;
-import org.joni.constants.ReturnCodes;
+import org.joni.constants.OPSize;
 import org.joni.encoding.Encoding;
 import org.joni.exception.ErrorMessages;
 import org.joni.exception.InternalException;
 
 class ByteCodeMachine extends StackMachine {
-    protected final byte[]bytes;
-    protected final int str;
-    protected final int end;
-    
     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 int msaStart;
-    private int msaOptions;
-    protected final Region msaRegion;
-    protected int msaBestLen;
-    protected int msaBestS;
-    
-    protected int msaBegin;
-    protected int msaEnd;
-    
 
     private final int[]code;        // byte code
     private int ip;                 // instruction pointer
 
-    // cached values
-    protected final int option;
-    protected final Encoding enc;
-    protected final int caseFoldFlag;
-    
     ByteCodeMachine(Regex regex, byte[]bytes, int p, int end) {
-        super(regex);
-        this.bytes = bytes;
-        this.str = p;
-        this.end = end;
-        
+        super(regex, bytes, p, end);
         this.code = regex.code;
-        this.option = regex.options;
-        this.enc = regex.enc;
-        this.caseFoldFlag = regex.caseFoldFlag;
-        
-        this.msaRegion = regex.numMem == 0 ? null : new Region(regex.numMem + 1);
     }
-    
-    protected final void msaInit(int option, int start) {
-        msaOptions = option;
-        msaStart = start;
-        if (Config.USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE) msaBestLen = ReturnCodes.MISMATCH;
-    }    
 
-    // USE_COMBINATION_EXPLOSION_CHECK    
-    private int stateCheckPos(int s, int snum) {
-        return (s - str) * regex.numCombExpCheck + (snum - 1); // make numCombExpCheck ivar ?
-    }
-    
-    private boolean stateCheckVal(int snum) {
-        if (stateCheckBuff != null) {
-            int x = stateCheckPos(s, snum);
-            return (stateCheckBuff[x / 8] & (1 << (x % 8))) != 0;
-        }
-        return false;
-    }
-    
-    // ELSE_IF_STATE_CHECK_MARK
-    protected final void stateCheckMark() {
-        StackEntry e = stack[stk];
-        int x = stateCheckPos(e.getStatePStr(), e.getStateCheck());
-        stateCheckBuff[x / 8] |= (1 << (x % 8)); 
-    }
-    
     protected int stkp; // a temporary
     private boolean makeCaptureHistoryTree(CaptureTreeNode node) {
         //CaptureTreeNode child;
@@ -136,10 +82,28 @@ class ByteCodeMachine extends StackMachine {
         }
         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 byte[]cfbuf;
     private byte[]cfbuf2;
-    
+
     protected final byte[]cfbuf() {
         return cfbuf == null ? cfbuf = new byte[Config.ENC_MBC_CASE_FOLD_MAXLEN] : cfbuf;
     }
@@ -382,7 +346,7 @@ class ByteCodeMachine extends StackMachine {
             } // USE_FIND_LONGEST_SEARCH_ALL_OF_RANGE
             
             bestLen = n;
-            Region region = msaRegion;
+            final Region region = msaRegion;
             if (region != null) {
                 // USE_POSIX_REGION_OPTION ... else ...
                 region.beg[0] = sstart - str;
@@ -406,23 +370,7 @@ class ByteCodeMachine extends StackMachine {
                 }
                 
                 if (Config.USE_CAPTURE_HISTORY) {
-                    if (regex.captureHistory != 0) {
-                        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);
-                    }
+                    if (regex.captureHistory != 0) checkCaptureHistory(region);
                 }
             } else {
                 msaBegin = sstart - str;
@@ -454,7 +402,7 @@ class ByteCodeMachine extends StackMachine {
     private boolean endBestLength() {
         if (isFindCondition(option)) {
             if (isFindNotEmpty(option) && s == sstart) {
-                bestLen = ReturnCodes.MISMATCH;
+                bestLen = -1;
                 {opFail(); return false;} /* for retry */
             }
             if (isFindLongest(option) && s < range) {
@@ -934,7 +882,7 @@ class ByteCodeMachine extends StackMachine {
         final byte[]bytes = this.bytes;
         
         while (s < range) {
-            if (stateCheckVal(mem)) {opFail(); return;}
+            if (stateCheckVal(s, mem)) {opFail(); return;}
             pushAltWithStateCheck(ip, s, sprev, mem);
             int n = enc.length(bytes[s]);
             if (s + n > range) {opFail(); return;}
@@ -950,7 +898,7 @@ class ByteCodeMachine extends StackMachine {
         final byte[]bytes = this.bytes;
         
         while (s < range) {
-            if (stateCheckVal(mem)) {opFail(); return;}
+            if (stateCheckVal(s, mem)) {opFail(); return;}
             pushAltWithStateCheck(ip, s, sprev, mem);
             if (bytes[s] == Encoding.NEW_LINE) {opFail(); return;}
             sprev = s;
@@ -965,7 +913,7 @@ class ByteCodeMachine extends StackMachine {
         
         final byte[]bytes = this.bytes;            
         while (s < range) {
-            if (stateCheckVal(mem)) {opFail(); return;}
+            if (stateCheckVal(s, mem)) {opFail(); return;}
             pushAltWithStateCheck(ip, s, sprev, mem);
             int n = enc.length(bytes[s]);
             if (s + n > range) {opFail(); return;}
@@ -979,7 +927,7 @@ class ByteCodeMachine extends StackMachine {
         int mem = code[ip++];
         
         while (s < range) {
-            if (stateCheckVal(mem)) {opFail(); return;}
+            if (stateCheckVal(s, mem)) {opFail(); return;}
             pushAltWithStateCheck(ip, s, sprev, mem);
             sprev = s;
             s++;
@@ -1142,7 +1090,7 @@ class ByteCodeMachine extends StackMachine {
     }
 
     private void opBeginPosition() {
-        if (s != msaStart) opFail();;
+        if (s != msaStart) opFail();
     }
 
     private void opMemoryStartPush() {
@@ -1391,7 +1339,18 @@ class ByteCodeMachine extends StackMachine {
             {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);
@@ -1479,7 +1438,7 @@ class ByteCodeMachine extends StackMachine {
     // CEC
     private void opStateCheckPush() {
         int mem = code[ip++];
-        if (stateCheckVal(mem)) {opFail(); return;}
+        if (stateCheckVal(s, mem)) {opFail(); return;}
         int addr = code[ip++];
         pushAltWithStateCheck(ip + addr, s, sprev, mem);
     }
@@ -1489,7 +1448,7 @@ class ByteCodeMachine extends StackMachine {
         int mem = code[ip++];
         int addr= code[ip++];
         
-        if (stateCheckVal(mem)) {
+        if (stateCheckVal(s, mem)) {
             ip += addr;
         } else {
             pushAltWithStateCheck(ip + addr, s, sprev, mem);
@@ -1499,7 +1458,7 @@ class ByteCodeMachine extends StackMachine {
     // CEC
     private void opStateCheck() {
         int mem = code[ip++];
-        if (stateCheckVal(mem)) {opFail(); return;}
+        if (stateCheckVal(s, mem)) {opFail(); return;}
         pushStateCheck(s, mem);
     }
     
diff --git a/src/org/joni/ByteCodePrinter.java b/src/org/joni/ByteCodePrinter.java
index 227c462..708e76d 100644
--- a/src/org/joni/ByteCodePrinter.java
+++ b/src/org/joni/ByteCodePrinter.java
@@ -67,6 +67,7 @@ class ByteCodePrinter {
 
         sb.append("[" + OPCode.OpCodeNames[code[bp]]);
         int argType = OPCode.OpCodeArgTypes[code[bp]];
+        int ip = bp;
         if (argType != Arguments.SPECIAL) {
             bp++;
             switch (argType) {
@@ -327,10 +328,15 @@ class ByteCodePrinter {
                 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");
diff --git a/src/org/joni/Compiler.java b/src/org/joni/Compiler.java
index b59006e..8227ba6 100644
--- a/src/org/joni/Compiler.java
+++ b/src/org/joni/Compiler.java
@@ -19,17 +19,6 @@
  */
 package org.joni;
 
-import static org.joni.BitStatus.bsAll;
-import static org.joni.BitStatus.bsAt;
-import static org.joni.Option.isCaptureGroup;
-import static org.joni.Option.isDynamic;
-import static org.joni.Option.isFindCondition;
-import static org.joni.Option.isIgnoreCase;
-import static org.joni.Option.isMultiline;
-import static org.joni.ast.QuantifierNode.isRepeatInfinite;
-
-import java.util.HashSet;
-
 import org.joni.ast.AnchorNode;
 import org.joni.ast.BackRefNode;
 import org.joni.ast.CClassNode;
@@ -40,300 +29,45 @@ import org.joni.ast.EncloseNode;
 import org.joni.ast.Node;
 import org.joni.ast.QuantifierNode;
 import org.joni.ast.StringNode;
-import org.joni.constants.AnchorType;
-import org.joni.constants.CharacterType;
-import org.joni.constants.EncloseType;
 import org.joni.constants.NodeType;
-import org.joni.constants.OPCode;
-import org.joni.constants.OPSize;
-import org.joni.constants.RegexState;
-import org.joni.constants.StackPopLevel;
-import org.joni.constants.TargetInfo;
-
-final class Compiler extends Analyser {
+import org.joni.encoding.Encoding;
+import org.joni.exception.ErrorMessages;
+import org.joni.exception.InternalException;
+import org.joni.exception.SyntaxException;
 
-    protected Compiler(ScanEnvironment env, byte[]bytes, int p, int end) {
-        super(env, bytes, p, end);
-    }
+abstract class Compiler implements ErrorMessages {
+    protected final Analyser analyser;
+    protected final Encoding enc;
+    protected final Regex regex;
     
-    protected final void compile() {
-        regex.state = RegexState.COMPILING;
-        
-        if (Config.DEBUG) {
-            Config.log.println(regex.encStringToString(bytes, getBegin(), getEnd()));
-        }
-        
-        reset();
-
-        regex.code = new int[(stop - p) * 2 + 1]; // +1: empty regex ??        
-        regex.codeLength = 0;
-        //regex.operands = new Object[10];
-        
-        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
-        
-        setupTree(root, 0);        
-        if (Config.DEBUG_PARSE_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;
-        
-        compileTree(root);
-        addOpcode(OPCode.END);
-        addOpcode(OPCode.FINISH); // for stack bottom
-        
-        if (Config.USE_SUBEXP_CALL && env.unsetAddrList != null) {
-            env.unsetAddrList.fix(regex);
-            env.unsetAddrList = null; /// ???            
-        }
-        
-        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);
-            Config.log.println(new ByteCodePrinter(regex).byteCodeListToString());
-        } // DEBUG_COMPILE
-        
-        regex.state = RegexState.NORMAL;
+    protected Compiler(Analyser analyser) {
+        this.analyser = analyser;
+        this.regex = analyser.regex;
+        this.enc = regex.enc;
     }
     
-    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;
+    final void compile() {
+        prepare();
+        compileTree(analyser.root);
+        finish();
     }
     
-    private int selectStrOpcode(int mbLength, int strLength, boolean ignoreCase) {
-        int op;
-        
-        if (ignoreCase) {
-            switch(strLength) {
-            case 1: op = enc.toLowerCaseTable() != null ? OPCode.EXACT1_IC_SB : OPCode.EXACT1_IC; break;
-            default:op = enc.toLowerCaseTable() != null ? OPCode.EXACTN_IC_SB : 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;
-            default:
-                op = OPCode.EXACTMBN;
-            } // switch
-        }
-        return op;
-    }
+    protected abstract void prepare();
+    protected abstract void finish();
     
-    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 void compileCall(CallNode node) {
-        addOpcode(OPCode.CALL);
-        node.unsetAddrList.add(regex.codeLength, node.target);
-        addAbsAddr(0); /*dummy addr.*/
-    }
+    protected abstract void compileAltNode(ConsAltNode node);
     
-    private void compileTreeNTimes(Node node, int n) {
-        for (int i=0; i<n; i++) compileTree(node);
+    private void compileStringRawNode(StringNode sn) {
+        if (sn.length() <= 0) return;        
+        addCompileString(sn.bytes, sn.p, 1 /*sb*/, sn.length(), false);
     }
-    
-    private int addCompileStringlength(byte[]bytes, int p, int mbLength, int strLength, boolean ignoreCase) {
-        int op = selectStrOpcode(mbLength, strLength, ignoreCase);
-        
-        int len = OPSize.OPCODE;
 
-        if (op == OPCode.EXACTMBN) len += OPSize.LENGTH;
-        if (isNeedStrLenOpExact(op)) len += OPSize.LENGTH;
-        
-        len += mbLength * strLength;
-        return len;
-    }
+    private void compileStringNode(StringNode node) {
+        StringNode sn = node;
+        if (sn.length() <= 0) return;
 
-    private void addCompileString(byte[]bytes, 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);                
-            }
-        }
-        regex.addBytes(bytes, p, mbLength * strLength);
-    }
-    
-    private int compileLengthStringNode(Node node) {
-        StringNode sn = (StringNode)node;
-        if (sn.length() <= 0) return 0; // ??? out
         boolean ambig = sn.isAmbig();
-        
-        int p, prev;
-        p = prev = sn.p;
-        int end = sn.end;
-        byte[]bytes = sn.bytes;
-        int prevLen = enc.length(bytes[p]);
-        p += prevLen;
-        
-        int slen = 1;
-        int rlen = 0;
-        
-        while (p < end) {
-            int len = enc.length(bytes[p]);
-            if (len == prevLen) {
-                slen++;
-            } else {
-                int r = addCompileStringlength(bytes, prev, prevLen, slen, ambig);
-                rlen += r;
-                prev = p;
-                slen = 1;
-                prevLen = len;
-            }
-            p += len;
-        }
-        int r = addCompileStringlength(bytes, prev, prevLen, slen, ambig);
-        rlen += r;
-        return rlen;
-    }
-    
-    private int compileLengthStringRawNode(StringNode sn) {
-        if (sn.length() <= 0) return 0; // ??? throw an exception ??
-        return addCompileStringlength(sn.bytes, sn.p, 1 /*sb*/, sn.length(), false);
-    }
-    
-    private void compileStringNode(Node node) {
-        StringNode sn = (StringNode)node;
-        if (sn.length() <= 0) return; // out ?
-        boolean ambig = sn.isAmbig();
-        
+
         int p, prev;
         p = prev = sn.p;
         int end = sn.end;
@@ -354,816 +88,23 @@ final class Compiler extends Analyser {
             }
             p += len;
         }
-        addCompileString(bytes, prev, prevLen, slen, ambig);
-    }
-    
-    private void compileStringRawNode(StringNode sn) {
-        if (sn.length() <= 0) return; // ??
-        addCompileString(sn.bytes, sn.p, 1 /*sb*/, sn.length(), false);
-    }
-    
-    private void addMultiByteCClass(CodeRangeBuffer mbuf) {
-        addLength(mbuf.used);
-        regex.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 (enc.minLength() > 1 || cc.bs.isEmpty()) {
-                len = OPSize.OPCODE;
-            } else {
-                len = OPSize.OPCODE + BitSet.BITSET_SIZE;
-            }
-            
-            len += OPSize.LENGTH + cc.mbuf.used;
-        }
-        return len;
-    }
-    
-    private 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(enc.isSingleByte() ? OPCode.CCLASS_NOT_SB : OPCode.CCLASS_NOT);
-            } else {
-                addOpcode(enc.isSingleByte() ? OPCode.CCLASS_SB : OPCode.CCLASS);
-            }
-            regex.addInts(cc.bs.bits, BitSet.BITSET_SIZE); // add_bitset
-        } else {
-            if (enc.minLength() > 1 || 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!
-                regex.addInts(cc.bs.bits, BitSet.BITSET_SIZE); // add_bitset
-                addMultiByteCClass(cc.mbuf);
-            }
-        }
-    }
-    
-    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;
-    }
-    
-    private 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(enc.isSingleByte() ? OPCode.ANYCHAR_ML_STAR_PEEK_NEXT_SB : OPCode.ANYCHAR_ML_STAR_PEEK_NEXT);
-                } else {
-                    addOpcode(enc.isSingleByte() ? OPCode.ANYCHAR_STAR_PEEK_NEXT_SB : OPCode.ANYCHAR_STAR_PEEK_NEXT);
-                }
-                if (cknOn(ckn)) {
-                    addStateCheckNum(ckn);
-                }
-                StringNode sn = (StringNode)qn.nextHeadExact;
-                regex.addBytes(sn.bytes, sn.p, 1);
-                return;
-            } else {
-                if (isMultiline(regex.options)) {
-                    if (cknOn(ckn)) {
-                        addOpcode(enc.isSingleByte() ? OPCode.STATE_CHECK_ANYCHAR_ML_STAR_SB : OPCode.STATE_CHECK_ANYCHAR_ML_STAR);
-                    } else {
-                        addOpcode(enc.isSingleByte() ? OPCode.ANYCHAR_ML_STAR_SB : OPCode.ANYCHAR_ML_STAR);
-                    }
-                } else {
-                    if (cknOn(ckn)) {
-                        addOpcode(enc.isSingleByte() ? OPCode.STATE_CHECK_ANYCHAR_STAR_SB : OPCode.STATE_CHECK_ANYCHAR_STAR);
-                    } else {
-                        addOpcode(enc.isSingleByte() ? OPCode.ANYCHAR_STAR_SB : 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 compileLengthQuantifierNode(QuantifierNode qn) {
-        return Config.USE_COMBINATION_EXPLOSION_CHECK ?
-                compileCECLengthQuantifierNode(qn) :
-                compileNonCECLengthQuantifierNode(qn);    
-    }
-    
-    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;
-    }
-    
-    private void compileQuantifierNode(QuantifierNode qn) {
-        if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
-            compileCECQuantifierNode(qn);
-        } else {
-            compileNonCECQuantifierNode(qn);
-        }
-    }
-    
-    private 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(enc.isSingleByte() ? OPCode.ANYCHAR_ML_STAR_PEEK_NEXT_SB : OPCode.ANYCHAR_ML_STAR_PEEK_NEXT);
-                } else {
-                    addOpcode(enc.isSingleByte() ? OPCode.ANYCHAR_STAR_PEEK_NEXT_SB : OPCode.ANYCHAR_STAR_PEEK_NEXT);
-                }
-                StringNode sn = (StringNode)qn.nextHeadExact;
-                regex.addBytes(sn.bytes, sn.p, 1);
-                return;
-            } else {
-                if (isMultiline(regex.options)) {
-                    addOpcode(enc.isSingleByte() ? OPCode.ANYCHAR_ML_STAR_SB : OPCode.ANYCHAR_ML_STAR);
-                } else {
-                    addOpcode(enc.isSingleByte() ? OPCode.ANYCHAR_STAR_SB : 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;
-                    regex.addBytes(sn.bytes, 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;
-                    regex.addBytes(sn.bytes, 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;
-        }
+        addCompileString(bytes, prev, prevLen, slen, ambig);        
     }
     
-    private 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;
-    }
+    protected abstract void addCompileString(byte[]bytes, int p, int mbLength, int strLength, boolean ignoreCase);
     
-    private void compileEncloseNode(EncloseNode node) {
-        if (node.isOption()) {
-            compileOptionNode(node);
-            return;
-        }
-        
-        int len;
-        switch (node.type) {
-        case EncloseType.MEMORY:
-            if (Config.USE_SUBEXP_CALL) {
-                if (node.isCalled()) {
-                    addOpcode(OPCode.CALL);
-                    node.callAddr = regex.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;
-    }
-    
-    private 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(enc.isSingleByte() ? OPCode.WORD_BOUND_SB : OPCode.WORD_BOUND);
-            break;
-            
-        case AnchorType.NOT_WORD_BOUND:
-            addOpcode(enc.isSingleByte() ? OPCode.NOT_WORD_BOUND_SB : OPCode.NOT_WORD_BOUND);
-            break;
-        
-        case AnchorType.WORD_BEGIN:
-            if (Config.USE_WORD_BEGIN_END)
-                addOpcode(enc.isSingleByte() ? OPCode.WORD_BEGIN_SB : OPCode.WORD_BEGIN);
-            break;
-        
-        case AnchorType.WORD_END:
-            if (Config.USE_WORD_BEGIN_END)
-                addOpcode(enc.isSingleByte() ? OPCode.WORD_END_SB : 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(enc.isSingleByte() ? OPCode.LOOK_BEHIND_SB : OPCode.LOOK_BEHIND);
-            if (node.charLength < 0) {
-                n = getCharLengthTree(node.target);
-                if (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 = getCharLengthTree(node.target);
-                if (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:
-            len = compileLengthQuantifierNode((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;
-    }
+    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);
     
-    private void compileTree(Node node) {
-        int len = 0;
-        
+    protected final void compileTree(Node node) {
         switch (node.getType()) {
         case NodeType.LIST:
             ConsAltNode lin = (ConsAltNode)node;
@@ -1173,28 +114,7 @@ final class Compiler extends Analyser {
             break;
             
         case NodeType.ALT:
-            ConsAltNode aln = (ConsAltNode)node;
-            do {
-                len += compileLengthTree(aln.car);
-                if (aln.cdr != null) {
-                    len += OPSize.PUSH + OPSize.JUMP;
-                }
-            } while ((aln = aln.cdr) != null);
-            int pos = regex.codeLength + len;  /* goal position */
-            
-            aln = (ConsAltNode)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 - (regex.codeLength + OPSize.JUMP);
-                    addOpcodeRelAddr(OPCode.JUMP, len);
-                }
-                
-            } while ((aln = aln.cdr) != null);
+            compileAltNode((ConsAltNode)node);
             break;
             
         case NodeType.STR:
@@ -1211,89 +131,41 @@ final class Compiler extends Analyser {
             break;
             
         case NodeType.CTYPE:
-            CTypeNode cn = (CTypeNode)node;
-            int op;
-            switch (cn.ctype) {
-            case CharacterType.WORD:
-                if (cn.not) {
-                    op = enc.isSingleByte() ? OPCode.NOT_WORD_SB : OPCode.NOT_WORD;
-                } else {
-                    op = enc.isSingleByte() ? OPCode.WORD_SB : OPCode.WORD;
-                }
-                break;
-                
-            default:
-                newInternalException(ERR_PARSER_BUG);
-                return; // not reached  
-            } // inner switch
-            addOpcode(op);
+            compileCTypeNode((CTypeNode)node);
             break;
             
         case NodeType.CANY:
-            if (isMultiline(regex.options)) {
-                addOpcode(enc.isSingleByte() ? OPCode.ANYCHAR_ML_SB : OPCode.ANYCHAR_ML);
-            } else {
-                addOpcode(enc.isSingleByte() ? OPCode.ANYCHAR_SB : OPCode.ANYCHAR);
-            }
+            compileAnyCharNode();
             break;
 
         case NodeType.BREF:
-            BackRefNode br = (BackRefNode)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]);                        
-                break;
-            } 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]);                        
-                }
-            }
+            compileBackrefNode((BackRefNode)node);
             break;
             
         case NodeType.CALL:
             if (Config.USE_SUBEXP_CALL) {
-                compileCall((CallNode)node);
+                compileCallNode((CallNode)node);
                 break;
             } // USE_SUBEXP_CALL
             break;
             
         case NodeType.QTFR:
-            compileQuantifierNode((QuantifierNode)node);
+            if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
+                compileCECQuantifierNode((QuantifierNode)node);
+            } else {
+                compileNonCECQuantifierNode((QuantifierNode)node);
+            }
             break;
             
         case NodeType.ENCLOSE:
-            compileEncloseNode((EncloseNode)node);
-            break;
-            
+            EncloseNode enode = (EncloseNode)node;
+            if (enode.isOption()) {
+                compileOptionNode(enode);
+            } else {
+                compileEncloseNode(enode);
+            }
+            break;            
+
         case NodeType.ANCHOR:
             compileAnchorNode((AnchorNode)node);
             break;
@@ -1304,87 +176,15 @@ final class Compiler extends Analyser {
         } // switch
     }
     
-    void addOpcode(int opcode) {
-        regex.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;
-        }
-        
-    }
-    
-    void addStateCheckNum(int num) {
-        regex.addInt(num);
-    }
-    
-    void addRelAddr(int addr) {
-        regex.addInt(addr);
-    }
-    
-    void addAbsAddr(int addr) {
-        regex.addInt(addr);
-    }
-    
-    void addLength(int length) {
-        regex.addInt(length);
-    }
-    
-    void addMemNum(int num) {
-        regex.addInt(num);
-    }
-    
-    void addPointer(Object o) {
-        regex.addObject(o);
-    }
-    
-    void addOption(int option) {
-        regex.addInt(option);
+    protected final void compileTreeNTimes(Node node, int n) {
+        for (int i=0; i<n; i++) compileTree(node);
     }
-    
-    void addOpcodeRelAddr(int opcode, int addr) {
-        addOpcode(opcode);
-        addRelAddr(addr);
+
+    protected void newSyntaxException(String message) {
+        throw new SyntaxException(message);
     }
-    
-    void addOpcodeOption(int opcode, int option) {
-        addOpcode(opcode);
-        addOption(option);
+
+    protected void newInternalException(String message) {
+        throw new InternalException(message);
     }
-    
-    
-    
 }
diff --git a/src/org/joni/Config.java b/src/org/joni/Config.java
index 9300f55..9adad03 100644
--- a/src/org/joni/Config.java
+++ b/src/org/joni/Config.java
@@ -102,10 +102,13 @@ public interface Config {
     final PrintStream log = System.out;
     final PrintStream err = System.err;
 
-    final boolean DEBUG_ALL                         = true;
+    final boolean DEBUG_ALL                         = false;
     final boolean DEBUG                             = DEBUG_ALL;    
     final boolean DEBUG_PARSE_TREE                  = DEBUG_ALL;
-    final boolean DEBUG_COMPILE                     = DEBUG_ALL;    
+    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;
 }
diff --git a/src/org/joni/Matcher.java b/src/org/joni/Matcher.java
index 001ce70..cf27171 100644
--- a/src/org/joni/Matcher.java
+++ b/src/org/joni/Matcher.java
@@ -23,17 +23,53 @@ package org.joni;
 import static org.joni.Option.isFindLongest;
 
 import org.joni.constants.AnchorType;
+import org.joni.encoding.Encoding;
+
+public abstract class Matcher extends IntHolder {
+    protected final Regex regex;
+    protected final Encoding enc;
+
+    protected final byte[]bytes;
+    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;
+
+    // cached values
+    protected final int option;
+    protected final int caseFoldFlag;
 
-public final class Matcher extends ByteCodeMachine {
-    
     public Matcher(Regex regex, byte[]bytes) {
         this(regex, bytes, 0, bytes.length);
     }
-    
+
     public Matcher(Regex regex, byte[]bytes, int p, int end) {
-        super(regex, bytes, p, end);
+        this.regex = regex;
+        this.enc = regex.enc;
+
+        this.bytes = bytes;
+        this.str = p;
+        this.end = end;
+
+        this.msaRegion = regex.numMem == 0 ? null : new Region(regex.numMem + 1);
+
+        this.option = regex.options;
+        this.caseFoldFlag = regex.caseFoldFlag;
     }
-    
+
+    // 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;
     }
@@ -48,8 +84,14 @@ public final class Matcher extends ByteCodeMachine {
     
     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);
         
@@ -350,12 +392,9 @@ public final class Matcher extends ByteCodeMachine {
                 s = start = str;
                 prev = -1;
                 msaInit(option, start);
-                
-                if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
-                    stateCheckBuff = null;
-                    stateCheckBuffSize = 0;
-                }
-                
+
+                if (Config.USE_COMBINATION_EXPLOSION_CHECK) stateCheckBuffClear();
+
                 if (matchCheck(end, s, prev)) return match(s);
                 return mismatch();
             }
diff --git a/src/org/joni/constants/ReturnCodes.java b/src/org/joni/MatcherFactory.java
similarity index 74%
copy from src/org/joni/constants/ReturnCodes.java
copy to src/org/joni/MatcherFactory.java
index 7afcde6..729eeb0 100644
--- a/src/org/joni/constants/ReturnCodes.java
+++ b/src/org/joni/MatcherFactory.java
@@ -17,10 +17,15 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  * SOFTWARE.
  */
-package org.joni.constants;
+package org.joni;
 
-public interface ReturnCodes {
-    final int NORMAL            = 0;
-    final int MISMATCH          = -1;
-    final int NO_SUPPORT_CONFIG = -2;
+public abstract class MatcherFactory {
+    public abstract Matcher create(Regex regex, byte[]bytes, int p, int end);
+
+    static final MatcherFactory DEFAULT = new MatcherFactory() {
+        @Override
+        public Matcher create(Regex regex, byte[] bytes, int p, int end) {
+            return new ByteCodeMachine(regex, bytes, p, end);
+        }
+    };
 }
diff --git a/src/org/joni/NameEntry.java b/src/org/joni/NameEntry.java
index 4dcb4ab..794cf1b 100644
--- a/src/org/joni/NameEntry.java
+++ b/src/org/joni/NameEntry.java
@@ -47,7 +47,7 @@ public final class NameEntry {
             System.arraycopy(backRefs, 0, result, 0, backNum);
             return result;
         }
-    }    
+    }
 
     private void alloc() {
         backRefs = new int[INIT_NAME_BACKREFS_ALLOC_NUM];
diff --git a/src/org/joni/constants/ReturnCodes.java b/src/org/joni/NativeMachine.java
similarity index 85%
copy from src/org/joni/constants/ReturnCodes.java
copy to src/org/joni/NativeMachine.java
index 7afcde6..6fc5dbb 100644
--- a/src/org/joni/constants/ReturnCodes.java
+++ b/src/org/joni/NativeMachine.java
@@ -17,10 +17,11 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  * SOFTWARE.
  */
-package org.joni.constants;
+package org.joni;
 
-public interface ReturnCodes {
-    final int NORMAL            = 0;
-    final int MISMATCH          = -1;
-    final int NO_SUPPORT_CONFIG = -2;
+public abstract class NativeMachine extends Matcher {
+
+    protected NativeMachine(Regex regex, byte[]bytes, int p, int end) {
+        super(regex, bytes, p, end);
+    }
 }
diff --git a/src/org/joni/Regex.java b/src/org/joni/Regex.java
index c5fc8da..06dc823 100644
--- a/src/org/joni/Regex.java
+++ b/src/org/joni/Regex.java
@@ -58,7 +58,8 @@ public final class Regex implements RegexState {
     int[]repeatRangeHi;
 
     public WarnCallback warnings;
-    
+    public MatcherFactory factory;
+
     final Encoding enc;
     int options;
     int userOptions;
@@ -123,8 +124,8 @@ public final class Regex implements RegexState {
         this.caseFoldFlag = caseFoldFlag;
         this.warnings = warnings;
 
-        new Compiler(new ScanEnvironment(this, syntax), bytes, p, end).compile();
-        
+        new Analyser(new ScanEnvironment(this, syntax), bytes, p, end).compile();
+
         this.warnings = null;
     }
     
@@ -133,7 +134,7 @@ public final class Regex implements RegexState {
     }
     
     public Matcher matcher(byte[]bytes, int p, int end) {
-        return new Matcher(this, bytes, p, end);
+        return factory.create(this, bytes, p, end);
     }
     
     public int numberOfCaptures() {
@@ -169,7 +170,7 @@ public final class Regex implements RegexState {
         if (nameTable != null) return nameTable.get(name, nameP, nameEnd);
         return null;
     }
-    
+
     void renumberNameTable(int[]map) {
         if (nameTable != null) {
             for (NameEntry e : nameTable) {
@@ -202,7 +203,7 @@ public final class Regex implements RegexState {
             // dup the name here as oni does ?, what for ? (it has to manage it, we don't)
             e = new NameEntry(name, nameP, nameEnd);
             nameTable.putDirect(name, nameP, nameEnd, e);
-        } else if (e.backNum >= 1 && !syntax.allowMultiplexDefinitionName()) { // env out!!!
+        } else if (e.backNum >= 1 && !syntax.allowMultiplexDefinitionName()) {
             throw new ValueException(ErrorMessages.ERR_MULTIPLEX_DEFINED_NAME, new String(name, nameP, nameEnd - nameP));
         }
 
@@ -218,7 +219,6 @@ public final class Regex implements RegexState {
         if (e == null) throw new ValueException(ErrorMessages.ERR_UNDEFINED_NAME_REFERENCE,
                                                 new String(name, nameP, nameEnd - nameP));
 
-        
         switch(e.backNum) {
         case 0:
             throw new InternalException(ErrorMessages.ERR_PARSER_BUG);
@@ -403,55 +403,6 @@ public final class Regex implements RegexState {
         return s;
     }
 
-    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;
-        }
-    }
-    
-    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);
-        code[offset] = i;
-    }
-    
-    void addObject(Object o) {
-        if (operands == null) {
-            operands = new Object[4];
-        } else if (operandLength >= operands.length) {
-            Object[]tmp = new Object[operands.length << 1];            
-            System.arraycopy(operands, 0, tmp, 0, operands.length);            
-            operands = tmp;            
-        }
-        addInt(operandLength);
-        operands[operandLength++] = o;
-    }
-    
-    void addBytes(byte[]bytes, int p ,int length) {
-        ensure(codeLength + length);
-        int end = p + length;
-        
-        while (p < end) code[codeLength++] = bytes[p++];
-    }
-    
-    void addInts(int[]ints, int length) { 
-        ensure(codeLength + length);
-        System.arraycopy(ints, 0, code, codeLength, length);
-        codeLength += length;
-    }
-
     public int getOptions() {
         return options;
     }
diff --git a/src/org/joni/ScanEnvironment.java b/src/org/joni/ScanEnvironment.java
index 152479d..549e62c 100644
--- a/src/org/joni/ScanEnvironment.java
+++ b/src/org/joni/ScanEnvironment.java
@@ -47,7 +47,7 @@ public final class ScanEnvironment {
 
     int numNamed; // USE_NAMED_GROUP
     
-    public Node memNodes[] = new Node[SCANENV_MEMNODES_SIZE]; // should be EncloseNode[] ???
+    public Node memNodes[];
     
     // USE_COMBINATION_EXPLOSION_CHECK
     int numCombExpCheck;
@@ -68,18 +68,14 @@ public final class ScanEnvironment {
         btMemStart = bsClear();
         btMemEnd = bsClear();
         backrefedMem = bsClear();
-        
+
         numCall = 0;
         numMem = 0;
-        
+
         numNamed = 0;
-        
-        if (memNodes.length > SCANENV_MEMNODES_SIZE) {
-            memNodes = new Node[SCANENV_MEMNODES_SIZE];
-        } else {
-            for (int i=0; i<SCANENV_MEMNODES_SIZE; i++) memNodes[i] = null;
-        }
-        
+
+        memNodes = null;
+
         numCombExpCheck = 0;
         combExpMaxRegNum = 0;
         currMaxRegNum = 0;
@@ -87,8 +83,9 @@ public final class ScanEnvironment {
     }
     
     public int addMemEntry() {
-        numMem++;
-        if (numMem >= memNodes.length) {
+        if (numMem++ == 0) {
+            memNodes = new Node[SCANENV_MEMNODES_SIZE];
+        } else if (numMem >= memNodes.length) {
             Node[]tmp = new Node[memNodes.length << 1];
             System.arraycopy(memNodes, 0, tmp, 0, memNodes.length);
             memNodes = tmp;
diff --git a/src/org/joni/ScannerSupport.java b/src/org/joni/ScannerSupport.java
index 696ac64..39a1f74 100644
--- a/src/org/joni/ScannerSupport.java
+++ b/src/org/joni/ScannerSupport.java
@@ -64,7 +64,7 @@ abstract class ScannerSupport extends IntHolder implements ErrorMessages {
             fetch();
             if (enc.isDigit(c)) {
                 int onum = num;
-                num = num * 10 + enc.digitVal(c);
+                num = num * 10 + Encoding.digitVal(c);
                 if (((onum ^ num) & INT_SIGN_BIT) != 0) return -1;
             } else {
                 unfetch();              
@@ -97,7 +97,7 @@ abstract class ScannerSupport extends IntHolder implements ErrorMessages {
             fetch();
             if (enc.isDigit(c) && c < '8') {                
                 int onum = num;             
-                int val = enc.odigitVal(c);
+                int val = Encoding.odigitVal(c);
                 num = (num << 3) + val;
                 if (((onum ^ num) & INT_SIGN_BIT) != 0) return -1;
             } else {
diff --git a/src/org/joni/StackMachine.java b/src/org/joni/StackMachine.java
index c619c04..9be72af 100644
--- a/src/org/joni/StackMachine.java
+++ b/src/org/joni/StackMachine.java
@@ -27,7 +27,7 @@ import java.util.Arrays;
 import org.joni.constants.StackPopLevel;
 import org.joni.constants.StackType;
 
-abstract class StackMachine extends IntHolder implements StackType {
+abstract class StackMachine extends Matcher implements StackType {
     protected static final int INVALID_INDEX = -1;
     
     protected StackEntry[]stack;
@@ -35,15 +35,14 @@ abstract class StackMachine extends IntHolder implements StackType {
 
     protected final int[]repeatStk;
     protected final int memStartStk, memEndStk;
-    
-    protected final Regex regex;
-    
+
     // CEC
     protected byte[] stateCheckBuff; // move to int[] ?
     int stateCheckBuffSize;    
-    
-    public StackMachine(Regex regex) {
-        this.regex = regex;
+
+    protected StackMachine(Regex regex, byte[]bytes, int p , int end) {
+        super(regex, bytes, p, end);
+
         this.stack = regex.stackNeeded ? fetchStack() : null;
         int n = regex.numRepeat + (regex.numMem << 1);
         this.repeatStk = n > 0 ? new int[n] : null;
@@ -102,14 +101,33 @@ abstract class StackMachine extends IntHolder implements StackType {
         ensure1();
         stack[stk++].type = type;
     }
-    
+
+    // CEC
+
+    // STATE_CHECK_POS
+    private int stateCheckPos(int s, int snum) {
+        return (s - str) * regex.numCombExpCheck + (snum - 1);
+    }
+
+    // STATE_CHECK_VAL
+    protected final boolean stateCheckVal(int s, int snum) {
+        if (stateCheckBuff != null) {
+            int x = stateCheckPos(s, snum);
+            return (stateCheckBuff[x / 8] & (1 << (x % 8))) != 0;
+        }
+        return false;
+    }
+
     // ELSE_IF_STATE_CHECK_MARK
-    protected abstract void stateCheckMark();
-    // STATE_CHECK_POS and STATE_CHECK_VAL implemented in byteCodeMachine, CEC only
-    
+    private void stateCheckMark() {
+        StackEntry e = stack[stk];
+        int x = stateCheckPos(e.getStatePStr(), e.getStateCheck());
+        stateCheckBuff[x / 8] |= (1 << (x % 8)); 
+    }
+
     // STATE_CHECK_BUFF_INIT
     private static final int STATE_CHECK_BUFF_MALLOC_THRESHOLD_SIZE = 16;
-    void stateCheckBuffInit(int strLength, int offset, int stateNum) {
+    protected final void stateCheckBuffInit(int strLength, int offset, int stateNum) {
         if (stateNum > 0 && strLength >= Config.CHECK_STRING_THRESHOLD_LEN) {
             int size = ((strLength + 1) * stateNum + 7) >>> 3;
             offset = (offset * stateNum) >>> 3;
@@ -131,8 +149,13 @@ abstract class StackMachine extends IntHolder implements StackType {
             stateCheckBuff = null; // reduce
             stateCheckBuffSize = 0;
         }
-    }    
-    
+    }
+
+    protected final void stateCheckBuffClear() {
+        stateCheckBuff = null;
+        stateCheckBuffSize = 0;
+    }
+
     private void push(int type, int pat, int s, int prev) {
         ensure1();
         StackEntry e = stack[stk];
@@ -329,57 +352,64 @@ abstract class StackMachine extends IntHolder implements StackType {
     protected final void popOne() {
         stk--;
     }
-    
-    protected final StackEntry pop() { 
-        StackEntry e;
-        
+
+    protected final StackEntry pop() {
         switch (regex.stackPopLevel) {
         case StackPopLevel.FREE:
-            while (true) {
-                e = stack[--stk];
-                
-                if ((e.type & MASK_POP_USED) != 0) {
-                    break;
-                } else if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
-                    if (e.type == STATE_CHECK_MARK) stateCheckMark();
-                }
-            }
-            return e;
+            return popFree();
         case StackPopLevel.MEM_START:
-            while (true) {
-                e = stack[--stk];
-                
-                if ((e.type & MASK_POP_USED) != 0) { 
-                    break;
-                } else if (e.type == MEM_START) {
-                    repeatStk[memStartStk + e.getMemNum()] = e.getMemStart();
-                    repeatStk[memEndStk + e.getMemNum()] = e.getMemEnd();
-                } else if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
-                    if (e.type == STATE_CHECK_MARK) stateCheckMark();
-                }
-            }
-            return e;
+            return popMemStart();
         default:
-            while (true) {
-                e = stack[--stk];
-
-                if ((e.type & MASK_POP_USED) != 0) {
-                    break;
-                } else if (e.type == MEM_START) {
-                    repeatStk[memStartStk + e.getMemNum()] = e.getMemStart();
-                    repeatStk[memEndStk + e.getMemNum()] = e.getMemEnd();
-                } else if (e.type == REPEAT_INC) {
-                    //int si = stack[stk + IREPEAT_INC_SI];
-                    //stack[si + IREPEAT_COUNT]--;
-                    stack[e.getSi()].decreaseRepeatCount();
-                } else if (e.type == MEM_END) {
-                    repeatStk[memStartStk + e.getMemNum()] = e.getMemStart();
-                    repeatStk[memEndStk + e.getMemNum()] = e.getMemEnd();                
-                } else if (Config.USE_COMBINATION_EXPLOSION_CHECK) {                    
-                    if (e.type == STATE_CHECK_MARK) stateCheckMark();                    
-                }
+            return popDefault();
+        }
+    }
+
+    private StackEntry popFree() {
+        while (true) {
+            StackEntry e = stack[--stk];
+            
+            if ((e.type & MASK_POP_USED) != 0) {
+                return e;
+            } else if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
+                if (e.type == STATE_CHECK_MARK) stateCheckMark();
+            }
+        }
+    }
+
+    private StackEntry popMemStart() {
+        while (true) {
+            StackEntry e = stack[--stk];
+            
+            if ((e.type & MASK_POP_USED) != 0) { 
+                return e;
+            } else if (e.type == MEM_START) {
+                repeatStk[memStartStk + e.getMemNum()] = e.getMemStart();
+                repeatStk[memEndStk + e.getMemNum()] = e.getMemEnd();
+            } else if (Config.USE_COMBINATION_EXPLOSION_CHECK) {
+                if (e.type == STATE_CHECK_MARK) stateCheckMark();
+            }
+        }
+    }
+
+    private StackEntry popDefault() {
+        while (true) {
+            StackEntry e = stack[--stk];
+
+            if ((e.type & MASK_POP_USED) != 0) {
+                return e;
+            } else if (e.type == MEM_START) {
+                repeatStk[memStartStk + e.getMemNum()] = e.getMemStart();
+                repeatStk[memEndStk + e.getMemNum()] = e.getMemEnd();
+            } else if (e.type == REPEAT_INC) {
+                //int si = stack[stk + IREPEAT_INC_SI];
+                //stack[si + IREPEAT_COUNT]--;
+                stack[e.getSi()].decreaseRepeatCount();
+            } else if (e.type == MEM_END) {
+                repeatStk[memStartStk + e.getMemNum()] = e.getMemStart();
+                repeatStk[memEndStk + e.getMemNum()] = e.getMemEnd();                
+            } else if (Config.USE_COMBINATION_EXPLOSION_CHECK) {                    
+                if (e.type == STATE_CHECK_MARK) stateCheckMark();                    
             }
-            return e;
         }
     }
 
diff --git a/src/org/joni/UnsetAddrList.java b/src/org/joni/UnsetAddrList.java
index d0648df..8787972 100644
--- a/src/org/joni/UnsetAddrList.java
+++ b/src/org/joni/UnsetAddrList.java
@@ -53,7 +53,7 @@ public final class UnsetAddrList {
         for (int i=0; i<num; i++) {
             EncloseNode en = (EncloseNode)targets[i];
             if (!en.isAddrFixed()) new InternalException(ErrorMessages.ERR_PARSER_BUG);
-            regex.setInt(en.callAddr, offsets[i]);
+            regex.code[offsets[i]] = en.callAddr; // is this safe ?
         }
     }
     
diff --git a/src/org/joni/constants/ReturnCodes.java b/src/org/joni/constants/AsmConstants.java
similarity index 53%
rename from src/org/joni/constants/ReturnCodes.java
rename to src/org/joni/constants/AsmConstants.java
index 7afcde6..6329780 100644
--- a/src/org/joni/constants/ReturnCodes.java
+++ b/src/org/joni/constants/AsmConstants.java
@@ -19,8 +19,31 @@
  */
 package org.joni.constants;
 
-public interface ReturnCodes {
-    final int NORMAL            = 0;
-    final int MISMATCH          = -1;
-    final int NO_SUPPORT_CONFIG = -2;
+public interface AsmConstants {
+    final int THIS = 0;
+
+    // argument indexes
+    final int RANGE             = 1;
+    final int SSTART            = 2;
+    final int SPREV             = 3;
+
+    // local var indexes
+    final int S                 = 4;            // current index
+    final int BYTES             = 5;            // string
+    final int LAST_INDEX        = BYTES + 1;
+
+    // frequently used field names (all ints)
+    final String STR            = "str";
+    final String END            = "end";
+    final String MSA_START      = "msaStart";
+    final String MSA_OPTONS     = "msaOptions";
+    final String MSA_BEST_LEN   = "msaBestLen";
+    final String MSA_BEST_S     = "msaBestS";
+    final String MSA_BEGIN      = "msaBegin";
+    final String MSA_END        = "msaEnd";
+
+    // generated field names
+    final String BITSET         = "bitset";
+    final String CODERANGE      = "range";
+    final String TEMPLATE       = "template";
 }
diff --git a/src/org/joni/encoding/unicode/UnicodeCTypeNames.java b/src/org/joni/encoding/unicode/UnicodeCTypeNames.java
index d69f044..c15b858 100644
--- a/src/org/joni/encoding/unicode/UnicodeCTypeNames.java
+++ b/src/org/joni/encoding/unicode/UnicodeCTypeNames.java
@@ -24,20 +24,8 @@ import org.joni.util.BytesHash;
 
 public class UnicodeCTypeNames {
 
-    static void initializeCTypeNameTable() {
-        BytesHash<Integer> table = new BytesHash<Integer>();
-        
-        int limit = Config.USE_UNICODE_PROPERTIES ? CTypeNameTable.length : 15;
-        
-        for (int i=0; i<limit; i++) 
-            table.putDirect(CTypeNameTable[i], i);
-        
-        CTypeNameHash = table;
-    }
-    
-    static BytesHash<Integer> CTypeNameHash;
-    
-    private static final byte CTypeNameTable[][] = new byte[][] {
+    private static final byte CTypeNameTable[][] = Config.USE_UNICODE_PROPERTIES ?
+        new byte[][] {
         "NEWLINE".getBytes(),
         "Alpha".getBytes(),
         "Blank".getBytes(),
@@ -53,7 +41,7 @@ public class UnicodeCTypeNames {
         "Word".getBytes(),
         "Alnum".getBytes(),
         "ASCII".getBytes(),
-        
+
         // unicode properties
         "Any".getBytes(),
         "Assigned".getBytes(),
@@ -155,6 +143,31 @@ public class UnicodeCTypeNames {
         "Tifinagh".getBytes(),
         "Ugaritic".getBytes(),
         "Yi".getBytes()
+    } :
+        new byte[][] {
+        "NEWLINE".getBytes(),
+        "Alpha".getBytes(),
+        "Blank".getBytes(),
+        "Cntrl".getBytes(),
+        "Digit".getBytes(),
+        "Graph".getBytes(),
+        "Lower".getBytes(),
+        "Print".getBytes(),
+        "Punct".getBytes(),
+        "Space".getBytes(),
+        "Upper".getBytes(),
+        "XDigit".getBytes(),
+        "Word".getBytes(),
+        "Alnum".getBytes(),
+        "ASCII".getBytes()
     };
 
+    private static BytesHash<Integer> initializeCTypeNameTable() {
+        BytesHash<Integer> table = new BytesHash<Integer>();
+        for (int i=0; i<CTypeNameTable.length; i++) table.putDirect(CTypeNameTable[i], i);
+
+        return table;
+    }
+    
+    static final BytesHash<Integer> CTypeNameHash = initializeCTypeNameTable();    
 }
diff --git a/src/org/joni/encoding/unicode/UnicodeCaseFolds.java b/src/org/joni/encoding/unicode/UnicodeCaseFolds.java
index 94b0324..ef48ac7 100644
--- a/src/org/joni/encoding/unicode/UnicodeCaseFolds.java
+++ b/src/org/joni/encoding/unicode/UnicodeCaseFolds.java
@@ -24,39 +24,6 @@ import org.joni.util.IntHash;
 
 public class UnicodeCaseFolds {
 
-    static void initializeCaseFoldTables() {
-        IntHash<int[]> fold = new IntHash<int[]>(1200);
-        for (int i=0; i<CaseFold1_From.length; i++)
-            fold.putDirect(CaseFold1_From[i], CaseFold1_To[i]);
-        for (int i=0; i<CaseFold_Locale_From.length; i++) 
-            fold.putDirect(CaseFold_Locale_From[i], CaseFold_Locale_To[i]);
-        FoldHash = fold;
-        
-        IntHash<int[]> unfold1 = new IntHash<int[]>(1000);
-        for (int i=0; i<CaseUnfold_11_From.length; i++) 
-            unfold1.putDirect(CaseUnfold_11_From[i], CaseUnfold_11_To[i]);
-        for (int i=0; i<CaseUnfold_11_Locale_From.length; i++) 
-            unfold1.putDirect(CaseUnfold_11_Locale_From[i], CaseUnfold_11_Locale_To[i]);
-        Unfold1Hash = unfold1;
-
-        IntArrayHash<int[]> unfold2 = new IntArrayHash<int[]>(200);
-        for (int i=0; i<CaseUnfold_12.length; i+=2)
-            unfold2.putDirect(CaseUnfold_12[i], CaseUnfold_12[i + 1]);
-        for (int i=0; i<CaseUnfold_12_Locale.length; i+=2)
-            unfold2.putDirect(CaseUnfold_12_Locale[i], CaseUnfold_12_Locale[i + 1]);
-        Unfold2Hash = unfold2;
-
-        IntArrayHash<int[]> unfold3 = new IntArrayHash<int[]>(30);
-        for (int i=0; i<CaseUnfold_13.length; i+=2)
-            unfold3.putDirect(CaseUnfold_13[i], CaseUnfold_13[i + 1]);
-        Unfold3Hash = unfold3;
-    }
-    
-    static IntHash<int[]> FoldHash;
-    static IntHash<int[]> Unfold1Hash;
-    static IntArrayHash<int[]> Unfold2Hash;
-    static IntArrayHash<int[]> Unfold3Hash; 
-    
     private static final int CaseFold1_From[]  = new int[] {
         0x0041,
         0x0042,
@@ -3838,4 +3805,43 @@ public class UnicodeCaseFolds {
         {0x03c5, 0x0313, 0x0342}, {0x1f56},
         {0x03c9, 0x0342, 0x03b9}, {0x1ff7}
     };
+
+    private static IntHash<int[]> initializeFoldHash() {
+        IntHash<int[]> fold = new IntHash<int[]>(1200);
+        for (int i=0; i<CaseFold1_From.length; i++)
+            fold.putDirect(CaseFold1_From[i], CaseFold1_To[i]);
+        for (int i=0; i<CaseFold_Locale_From.length; i++) 
+            fold.putDirect(CaseFold_Locale_From[i], CaseFold_Locale_To[i]);
+        return fold;
+    }
+
+    private static IntHash<int[]> initializeUnfold1Hash() {
+        IntHash<int[]> unfold1 = new IntHash<int[]>(1000);
+        for (int i=0; i<CaseUnfold_11_From.length; i++) 
+            unfold1.putDirect(CaseUnfold_11_From[i], CaseUnfold_11_To[i]);
+        for (int i=0; i<CaseUnfold_11_Locale_From.length; i++) 
+            unfold1.putDirect(CaseUnfold_11_Locale_From[i], CaseUnfold_11_Locale_To[i]);
+        return unfold1;
+    }
+
+    private static IntArrayHash<int[]> initializeUnfold2Hash() {
+        IntArrayHash<int[]> unfold2 = new IntArrayHash<int[]>(200);
+        for (int i=0; i<CaseUnfold_12.length; i+=2)
+            unfold2.putDirect(CaseUnfold_12[i], CaseUnfold_12[i + 1]);
+        for (int i=0; i<CaseUnfold_12_Locale.length; i+=2)
+            unfold2.putDirect(CaseUnfold_12_Locale[i], CaseUnfold_12_Locale[i + 1]);
+        return unfold2;
+    }
+
+    private static IntArrayHash<int[]> initializeUnfold3Hash() {
+        IntArrayHash<int[]> unfold3 = new IntArrayHash<int[]>(30);
+        for (int i=0; i<CaseUnfold_13.length; i+=2)
+            unfold3.putDirect(CaseUnfold_13[i], CaseUnfold_13[i + 1]);
+        return unfold3;
+    }
+
+    static final IntHash<int[]> FoldHash = initializeFoldHash();
+    static final IntHash<int[]> Unfold1Hash = initializeUnfold1Hash();
+    static final IntArrayHash<int[]> Unfold2Hash = initializeUnfold2Hash();
+    static final IntArrayHash<int[]> Unfold3Hash = initializeUnfold3Hash();    
 }
diff --git a/src/org/joni/encoding/unicode/UnicodeCodeRanges.java b/src/org/joni/encoding/unicode/UnicodeCodeRanges.java
index 987199d..edf886c 100644
--- a/src/org/joni/encoding/unicode/UnicodeCodeRanges.java
+++ b/src/org/joni/encoding/unicode/UnicodeCodeRanges.java
@@ -23,158 +23,14 @@ import org.joni.Config;
 
 public class UnicodeCodeRanges {
 
-    public static void initializeCodeRanges() {
-        if (Config.USE_UNICODE_PROPERTIES) {
-            CodeRangeTable = new int[][] {
-            UnicodeCodeRanges.CR_NEWLINE,
-            UnicodeCodeRanges.CR_Alpha,
-            UnicodeCodeRanges.CR_Blank,
-            UnicodeCodeRanges.CR_Cntrl,
-            UnicodeCodeRanges.CR_Digit,
-            UnicodeCodeRanges.CR_Graph,
-            UnicodeCodeRanges.CR_Lower,
-            UnicodeCodeRanges.CR_Print,
-            UnicodeCodeRanges.CR_Punct,
-            UnicodeCodeRanges.CR_Space,
-            UnicodeCodeRanges.CR_Upper,
-            UnicodeCodeRanges.CR_XDigit,
-            UnicodeCodeRanges.CR_Word,
-            UnicodeCodeRanges.CR_Alnum,
-            UnicodeCodeRanges.CR_ASCII,
-
-                // unicode properties
-            UnicodeProperties.CR_Any,
-            UnicodeProperties.CR_Assigned,
-            UnicodeProperties.CR_C,
-            UnicodeProperties.CR_Cc,
-            UnicodeProperties.CR_Cf,
-            UnicodeProperties.CR_Cn,
-            UnicodeProperties.CR_Co,
-            UnicodeProperties.CR_Cs,
-            UnicodeProperties.CR_L,
-            UnicodeProperties.CR_Ll,
-            UnicodeProperties.CR_Lm,
-            UnicodeProperties.CR_Lo,
-            UnicodeProperties.CR_Lt,
-            UnicodeProperties.CR_Lu,
-            UnicodeProperties.CR_M,
-            UnicodeProperties.CR_Mc,
-            UnicodeProperties.CR_Me,
-            UnicodeProperties.CR_Mn,
-            UnicodeProperties.CR_N,
-            UnicodeProperties.CR_Nd,
-            UnicodeProperties.CR_Nl,
-            UnicodeProperties.CR_No,
-            UnicodeProperties.CR_P,
-            UnicodeProperties.CR_Pc,
-            UnicodeProperties.CR_Pd,
-            UnicodeProperties.CR_Pe,
-            UnicodeProperties.CR_Pf,
-            UnicodeProperties.CR_Pi,
-            UnicodeProperties.CR_Po,
-            UnicodeProperties.CR_Ps,
-            UnicodeProperties.CR_S,
-            UnicodeProperties.CR_Sc,
-            UnicodeProperties.CR_Sk,
-            UnicodeProperties.CR_Sm,
-            UnicodeProperties.CR_So,
-            UnicodeProperties.CR_Z,
-            UnicodeProperties.CR_Zl,
-            UnicodeProperties.CR_Zp,
-            UnicodeProperties.CR_Zs,
-            UnicodePropertiesScripts.CR_Arabic,
-            UnicodePropertiesScripts.CR_Armenian,
-            UnicodePropertiesScripts.CR_Bengali,
-            UnicodePropertiesScripts.CR_Bopomofo,
-            UnicodePropertiesScripts.CR_Braille,
-            UnicodePropertiesScripts.CR_Buginese,
-            UnicodePropertiesScripts.CR_Buhid,
-            UnicodePropertiesScripts.CR_Canadian_Aboriginal,
-            UnicodePropertiesScripts.CR_Cherokee,
-            UnicodePropertiesScripts.CR_Common,
-            UnicodePropertiesScripts.CR_Coptic,
-            UnicodePropertiesScripts.CR_Cypriot,
-            UnicodePropertiesScripts.CR_Cyrillic,
-            UnicodePropertiesScripts.CR_Deseret,
-            UnicodePropertiesScripts.CR_Devanagari,
-            UnicodePropertiesScripts.CR_Ethiopic,
-            UnicodePropertiesScripts.CR_Georgian,
-            UnicodePropertiesScripts.CR_Glagolitic,
-            UnicodePropertiesScripts.CR_Gothic,
-            UnicodePropertiesScripts.CR_Greek,
-            UnicodePropertiesScripts.CR_Gujarati,
-            UnicodePropertiesScripts.CR_Gurmukhi,
-            UnicodePropertiesScripts.CR_Han,
-            UnicodePropertiesScripts.CR_Hangul,
-            UnicodePropertiesScripts.CR_Hanunoo,
-            UnicodePropertiesScripts.CR_Hebrew,
-            UnicodePropertiesScripts.CR_Hiragana,
-            UnicodePropertiesScripts.CR_Inherited,
-            UnicodePropertiesScripts.CR_Kannada,
-            UnicodePropertiesScripts.CR_Katakana,
-            UnicodePropertiesScripts.CR_Kharoshthi,
-            UnicodePropertiesScripts.CR_Khmer,
-            UnicodePropertiesScripts.CR_Lao,
-            UnicodePropertiesScripts.CR_Latin,
-            UnicodePropertiesScripts.CR_Limbu,
-            UnicodePropertiesScripts.CR_Linear_B,
-            UnicodePropertiesScripts.CR_Malayalam,
-            UnicodePropertiesScripts.CR_Mongolian,
-            UnicodePropertiesScripts.CR_Myanmar,
-            UnicodePropertiesScripts.CR_New_Tai_Lue,
-            UnicodePropertiesScripts.CR_Ogham,
-            UnicodePropertiesScripts.CR_Old_Italic,
-            UnicodePropertiesScripts.CR_Old_Persian,
-            UnicodePropertiesScripts.CR_Oriya,
-            UnicodePropertiesScripts.CR_Osmanya,
-            UnicodePropertiesScripts.CR_Runic,
-            UnicodePropertiesScripts.CR_Shavian,
-            UnicodePropertiesScripts.CR_Sinhala,
-            UnicodePropertiesScripts.CR_Syloti_Nagri,
-            UnicodePropertiesScripts.CR_Syriac,
-            UnicodePropertiesScripts.CR_Tagalog,
-            UnicodePropertiesScripts.CR_Tagbanwa,
-            UnicodePropertiesScripts.CR_Tai_Le,
-            UnicodePropertiesScripts.CR_Tamil,
-            UnicodePropertiesScripts.CR_Telugu,
-            UnicodePropertiesScripts.CR_Thaana,
-            UnicodePropertiesScripts.CR_Thai,
-            UnicodePropertiesScripts.CR_Tibetan,
-            UnicodePropertiesScripts.CR_Tifinagh,
-            UnicodePropertiesScripts.CR_Ugaritic,
-            UnicodePropertiesScripts.CR_Yi,
-            };
-        } else {
-            CodeRangeTable = new int[][] {
-            UnicodeCodeRanges.CR_NEWLINE,
-            UnicodeCodeRanges.CR_Alpha,
-            UnicodeCodeRanges.CR_Blank,
-            UnicodeCodeRanges.CR_Cntrl,
-            UnicodeCodeRanges.CR_Digit,
-            UnicodeCodeRanges.CR_Graph,
-            UnicodeCodeRanges.CR_Lower,
-            UnicodeCodeRanges.CR_Print,
-            UnicodeCodeRanges.CR_Punct,
-            UnicodeCodeRanges.CR_Space,
-            UnicodeCodeRanges.CR_Upper,
-            UnicodeCodeRanges.CR_XDigit,
-            UnicodeCodeRanges.CR_Word,
-            UnicodeCodeRanges.CR_Alnum,
-            UnicodeCodeRanges.CR_ASCII,
-            };
-        }
-    }
-    
-    static int[][]CodeRangeTable;
-
     /* 'NEWLINE' */
-    static final int CR_NEWLINE[] = {
+    private static final int CR_NEWLINE[] = {
       1,
       0x000a, 0x000a
     }; /* CR_NEWLINE */
     
     /* 'Alpha': [[:Alpha:]] */
-    static final int CR_Alpha[] = {
+    private static final int CR_Alpha[] = {
       418,
       0x0041, 0x005a,
       0x0061, 0x007a,
@@ -597,7 +453,7 @@ public class UnicodeCodeRanges {
     }; /* CR_Alpha */
     
     /* 'Blank': [[:Blank:]] */
-    static final int CR_Blank[] = {
+    private static final int CR_Blank[] = {
       9,
       0x0009, 0x0009,
       0x0020, 0x0020,
@@ -611,7 +467,7 @@ public class UnicodeCodeRanges {
     }; /* CR_Blank */
     
     /* 'Cntrl': [[:Cntrl:]] */
-    static final int CR_Cntrl[] = {
+    private static final int CR_Cntrl[] = {
       19,
       0x0000, 0x001f,
       0x007f, 0x009f,
@@ -635,7 +491,7 @@ public class UnicodeCodeRanges {
     }; /* CR_Cntrl */
     
     /* 'Digit': [[:Digit:]] */
-    static final int CR_Digit[] = {
+    private static final int CR_Digit[] = {
       23,
       0x0030, 0x0039,
       0x0660, 0x0669,
@@ -663,7 +519,7 @@ public class UnicodeCodeRanges {
     }; /* CR_Digit */
 
     /* 'Graph': [[:Graph:]] */
-    static final int CR_Graph[] = {
+    private static final int CR_Graph[] = {
       424,
       0x0021, 0x007e,
       0x00a1, 0x0241,
@@ -1092,7 +948,7 @@ public class UnicodeCodeRanges {
     }; /* CR_Graph */
     
     /* 'Lower': [[:Lower:]] */
-    static final int CR_Lower[] = {
+    private static final int CR_Lower[] = {
       480,
       0x0061, 0x007a,
       0x00aa, 0x00aa,
@@ -1577,7 +1433,7 @@ public class UnicodeCodeRanges {
     }; /* CR_Lower */
 
     /* 'Print': [[:Print:]] */
-    static final int CR_Print[] = {
+    private static final int CR_Print[] = {
       423,
       0x0009, 0x000d,
       0x0020, 0x007e,
@@ -2005,7 +1861,7 @@ public class UnicodeCodeRanges {
     }; /* CR_Print */
     
     /* 'Punct': [[:Punct:]] */
-    static final int CR_Punct[] = {
+    private static final int CR_Punct[] = {
       96,
       0x0021, 0x0023,
       0x0025, 0x002a,
@@ -2106,7 +1962,7 @@ public class UnicodeCodeRanges {
     }; /* CR_Punct */
 
     /* 'Space': [[:Space:]] */
-    static final int CR_Space[] = {
+    private static final int CR_Space[] = {
       11,
       0x0009, 0x000d,
       0x0020, 0x0020,
@@ -2122,7 +1978,7 @@ public class UnicodeCodeRanges {
     }; /* CR_Space */
     
     /* 'Upper': [[:Upper:]] */
-    static final int CR_Upper[] = {
+    private static final int CR_Upper[] = {
       476,
       0x0041, 0x005a,
       0x00c0, 0x00d6,
@@ -2603,7 +2459,7 @@ public class UnicodeCodeRanges {
     }; /* CR_Upper */
     
     /* 'XDigit': [[:XDigit:]] */
-    static final int CR_XDigit[] = {
+    private static final int CR_XDigit[] = {
       3,
       0x0030, 0x0039,
       0x0041, 0x0046,
@@ -2611,7 +2467,7 @@ public class UnicodeCodeRanges {
     }; /* CR_XDigit */
 
     /* 'Word': [[:Word:]] */
-    static final int CR_Word[] = {
+    private static final int CR_Word[] = {
       464,
       0x0030, 0x0039,
       0x0041, 0x005a,
@@ -3080,7 +2936,7 @@ public class UnicodeCodeRanges {
     }; /* CR_Word */
 
     /* 'Alnum': [[:Alnum:]] */
-    static final int CR_Alnum[] = {
+    private static final int CR_Alnum[] = {
       436,
       0x0030, 0x0039,
       0x0041, 0x005a,
@@ -3521,9 +3377,146 @@ public class UnicodeCodeRanges {
     }; /* CR_Alnum */
 
     /* 'ASCII': [[:ASCII:]] */
-    static final int CR_ASCII[] = {
+    private static final int CR_ASCII[] = {
       1,
       0x0000, 0x007f
     }; /* CR_ASCII */
-    
+
+    static final int[][]CodeRangeTable = Config.USE_UNICODE_PROPERTIES ?
+            new int[][] {
+                UnicodeCodeRanges.CR_NEWLINE,
+                UnicodeCodeRanges.CR_Alpha,
+                UnicodeCodeRanges.CR_Blank,
+                UnicodeCodeRanges.CR_Cntrl,
+                UnicodeCodeRanges.CR_Digit,
+                UnicodeCodeRanges.CR_Graph,
+                UnicodeCodeRanges.CR_Lower,
+                UnicodeCodeRanges.CR_Print,
+                UnicodeCodeRanges.CR_Punct,
+                UnicodeCodeRanges.CR_Space,
+                UnicodeCodeRanges.CR_Upper,
+                UnicodeCodeRanges.CR_XDigit,
+                UnicodeCodeRanges.CR_Word,
+                UnicodeCodeRanges.CR_Alnum,
+                UnicodeCodeRanges.CR_ASCII,
+
+                // unicode properties
+                UnicodeProperties.CR_Any,
+                UnicodeProperties.CR_Assigned,
+                UnicodeProperties.CR_C,
+                UnicodeProperties.CR_Cc,
+                UnicodeProperties.CR_Cf,
+                UnicodeProperties.CR_Cn,
+                UnicodeProperties.CR_Co,
+                UnicodeProperties.CR_Cs,
+                UnicodeProperties.CR_L,
+                UnicodeProperties.CR_Ll,
+                UnicodeProperties.CR_Lm,
+                UnicodeProperties.CR_Lo,
+                UnicodeProperties.CR_Lt,
+                UnicodeProperties.CR_Lu,
+                UnicodeProperties.CR_M,
+                UnicodeProperties.CR_Mc,
+                UnicodeProperties.CR_Me,
+                UnicodeProperties.CR_Mn,
+                UnicodeProperties.CR_N,
+                UnicodeProperties.CR_Nd,
+                UnicodeProperties.CR_Nl,
+                UnicodeProperties.CR_No,
+                UnicodeProperties.CR_P,
+                UnicodeProperties.CR_Pc,
+                UnicodeProperties.CR_Pd,
+                UnicodeProperties.CR_Pe,
+                UnicodeProperties.CR_Pf,
+                UnicodeProperties.CR_Pi,
+                UnicodeProperties.CR_Po,
+                UnicodeProperties.CR_Ps,
+                UnicodeProperties.CR_S,
+                UnicodeProperties.CR_Sc,
+                UnicodeProperties.CR_Sk,
+                UnicodeProperties.CR_Sm,
+                UnicodeProperties.CR_So,
+                UnicodeProperties.CR_Z,
+                UnicodeProperties.CR_Zl,
+                UnicodeProperties.CR_Zp,
+                UnicodeProperties.CR_Zs,
+                UnicodePropertiesScripts.CR_Arabic,
+                UnicodePropertiesScripts.CR_Armenian,
+                UnicodePropertiesScripts.CR_Bengali,
+                UnicodePropertiesScripts.CR_Bopomofo,
+                UnicodePropertiesScripts.CR_Braille,
+                UnicodePropertiesScripts.CR_Buginese,
+                UnicodePropertiesScripts.CR_Buhid,
+                UnicodePropertiesScripts.CR_Canadian_Aboriginal,
+                UnicodePropertiesScripts.CR_Cherokee,
+                UnicodePropertiesScripts.CR_Common,
+                UnicodePropertiesScripts.CR_Coptic,
+                UnicodePropertiesScripts.CR_Cypriot,
+                UnicodePropertiesScripts.CR_Cyrillic,
+                UnicodePropertiesScripts.CR_Deseret,
+                UnicodePropertiesScripts.CR_Devanagari,
+                UnicodePropertiesScripts.CR_Ethiopic,
+                UnicodePropertiesScripts.CR_Georgian,
+                UnicodePropertiesScripts.CR_Glagolitic,
+                UnicodePropertiesScripts.CR_Gothic,
+                UnicodePropertiesScripts.CR_Greek,
+                UnicodePropertiesScripts.CR_Gujarati,
+                UnicodePropertiesScripts.CR_Gurmukhi,
+                UnicodePropertiesScripts.CR_Han,
+                UnicodePropertiesScripts.CR_Hangul,
+                UnicodePropertiesScripts.CR_Hanunoo,
+                UnicodePropertiesScripts.CR_Hebrew,
+                UnicodePropertiesScripts.CR_Hiragana,
+                UnicodePropertiesScripts.CR_Inherited,
+                UnicodePropertiesScripts.CR_Kannada,
+                UnicodePropertiesScripts.CR_Katakana,
+                UnicodePropertiesScripts.CR_Kharoshthi,
+                UnicodePropertiesScripts.CR_Khmer,
+                UnicodePropertiesScripts.CR_Lao,
+                UnicodePropertiesScripts.CR_Latin,
+                UnicodePropertiesScripts.CR_Limbu,
+                UnicodePropertiesScripts.CR_Linear_B,
+                UnicodePropertiesScripts.CR_Malayalam,
+                UnicodePropertiesScripts.CR_Mongolian,
+                UnicodePropertiesScripts.CR_Myanmar,
+                UnicodePropertiesScripts.CR_New_Tai_Lue,
+                UnicodePropertiesScripts.CR_Ogham,
+                UnicodePropertiesScripts.CR_Old_Italic,
+                UnicodePropertiesScripts.CR_Old_Persian,
+                UnicodePropertiesScripts.CR_Oriya,
+                UnicodePropertiesScripts.CR_Osmanya,
+                UnicodePropertiesScripts.CR_Runic,
+                UnicodePropertiesScripts.CR_Shavian,
+                UnicodePropertiesScripts.CR_Sinhala,
+                UnicodePropertiesScripts.CR_Syloti_Nagri,
+                UnicodePropertiesScripts.CR_Syriac,
+                UnicodePropertiesScripts.CR_Tagalog,
+                UnicodePropertiesScripts.CR_Tagbanwa,
+                UnicodePropertiesScripts.CR_Tai_Le,
+                UnicodePropertiesScripts.CR_Tamil,
+                UnicodePropertiesScripts.CR_Telugu,
+                UnicodePropertiesScripts.CR_Thaana,
+                UnicodePropertiesScripts.CR_Thai,
+                UnicodePropertiesScripts.CR_Tibetan,
+                UnicodePropertiesScripts.CR_Tifinagh,
+                UnicodePropertiesScripts.CR_Ugaritic,
+                UnicodePropertiesScripts.CR_Yi,
+            } :
+            new int[][] {
+                UnicodeCodeRanges.CR_NEWLINE,
+                UnicodeCodeRanges.CR_Alpha,
+                UnicodeCodeRanges.CR_Blank,
+                UnicodeCodeRanges.CR_Cntrl,
+                UnicodeCodeRanges.CR_Digit,
+                UnicodeCodeRanges.CR_Graph,
+                UnicodeCodeRanges.CR_Lower,
+                UnicodeCodeRanges.CR_Print,
+                UnicodeCodeRanges.CR_Punct,
+                UnicodeCodeRanges.CR_Space,
+                UnicodeCodeRanges.CR_Upper,
+                UnicodeCodeRanges.CR_XDigit,
+                UnicodeCodeRanges.CR_Word,
+                UnicodeCodeRanges.CR_Alnum,
+                UnicodeCodeRanges.CR_ASCII,
+           };
 }
\ No newline at end of file
diff --git a/src/org/joni/encoding/unicode/UnicodeEncoding.java b/src/org/joni/encoding/unicode/UnicodeEncoding.java
index 17d0c29..fee01f6 100644
--- a/src/org/joni/encoding/unicode/UnicodeEncoding.java
+++ b/src/org/joni/encoding/unicode/UnicodeEncoding.java
@@ -40,18 +40,14 @@ public abstract class UnicodeEncoding extends MultiByteEncoding {
     
     // onigenc_unicode_is_code_ctype
     @Override
-    public boolean isCodeCType(int code, int ctype) {
+    public final boolean isCodeCType(int code, int ctype) {
         if (Config.USE_UNICODE_PROPERTIES) {
             if (ctype <= CharacterType.MAX_STD_CTYPE && code < 256)
                 return isCodeCTypeInternal(code, ctype); 
         } else {
             if (code < 256) return isCodeCTypeInternal(code, ctype);
         }
-        
-        if (UnicodeCodeRanges.CodeRangeTable == null) synchronized(getClass()) {
-            UnicodeCodeRanges.initializeCodeRanges();
-        }
-        
+
         if (ctype > UnicodeCodeRanges.CodeRangeTable.length) throw new InternalError(ErrorMessages.ERR_TYPE_BUG);
 
         return CodeRangeBuffer.isInCodeRange(UnicodeCodeRanges.CodeRangeTable[ctype], code);
@@ -60,10 +56,6 @@ public abstract class UnicodeEncoding extends MultiByteEncoding {
     
     // onigenc_unicode_ctype_code_range
     protected final int[]ctypeCodeRange(int ctype) {
-        if (UnicodeCodeRanges.CodeRangeTable == null) synchronized(getClass()) {
-            UnicodeCodeRanges.initializeCodeRanges();
-        }
-        
         if (ctype >= UnicodeCodeRanges.CodeRangeTable.length) throw new InternalError(ErrorMessages.ERR_TYPE_BUG);
         
         return UnicodeCodeRanges.CodeRangeTable[ctype];
@@ -84,11 +76,7 @@ public abstract class UnicodeEncoding extends MultiByteEncoding {
             if (len >= PROPERTY_NAME_MAX_SIZE) throw new ValueException(ErrorMessages.ERR_INVALID_CHAR_PROPERTY_NAME, name, p, end);
             p_ += length(name[p_]);
         }
-        
-        if (UnicodeCTypeNames.CTypeNameHash == null) synchronized(getClass()) {
-            UnicodeCTypeNames.initializeCTypeNameTable();
-        }
-        
+
         Integer ctype = UnicodeCTypeNames.CTypeNameHash.get(buf, 0, len);
         if (ctype == null) throw new ValueException(ErrorMessages.ERR_INVALID_CHAR_PROPERTY_NAME, name, p, end);
         return ctype;       
@@ -97,10 +85,6 @@ public abstract class UnicodeEncoding extends MultiByteEncoding {
     // onigenc_unicode_mbc_case_fold
     @Override
     public int mbcCaseFold(int flag, byte[]bytes, IntHolder pp, int end, byte[]fold) {
-        if (UnicodeCaseFolds.FoldHash == null) synchronized (getClass()) {
-            UnicodeCaseFolds.initializeCaseFoldTables();
-        }
-        
         int p = pp.value;
         int foldP = 0;
         
@@ -251,9 +235,6 @@ public abstract class UnicodeEncoding extends MultiByteEncoding {
     // onigenc_unicode_get_case_fold_codes_by_str
     @Override
     public CaseFoldCodeItem[]caseFoldCodesByString(int flag, byte[]bytes, int p, int end) {
-        if (UnicodeCaseFolds.FoldHash == null) synchronized (getClass()) {
-            UnicodeCaseFolds.initializeCaseFoldTables();
-        }
         int code = mbcToCode(bytes, p, end);
         int len = length(bytes[p]);
         
@@ -450,5 +431,4 @@ public abstract class UnicodeEncoding extends MultiByteEncoding {
           0x30e2, 0x30e2, 0x30e2, 0x30e2, 0x30e2, 0x30e2, 0x30e2, 0x00a0,
           0x30e2, 0x30e2, 0x30e2, 0x30e2, 0x30e2, 0x30e2, 0x30e2, 0x30e2
     };      
-    
 }
diff --git a/src/org/joni/encoding/unicode/UnicodeProperties.java b/src/org/joni/encoding/unicode/UnicodeProperties.java
index 8b6516a..3a3a3b9 100644
--- a/src/org/joni/encoding/unicode/UnicodeProperties.java
+++ b/src/org/joni/encoding/unicode/UnicodeProperties.java
@@ -19,17 +19,19 @@
  */
 package org.joni.encoding.unicode;
 
+import org.joni.Config;
+
 public interface UnicodeProperties {
     // #ifdef USE_UNICODE_PROPERTIES
 
     /* 'Any': - */
-    static final int CR_Any[] = {
+    static final int CR_Any[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       1,
       0x0000, 0x10ffff
-    }; /* CR_Any */
+    } : null; /* CR_Any */
 
     /* 'Assigned': - */
-    static final int CR_Assigned[] = {
+    static final int CR_Assigned[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       420,
       0x0000, 0x0241,
       0x0250, 0x036f,
@@ -451,10 +453,10 @@ public interface UnicodeProperties {
       0xe0100, 0xe01ef,
       0xf0000, 0xffffd,
       0x100000, 0x10fffd
-    }; /* CR_Assigned */
+    } : null; /* CR_Assigned */
 
     /* 'C': Major Category */
-    static final int CR_C[] = {
+    static final int CR_C[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       422,
       0x0000, 0x001f,
       0x007f, 0x009f,
@@ -878,17 +880,17 @@ public interface UnicodeProperties {
       0x2a6d7, 0x2f7ff,
       0x2fa1e, 0xe00ff,
       0xe01f0, 0x10ffff
-    }; /* CR_C */
+    } : null; /* CR_C */
 
     /* 'Cc': General Category */
-    static final int CR_Cc[] = {
+    static final int CR_Cc[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       2,
       0x0000, 0x001f,
       0x007f, 0x009f
-    }; /* CR_Cc */
+    } : null; /* CR_Cc */
 
     /* 'Cf': General Category */
-    static final int CR_Cf[] = {
+    static final int CR_Cf[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       14,
       0x00ad, 0x00ad,
       0x0600, 0x0603,
@@ -904,10 +906,10 @@ public interface UnicodeProperties {
       0x1d173, 0x1d17a,
       0xe0001, 0xe0001,
       0xe0020, 0xe007f
-    }; /* CR_Cf */
+    } : null; /* CR_Cf */
 
     /* 'Cn': General Category */
-    static final int CR_Cn[] = {
+    static final int CR_Cn[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       420,
       0x0242, 0x024f,
       0x0370, 0x0373,
@@ -1329,24 +1331,24 @@ public interface UnicodeProperties {
       0xe01f0, 0xeffff,
       0xffffe, 0xfffff,
       0x10fffe, 0x10ffff
-    }; /* CR_Cn */
+    } : null; /* CR_Cn */
 
     /* 'Co': General Category */
-    static final int CR_Co[] = {
+    static final int CR_Co[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       3,
       0xe000, 0xf8ff,
       0xf0000, 0xffffd,
       0x100000, 0x10fffd
-    }; /* CR_Co */
+    } : null; /* CR_Co */
 
     /* 'Cs': General Category */
-    static final int CR_Cs[] = {
+    static final int CR_Cs[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       1,
       0xd800, 0xdfff
-    }; /* CR_Cs */
+    } : null; /* CR_Cs */
 
     /* 'L': Major Category */
-    static final int CR_L[] = {
+    static final int CR_L[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       347,
       0x0041, 0x005a,
       0x0061, 0x007a,
@@ -1695,10 +1697,10 @@ public interface UnicodeProperties {
       0x1d7c4, 0x1d7c9,
       0x20000, 0x2a6d6,
       0x2f800, 0x2fa1d
-    }; /* CR_L */
+    } : null; /* CR_L */
 
     /* 'Ll': General Category */
-    static final int CR_Ll[] = {
+    static final int CR_Ll[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       480,
       0x0061, 0x007a,
       0x00aa, 0x00aa,
@@ -2180,10 +2182,10 @@ public interface UnicodeProperties {
       0x1d78a, 0x1d78f,
       0x1d7aa, 0x1d7c2,
       0x1d7c4, 0x1d7c9
-    }; /* CR_Ll */
+    } : null; /* CR_Ll */
 
     /* 'Lm': General Category */
-    static final int CR_Lm[] = {
+    static final int CR_Lm[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       26,
       0x02b0, 0x02c1,
       0x02c6, 0x02d1,
@@ -2211,10 +2213,10 @@ public interface UnicodeProperties {
       0xa015, 0xa015,
       0xff70, 0xff70,
       0xff9e, 0xff9f
-    }; /* CR_Lm */
+    } : null; /* CR_Lm */
 
     /* 'Lo': General Category */
-    static final int CR_Lo[] = {
+    static final int CR_Lo[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       245,
       0x01bb, 0x01bb,
       0x01c0, 0x01c3,
@@ -2461,10 +2463,10 @@ public interface UnicodeProperties {
       0x10a19, 0x10a33,
       0x20000, 0x2a6d6,
       0x2f800, 0x2fa1d
-    }; /* CR_Lo */
+    } : null; /* CR_Lo */
 
     /* 'Lt': General Category */
-    static final int CR_Lt[] = {
+    static final int CR_Lt[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       10,
       0x01c5, 0x01c5,
       0x01c8, 0x01c8,
@@ -2476,10 +2478,10 @@ public interface UnicodeProperties {
       0x1fbc, 0x1fbc,
       0x1fcc, 0x1fcc,
       0x1ffc, 0x1ffc
-    }; /* CR_Lt */
+    } : null; /* CR_Lt */
 
     /* 'Lu': General Category */
-    static final int CR_Lu[] = {
+    static final int CR_Lu[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       476,
       0x0041, 0x005a,
       0x00c0, 0x00d6,
@@ -2957,10 +2959,10 @@ public interface UnicodeProperties {
       0x1d71c, 0x1d734,
       0x1d756, 0x1d76e,
       0x1d790, 0x1d7a8
-    }; /* CR_Lu */
+    } : null; /* CR_Lu */
 
     /* 'M': Major Category */
-    static final int CR_M[] = {
+    static final int CR_M[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       133,
       0x0300, 0x036f,
       0x0483, 0x0486,
@@ -3095,10 +3097,10 @@ public interface UnicodeProperties {
       0x1d1aa, 0x1d1ad,
       0x1d242, 0x1d244,
       0xe0100, 0xe01ef
-    }; /* CR_M */
+    } : null; /* CR_M */
 
     /* 'Mc': General Category */
-    static final int CR_Mc[] = {
+    static final int CR_Mc[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       63,
       0x0903, 0x0903,
       0x093e, 0x0940,
@@ -3163,19 +3165,19 @@ public interface UnicodeProperties {
       0xa827, 0xa827,
       0x1d165, 0x1d166,
       0x1d16d, 0x1d172
-    }; /* CR_Mc */
+    } : null; /* CR_Mc */
 
     /* 'Me': General Category */
-    static final int CR_Me[] = {
+    static final int CR_Me[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       4,
       0x0488, 0x0489,
       0x06de, 0x06de,
       0x20dd, 0x20e0,
       0x20e2, 0x20e4
-    }; /* CR_Me */
+    } : null; /* CR_Me */
 
     /* 'Mn': General Category */
-    static final int CR_Mn[] = {
+    static final int CR_Mn[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       124,
       0x0300, 0x036f,
       0x0483, 0x0486,
@@ -3301,10 +3303,10 @@ public interface UnicodeProperties {
       0x1d1aa, 0x1d1ad,
       0x1d242, 0x1d244,
       0xe0100, 0xe01ef
-    }; /* CR_Mn */
+    } : null; /* CR_Mn */
 
     /* 'N': Major Category */
-    static final int CR_N[] = {
+    static final int CR_N[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       53,
       0x0030, 0x0039,
       0x00b2, 0x00b3,
@@ -3359,10 +3361,10 @@ public interface UnicodeProperties {
       0x104a0, 0x104a9,
       0x10a40, 0x10a47,
       0x1d7ce, 0x1d7ff
-    }; /* CR_N */
+    } : null; /* CR_N */
 
     /* 'Nd': General Category */
-    static final int CR_Nd[] = {
+    static final int CR_Nd[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       23,
       0x0030, 0x0039,
       0x0660, 0x0669,
@@ -3387,10 +3389,10 @@ public interface UnicodeProperties {
       0xff10, 0xff19,
       0x104a0, 0x104a9,
       0x1d7ce, 0x1d7ff
-    }; /* CR_Nd */
+    } : null; /* CR_Nd */
 
     /* 'Nl': General Category */
-    static final int CR_Nl[] = {
+    static final int CR_Nl[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       8,
       0x16ee, 0x16f0,
       0x2160, 0x2183,
@@ -3400,10 +3402,10 @@ public interface UnicodeProperties {
       0x10140, 0x10174,
       0x1034a, 0x1034a,
       0x103d1, 0x103d5
-    }; /* CR_Nl */
+    } : null; /* CR_Nl */
 
     /* 'No': General Category */
-    static final int CR_No[] = {
+    static final int CR_No[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       26,
       0x00b2, 0x00b3,
       0x00b9, 0x00b9,
@@ -3431,10 +3433,10 @@ public interface UnicodeProperties {
       0x1018a, 0x1018a,
       0x10320, 0x10323,
       0x10a40, 0x10a47
-    }; /* CR_No */
+    } : null; /* CR_No */
 
     /* 'P': Major Category */
-    static final int CR_P[] = {
+    static final int CR_P[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       96,
       0x0021, 0x0023,
       0x0025, 0x002a,
@@ -3532,10 +3534,10 @@ public interface UnicodeProperties {
       0x10100, 0x10101,
       0x1039f, 0x1039f,
       0x10a50, 0x10a58
-    }; /* CR_P */
+    } : null; /* CR_P */
 
     /* 'Pc': General Category */
-    static final int CR_Pc[] = {
+    static final int CR_Pc[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       6,
       0x005f, 0x005f,
       0x203f, 0x2040,
@@ -3543,10 +3545,10 @@ public interface UnicodeProperties {
       0xfe33, 0xfe34,
       0xfe4d, 0xfe4f,
       0xff3f, 0xff3f
-    }; /* CR_Pc */
+    } : null; /* CR_Pc */
 
     /* 'Pd': General Category */
-    static final int CR_Pd[] = {
+    static final int CR_Pd[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       12,
       0x002d, 0x002d,
       0x058a, 0x058a,
@@ -3560,10 +3562,10 @@ public interface UnicodeProperties {
       0xfe58, 0xfe58,
       0xfe63, 0xfe63,
       0xff0d, 0xff0d
-    }; /* CR_Pd */
+    } : null; /* CR_Pd */
 
     /* 'Pe': General Category */
-    static final int CR_Pe[] = {
+    static final int CR_Pe[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       65,
       0x0029, 0x0029,
       0x005d, 0x005d,
@@ -3630,10 +3632,10 @@ public interface UnicodeProperties {
       0xff5d, 0xff5d,
       0xff60, 0xff60,
       0xff63, 0xff63
-    }; /* CR_Pe */
+    } : null; /* CR_Pe */
 
     /* 'Pf': General Category */
-    static final int CR_Pf[] = {
+    static final int CR_Pf[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       9,
       0x00bb, 0x00bb,
       0x2019, 0x2019,
@@ -3644,10 +3646,10 @@ public interface UnicodeProperties {
       0x2e0a, 0x2e0a,
       0x2e0d, 0x2e0d,
       0x2e1d, 0x2e1d
-    }; /* CR_Pf */
+    } : null; /* CR_Pf */
 
     /* 'Pi': General Category */
-    static final int CR_Pi[] = {
+    static final int CR_Pi[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       10,
       0x00ab, 0x00ab,
       0x2018, 0x2018,
@@ -3659,10 +3661,10 @@ public interface UnicodeProperties {
       0x2e09, 0x2e09,
       0x2e0c, 0x2e0c,
       0x2e1c, 0x2e1c
-    }; /* CR_Pi */
+    } : null; /* CR_Pi */
 
     /* 'Po': General Category */
-    static final int CR_Po[] = {
+    static final int CR_Po[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       88,
       0x0021, 0x0023,
       0x0025, 0x0027,
@@ -3752,10 +3754,10 @@ public interface UnicodeProperties {
       0x10100, 0x10101,
       0x1039f, 0x1039f,
       0x10a50, 0x10a58
-    }; /* CR_Po */
+    } : null; /* CR_Po */
 
     /* 'Ps': General Category */
-    static final int CR_Ps[] = {
+    static final int CR_Ps[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       67,
       0x0028, 0x0028,
       0x005b, 0x005b,
@@ -3824,10 +3826,10 @@ public interface UnicodeProperties {
       0xff5b, 0xff5b,
       0xff5f, 0xff5f,
       0xff62, 0xff62
-    }; /* CR_Ps */
+    } : null; /* CR_Ps */
 
     /* 'S': Major Category */
-    static final int CR_S[] = {
+    static final int CR_S[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       162,
       0x0024, 0x0024,
       0x002b, 0x002b,
@@ -3991,10 +3993,10 @@ public interface UnicodeProperties {
       0x1d789, 0x1d789,
       0x1d7a9, 0x1d7a9,
       0x1d7c3, 0x1d7c3
-    }; /* CR_S */
+    } : null; /* CR_S */
 
     /* 'Sc': General Category */
-    static final int CR_Sc[] = {
+    static final int CR_Sc[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       14,
       0x0024, 0x0024,
       0x00a2, 0x00a5,
@@ -4010,10 +4012,10 @@ public interface UnicodeProperties {
       0xff04, 0xff04,
       0xffe0, 0xffe1,
       0xffe5, 0xffe6
-    }; /* CR_Sc */
+    } : null; /* CR_Sc */
 
     /* 'Sk': General Category */
-    static final int CR_Sk[] = {
+    static final int CR_Sk[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       23,
       0x005e, 0x005e,
       0x0060, 0x0060,
@@ -4038,10 +4040,10 @@ public interface UnicodeProperties {
       0xff3e, 0xff3e,
       0xff40, 0xff40,
       0xffe3, 0xffe3
-    }; /* CR_Sk */
+    } : null; /* CR_Sk */
 
     /* 'Sm': General Category */
-    static final int CR_Sm[] = {
+    static final int CR_Sm[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       59,
       0x002b, 0x002b,
       0x003c, 0x003e,
@@ -4102,10 +4104,10 @@ public interface UnicodeProperties {
       0x1d789, 0x1d789,
       0x1d7a9, 0x1d7a9,
       0x1d7c3, 0x1d7c3
-    }; /* CR_Sm */
+    } : null; /* CR_Sm */
 
     /* 'So': General Category */
-    static final int CR_So[] = {
+    static final int CR_So[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       120,
       0x00a6, 0x00a7,
       0x00a9, 0x00a9,
@@ -4227,10 +4229,10 @@ public interface UnicodeProperties {
       0x1d200, 0x1d241,
       0x1d245, 0x1d245,
       0x1d300, 0x1d356
-    }; /* CR_So */
+    } : null; /* CR_So */
 
     /* 'Z': Major Category */
-    static final int CR_Z[] = {
+    static final int CR_Z[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       9,
       0x0020, 0x0020,
       0x00a0, 0x00a0,
@@ -4241,22 +4243,22 @@ public interface UnicodeProperties {
       0x202f, 0x202f,
       0x205f, 0x205f,
       0x3000, 0x3000
-    }; /* CR_Z */
+    } : null; /* CR_Z */
 
     /* 'Zl': General Category */
-    static final int CR_Zl[] = {
+    static final int CR_Zl[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       1,
       0x2028, 0x2028
-    }; /* CR_Zl */
+    } : null; /* CR_Zl */
 
     /* 'Zp': General Category */
-    static final int CR_Zp[] = {
+    static final int CR_Zp[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       1,
       0x2029, 0x2029
-    }; /* CR_Zp */
+    } : null; /* CR_Zp */
 
     /* 'Zs': General Category */
-    static final int CR_Zs[] = {
+    static final int CR_Zs[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       8,
       0x0020, 0x0020,
       0x00a0, 0x00a0,
@@ -4266,5 +4268,5 @@ public interface UnicodeProperties {
       0x202f, 0x202f,
       0x205f, 0x205f,
       0x3000, 0x3000
-    }; /* CR_Zs */
+    } : null; /* CR_Zs */
 }
diff --git a/src/org/joni/encoding/unicode/UnicodePropertiesScripts.java b/src/org/joni/encoding/unicode/UnicodePropertiesScripts.java
index f68b74d..841d06f 100644
--- a/src/org/joni/encoding/unicode/UnicodePropertiesScripts.java
+++ b/src/org/joni/encoding/unicode/UnicodePropertiesScripts.java
@@ -19,9 +19,11 @@
  */
 package org.joni.encoding.unicode;
 
+import org.joni.Config;
+
 public interface UnicodePropertiesScripts {
     /* 'Arabic': Script */
-    static final int CR_Arabic[] = {
+    static final int CR_Arabic[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       17,
       0x060b, 0x060b,
       0x060d, 0x0615,
@@ -40,20 +42,20 @@ public interface UnicodePropertiesScripts {
       0xfdf0, 0xfdfc,
       0xfe70, 0xfe74,
       0xfe76, 0xfefc
-    }; /* CR_Arabic */
+    } : null; /* CR_Arabic */
 
     /* 'Armenian': Script */
-    static final int CR_Armenian[] = {
+    static final int CR_Armenian[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       5,
       0x0531, 0x0556,
       0x0559, 0x055f,
       0x0561, 0x0587,
       0x058a, 0x058a,
       0xfb13, 0xfb17
-    }; /* CR_Armenian */
+    } : null; /* CR_Armenian */
 
     /* 'Bengali': Script */
-    static final int CR_Bengali[] = {
+    static final int CR_Bengali[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       14,
       0x0981, 0x0983,
       0x0985, 0x098c,
@@ -69,48 +71,48 @@ public interface UnicodePropertiesScripts {
       0x09dc, 0x09dd,
       0x09df, 0x09e3,
       0x09e6, 0x09fa
-    }; /* CR_Bengali */
+    } : null; /* CR_Bengali */
 
     /* 'Bopomofo': Script */
-    static final int CR_Bopomofo[] = {
+    static final int CR_Bopomofo[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       2,
       0x3105, 0x312c,
       0x31a0, 0x31b7
-    }; /* CR_Bopomofo */
+    } : null; /* CR_Bopomofo */
 
     /* 'Braille': Script */
-    static final int CR_Braille[] = {
+    static final int CR_Braille[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       1,
       0x2800, 0x28ff
-    }; /* CR_Braille */
+    } : null; /* CR_Braille */
 
     /* 'Buginese': Script */
-    static final int CR_Buginese[] = {
+    static final int CR_Buginese[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       2,
       0x1a00, 0x1a1b,
       0x1a1e, 0x1a1f
-    }; /* CR_Buginese */
+    } : null; /* CR_Buginese */
 
     /* 'Buhid': Script */
-    static final int CR_Buhid[] = {
+    static final int CR_Buhid[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       1,
       0x1740, 0x1753
-    }; /* CR_Buhid */
+    } : null; /* CR_Buhid */
 
     /* 'Canadian_Aboriginal': Script */
-    static final int CR_Canadian_Aboriginal[] = {
+    static final int CR_Canadian_Aboriginal[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       1,
       0x1401, 0x1676
-    }; /* CR_Canadian_Aboriginal */
+    } : null; /* CR_Canadian_Aboriginal */
 
     /* 'Cherokee': Script */
-    static final int CR_Cherokee[] = {
+    static final int CR_Cherokee[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       1,
       0x13a0, 0x13f4
-    }; /* CR_Cherokee */
+    } : null; /* CR_Cherokee */
 
     /* 'Common': Script */
-    static final int CR_Common[] = {
+    static final int CR_Common[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       126,
       0x0000, 0x0040,
       0x005b, 0x0060,
@@ -238,18 +240,18 @@ public interface UnicodePropertiesScripts {
       0xe0020, 0xe007f,
       0xf0000, 0xffffd,
       0x100000, 0x10fffd
-    }; /* CR_Common */
+    } : null; /* CR_Common */
 
     /* 'Coptic': Script */
-    static final int CR_Coptic[] = {
+    static final int CR_Coptic[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       3,
       0x03e2, 0x03ef,
       0x2c80, 0x2cea,
       0x2cf9, 0x2cff
-    }; /* CR_Coptic */
+    } : null; /* CR_Coptic */
 
     /* 'Cypriot': Script */
-    static final int CR_Cypriot[] = {
+    static final int CR_Cypriot[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       6,
       0x10800, 0x10805,
       0x10808, 0x10808,
@@ -257,10 +259,10 @@ public interface UnicodePropertiesScripts {
       0x10837, 0x10838,
       0x1083c, 0x1083c,
       0x1083f, 0x1083f
-    }; /* CR_Cypriot */
+    } : null; /* CR_Cypriot */
 
     /* 'Cyrillic': Script */
-    static final int CR_Cyrillic[] = {
+    static final int CR_Cyrillic[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       6,
       0x0400, 0x0486,
       0x0488, 0x04ce,
@@ -268,16 +270,16 @@ public interface UnicodePropertiesScripts {
       0x0500, 0x050f,
       0x1d2b, 0x1d2b,
       0x1d78, 0x1d78
-    }; /* CR_Cyrillic */
+    } : null; /* CR_Cyrillic */
 
     /* 'Deseret': Script */
-    static final int CR_Deseret[] = {
+    static final int CR_Deseret[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       1,
       0x10400, 0x1044f
-    }; /* CR_Deseret */
+    } : null; /* CR_Deseret */
 
     /* 'Devanagari': Script */
-    static final int CR_Devanagari[] = {
+    static final int CR_Devanagari[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       6,
       0x0901, 0x0939,
       0x093c, 0x094d,
@@ -285,10 +287,10 @@ public interface UnicodePropertiesScripts {
       0x0958, 0x0963,
       0x0966, 0x096f,
       0x097d, 0x097d
-    }; /* CR_Devanagari */
+    } : null; /* CR_Devanagari */
 
     /* 'Ethiopic': Script */
-    static final int CR_Ethiopic[] = {
+    static final int CR_Ethiopic[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       27,
       0x1200, 0x1248,
       0x124a, 0x124d,
@@ -317,32 +319,32 @@ public interface UnicodePropertiesScripts {
       0x2dc8, 0x2dce,
       0x2dd0, 0x2dd6,
       0x2dd8, 0x2dde
-    }; /* CR_Ethiopic */
+    } : null; /* CR_Ethiopic */
 
     /* 'Georgian': Script */
-    static final int CR_Georgian[] = {
+    static final int CR_Georgian[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       4,
       0x10a0, 0x10c5,
       0x10d0, 0x10fa,
       0x10fc, 0x10fc,
       0x2d00, 0x2d25
-    }; /* CR_Georgian */
+    } : null; /* CR_Georgian */
 
     /* 'Glagolitic': Script */
-    static final int CR_Glagolitic[] = {
+    static final int CR_Glagolitic[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       2,
       0x2c00, 0x2c2e,
       0x2c30, 0x2c5e
-    }; /* CR_Glagolitic */
+    } : null; /* CR_Glagolitic */
 
     /* 'Gothic': Script */
-    static final int CR_Gothic[] = {
+    static final int CR_Gothic[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       1,
       0x10330, 0x1034a
-    }; /* CR_Gothic */
+    } : null; /* CR_Gothic */
 
     /* 'Greek': Script */
-    static final int CR_Greek[] = {
+    static final int CR_Greek[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       31,
       0x0374, 0x0375,
       0x037a, 0x037a,
@@ -375,10 +377,10 @@ public interface UnicodePropertiesScripts {
       0x2126, 0x2126,
       0x10140, 0x1018a,
       0x1d200, 0x1d245
-    }; /* CR_Greek */
+    } : null; /* CR_Greek */
 
     /* 'Gujarati': Script */
-    static final int CR_Gujarati[] = {
+    static final int CR_Gujarati[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       14,
       0x0a81, 0x0a83,
       0x0a85, 0x0a8d,
@@ -394,10 +396,10 @@ public interface UnicodePropertiesScripts {
       0x0ae0, 0x0ae3,
       0x0ae6, 0x0aef,
       0x0af1, 0x0af1
-    }; /* CR_Gujarati */
+    } : null; /* CR_Gujarati */
 
     /* 'Gurmukhi': Script */
-    static final int CR_Gurmukhi[] = {
+    static final int CR_Gurmukhi[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       15,
       0x0a01, 0x0a03,
       0x0a05, 0x0a0a,
@@ -414,10 +416,10 @@ public interface UnicodePropertiesScripts {
       0x0a59, 0x0a5c,
       0x0a5e, 0x0a5e,
       0x0a66, 0x0a74
-    }; /* CR_Gurmukhi */
+    } : null; /* CR_Gurmukhi */
 
     /* 'Han': Script */
-    static final int CR_Han[] = {
+    static final int CR_Han[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       14,
       0x2e80, 0x2e99,
       0x2e9b, 0x2ef3,
@@ -433,10 +435,10 @@ public interface UnicodePropertiesScripts {
       0xfa70, 0xfad9,
       0x20000, 0x2a6d6,
       0x2f800, 0x2fa1d
-    }; /* CR_Han */
+    } : null; /* CR_Han */
 
     /* 'Hangul': Script */
-    static final int CR_Hangul[] = {
+    static final int CR_Hangul[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       12,
       0x1100, 0x1159,
       0x115f, 0x11a2,
@@ -450,16 +452,16 @@ public interface UnicodePropertiesScripts {
       0xffca, 0xffcf,
       0xffd2, 0xffd7,
       0xffda, 0xffdc
-    }; /* CR_Hangul */
+    } : null; /* CR_Hangul */
 
     /* 'Hanunoo': Script */
-    static final int CR_Hanunoo[] = {
+    static final int CR_Hanunoo[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       1,
       0x1720, 0x1734
-    }; /* CR_Hanunoo */
+    } : null; /* CR_Hanunoo */
 
     /* 'Hebrew': Script */
-    static final int CR_Hebrew[] = {
+    static final int CR_Hebrew[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       10,
       0x0591, 0x05b9,
       0x05bb, 0x05c7,
@@ -471,17 +473,17 @@ public interface UnicodePropertiesScripts {
       0xfb40, 0xfb41,
       0xfb43, 0xfb44,
       0xfb46, 0xfb4f
-    }; /* CR_Hebrew */
+    } : null; /* CR_Hebrew */
 
     /* 'Hiragana': Script */
-    static final int CR_Hiragana[] = {
+    static final int CR_Hiragana[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       2,
       0x3041, 0x3096,
       0x309d, 0x309f
-    }; /* CR_Hiragana */
+    } : null; /* CR_Hiragana */
 
     /* 'Inherited': Script */
-    static final int CR_Inherited[] = {
+    static final int CR_Inherited[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       15,
       0x0300, 0x036f,
       0x064b, 0x0655,
@@ -498,10 +500,10 @@ public interface UnicodePropertiesScripts {
       0x1d185, 0x1d18b,
       0x1d1aa, 0x1d1ad,
       0xe0100, 0xe01ef
-    }; /* CR_Inherited */
+    } : null; /* CR_Inherited */
 
     /* 'Kannada': Script */
-    static final int CR_Kannada[] = {
+    static final int CR_Kannada[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       13,
       0x0c82, 0x0c83,
       0x0c85, 0x0c8c,
@@ -516,20 +518,20 @@ public interface UnicodePropertiesScripts {
       0x0cde, 0x0cde,
       0x0ce0, 0x0ce1,
       0x0ce6, 0x0cef
-    }; /* CR_Kannada */
+    } : null; /* CR_Kannada */
 
     /* 'Katakana': Script */
-    static final int CR_Katakana[] = {
+    static final int CR_Katakana[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       5,
       0x30a1, 0x30fa,
       0x30fd, 0x30ff,
       0x31f0, 0x31ff,
       0xff66, 0xff6f,
       0xff71, 0xff9d
-    }; /* CR_Katakana */
+    } : null; /* CR_Katakana */
 
     /* 'Kharoshthi': Script */
-    static final int CR_Kharoshthi[] = {
+    static final int CR_Kharoshthi[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       8,
       0x10a00, 0x10a03,
       0x10a05, 0x10a06,
@@ -539,19 +541,19 @@ public interface UnicodePropertiesScripts {
       0x10a38, 0x10a3a,
       0x10a3f, 0x10a47,
       0x10a50, 0x10a58
-    }; /* CR_Kharoshthi */
+    } : null; /* CR_Kharoshthi */
 
     /* 'Khmer': Script */
-    static final int CR_Khmer[] = {
+    static final int CR_Khmer[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       4,
       0x1780, 0x17dd,
       0x17e0, 0x17e9,
       0x17f0, 0x17f9,
       0x19e0, 0x19ff
-    }; /* CR_Khmer */
+    } : null; /* CR_Khmer */
 
     /* 'Lao': Script */
-    static final int CR_Lao[] = {
+    static final int CR_Lao[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       18,
       0x0e81, 0x0e82,
       0x0e84, 0x0e84,
@@ -571,10 +573,10 @@ public interface UnicodePropertiesScripts {
       0x0ec8, 0x0ecd,
       0x0ed0, 0x0ed9,
       0x0edc, 0x0edd
-    }; /* CR_Lao */
+    } : null; /* CR_Lao */
 
     /* 'Latin': Script */
-    static final int CR_Latin[] = {
+    static final int CR_Latin[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       23,
       0x0041, 0x005a,
       0x0061, 0x007a,
@@ -599,20 +601,20 @@ public interface UnicodePropertiesScripts {
       0xfb00, 0xfb06,
       0xff21, 0xff3a,
       0xff41, 0xff5a
-    }; /* CR_Latin */
+    } : null; /* CR_Latin */
 
     /* 'Limbu': Script */
-    static final int CR_Limbu[] = {
+    static final int CR_Limbu[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       5,
       0x1900, 0x191c,
       0x1920, 0x192b,
       0x1930, 0x193b,
       0x1940, 0x1940,
       0x1944, 0x194f
-    }; /* CR_Limbu */
+    } : null; /* CR_Limbu */
 
     /* 'Linear_B': Script */
-    static final int CR_Linear_B[] = {
+    static final int CR_Linear_B[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       7,
       0x10000, 0x1000b,
       0x1000d, 0x10026,
@@ -621,10 +623,10 @@ public interface UnicodePropertiesScripts {
       0x1003f, 0x1004d,
       0x10050, 0x1005d,
       0x10080, 0x100fa
-    }; /* CR_Linear_B */
+    } : null; /* CR_Linear_B */
 
     /* 'Malayalam': Script */
-    static final int CR_Malayalam[] = {
+    static final int CR_Malayalam[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       11,
       0x0d02, 0x0d03,
       0x0d05, 0x0d0c,
@@ -637,19 +639,19 @@ public interface UnicodePropertiesScripts {
       0x0d57, 0x0d57,
       0x0d60, 0x0d61,
       0x0d66, 0x0d6f
-    }; /* CR_Malayalam */
+    } : null; /* CR_Malayalam */
 
     /* 'Mongolian': Script */
-    static final int CR_Mongolian[] = {
+    static final int CR_Mongolian[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       4,
       0x1800, 0x180e,
       0x1810, 0x1819,
       0x1820, 0x1877,
       0x1880, 0x18a9
-    }; /* CR_Mongolian */
+    } : null; /* CR_Mongolian */
 
     /* 'Myanmar': Script */
-    static final int CR_Myanmar[] = {
+    static final int CR_Myanmar[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       6,
       0x1000, 0x1021,
       0x1023, 0x1027,
@@ -657,39 +659,39 @@ public interface UnicodePropertiesScripts {
       0x102c, 0x1032,
       0x1036, 0x1039,
       0x1040, 0x1059
-    }; /* CR_Myanmar */
+    } : null; /* CR_Myanmar */
 
     /* 'New_Tai_Lue': Script */
-    static final int CR_New_Tai_Lue[] = {
+    static final int CR_New_Tai_Lue[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       4,
       0x1980, 0x19a9,
       0x19b0, 0x19c9,
       0x19d0, 0x19d9,
       0x19de, 0x19df
-    }; /* CR_New_Tai_Lue */
+    } : null; /* CR_New_Tai_Lue */
 
     /* 'Ogham': Script */
-    static final int CR_Ogham[] = {
+    static final int CR_Ogham[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       1,
       0x1680, 0x169c
-    }; /* CR_Ogham */
+    } : null; /* CR_Ogham */
 
     /* 'Old_Italic': Script */
-    static final int CR_Old_Italic[] = {
+    static final int CR_Old_Italic[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       2,
       0x10300, 0x1031e,
       0x10320, 0x10323
-    }; /* CR_Old_Italic */
+    } : null; /* CR_Old_Italic */
 
     /* 'Old_Persian': Script */
-    static final int CR_Old_Persian[] = {
+    static final int CR_Old_Persian[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       2,
       0x103a0, 0x103c3,
       0x103c8, 0x103d5
-    }; /* CR_Old_Persian */
+    } : null; /* CR_Old_Persian */
 
     /* 'Oriya': Script */
-    static final int CR_Oriya[] = {
+    static final int CR_Oriya[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       14,
       0x0b01, 0x0b03,
       0x0b05, 0x0b0c,
@@ -705,30 +707,30 @@ public interface UnicodePropertiesScripts {
       0x0b5c, 0x0b5d,
       0x0b5f, 0x0b61,
       0x0b66, 0x0b71
-    }; /* CR_Oriya */
+    } : null; /* CR_Oriya */
 
     /* 'Osmanya': Script */
-    static final int CR_Osmanya[] = {
+    static final int CR_Osmanya[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       2,
       0x10480, 0x1049d,
       0x104a0, 0x104a9
-    }; /* CR_Osmanya */
+    } : null; /* CR_Osmanya */
 
     /* 'Runic': Script */
-    static final int CR_Runic[] = {
+    static final int CR_Runic[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       2,
       0x16a0, 0x16ea,
       0x16ee, 0x16f0
-    }; /* CR_Runic */
+    } : null; /* CR_Runic */
 
     /* 'Shavian': Script */
-    static final int CR_Shavian[] = {
+    static final int CR_Shavian[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       1,
       0x10450, 0x1047f
-    }; /* CR_Shavian */
+    } : null; /* CR_Shavian */
 
     /* 'Sinhala': Script */
-    static final int CR_Sinhala[] = {
+    static final int CR_Sinhala[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       11,
       0x0d82, 0x0d83,
       0x0d85, 0x0d96,
@@ -741,46 +743,46 @@ public interface UnicodePropertiesScripts {
       0x0dd6, 0x0dd6,
       0x0dd8, 0x0ddf,
       0x0df2, 0x0df4
-    }; /* CR_Sinhala */
+    } : null; /* CR_Sinhala */
 
     /* 'Syloti_Nagri': Script */
-    static final int CR_Syloti_Nagri[] = {
+    static final int CR_Syloti_Nagri[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       1,
       0xa800, 0xa82b
-    }; /* CR_Syloti_Nagri */
+    } : null; /* CR_Syloti_Nagri */
 
     /* 'Syriac': Script */
-    static final int CR_Syriac[] = {
+    static final int CR_Syriac[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       3,
       0x0700, 0x070d,
       0x070f, 0x074a,
       0x074d, 0x074f
-    }; /* CR_Syriac */
+    } : null; /* CR_Syriac */
 
     /* 'Tagalog': Script */
-    static final int CR_Tagalog[] = {
+    static final int CR_Tagalog[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       2,
       0x1700, 0x170c,
       0x170e, 0x1714
-    }; /* CR_Tagalog */
+    } : null; /* CR_Tagalog */
 
     /* 'Tagbanwa': Script */
-    static final int CR_Tagbanwa[] = {
+    static final int CR_Tagbanwa[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       3,
       0x1760, 0x176c,
       0x176e, 0x1770,
       0x1772, 0x1773
-    }; /* CR_Tagbanwa */
+    } : null; /* CR_Tagbanwa */
 
     /* 'Tai_Le': Script */
-    static final int CR_Tai_Le[] = {
+    static final int CR_Tai_Le[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       2,
       0x1950, 0x196d,
       0x1970, 0x1974
-    }; /* CR_Tai_Le */
+    } : null; /* CR_Tai_Le */
 
     /* 'Tamil': Script */
-    static final int CR_Tamil[] = {
+    static final int CR_Tamil[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       15,
       0x0b82, 0x0b83,
       0x0b85, 0x0b8a,
@@ -797,10 +799,10 @@ public interface UnicodePropertiesScripts {
       0x0bca, 0x0bcd,
       0x0bd7, 0x0bd7,
       0x0be6, 0x0bfa
-    }; /* CR_Tamil */
+    } : null; /* CR_Tamil */
 
     /* 'Telugu': Script */
-    static final int CR_Telugu[] = {
+    static final int CR_Telugu[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       12,
       0x0c01, 0x0c03,
       0x0c05, 0x0c0c,
@@ -814,23 +816,23 @@ public interface UnicodePropertiesScripts {
       0x0c55, 0x0c56,
       0x0c60, 0x0c61,
       0x0c66, 0x0c6f
-    }; /* CR_Telugu */
+    } : null; /* CR_Telugu */
 
     /* 'Thaana': Script */
-    static final int CR_Thaana[] = {
+    static final int CR_Thaana[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       1,
       0x0780, 0x07b1
-    }; /* CR_Thaana */
+    } : null; /* CR_Thaana */
 
     /* 'Thai': Script */
-    static final int CR_Thai[] = {
+    static final int CR_Thai[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       2,
       0x0e01, 0x0e3a,
       0x0e40, 0x0e5b
-    }; /* CR_Thai */
+    } : null; /* CR_Thai */
 
     /* 'Tibetan': Script */
-    static final int CR_Tibetan[] = {
+    static final int CR_Tibetan[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       7,
       0x0f00, 0x0f47,
       0x0f49, 0x0f6a,
@@ -839,28 +841,28 @@ public interface UnicodePropertiesScripts {
       0x0f99, 0x0fbc,
       0x0fbe, 0x0fcc,
       0x0fcf, 0x0fd1
-    }; /* CR_Tibetan */
+    } : null; /* CR_Tibetan */
 
     /* 'Tifinagh': Script */
-    static final int CR_Tifinagh[] = {
+    static final int CR_Tifinagh[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       2,
       0x2d30, 0x2d65,
       0x2d6f, 0x2d6f
-    }; /* CR_Tifinagh */
+    } : null; /* CR_Tifinagh */
 
     /* 'Ugaritic': Script */
-    static final int CR_Ugaritic[] = {
+    static final int CR_Ugaritic[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       2,
       0x10380, 0x1039d,
       0x1039f, 0x1039f
-    }; /* CR_Ugaritic */
+    } : null; /* CR_Ugaritic */
 
     /* 'Yi': Script */
-    static final int CR_Yi[] = {
+    static final int CR_Yi[] = Config.USE_UNICODE_PROPERTIES ? new int[]{
       2,
       0xa000, 0xa48c,
       0xa490, 0xa4c6
-    }; /* CR_Yi */
+    } : null; /* CR_Yi */
 
     // #endif /* USE_UNICODE_PROPERTIES */
 }

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/jruby-joni.git



More information about the pkg-java-commits mailing list