]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/chr/CHRCodeGenerator.java
(refs #7250) Cleaning up CHR code generation
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / internal / codegen / chr / CHRCodeGenerator.java
index bd7059ee209a15f90c8985b1e89a70e52d82d22e..3a249c5deabaf38228a9fc4b268ecf5f01272832 100644 (file)
-package org.simantics.scl.compiler.internal.codegen.chr;\r
-\r
-import java.util.ArrayList;\r
-\r
-import org.cojen.classfile.TypeDesc;\r
-import org.objectweb.asm.Label;\r
-import org.objectweb.asm.Opcodes;\r
-import org.simantics.scl.compiler.elaboration.chr.CHRRuleset;\r
-import org.simantics.scl.compiler.elaboration.chr.plan.PrioritizedPlan;\r
-import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint;\r
-import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint.IndexInfo;\r
-import org.simantics.scl.compiler.internal.codegen.references.BoundVar;\r
-import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator;\r
-import org.simantics.scl.compiler.internal.codegen.utils.ClassBuilder;\r
-import org.simantics.scl.compiler.internal.codegen.utils.CodeBuilderUtils;\r
-import org.simantics.scl.compiler.internal.codegen.utils.Constants;\r
-import org.simantics.scl.compiler.internal.codegen.utils.LocalVariable;\r
-import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;\r
-import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilderBase;\r
-import org.simantics.scl.compiler.internal.codegen.utils.ModuleBuilder;\r
-\r
-import gnu.trove.list.array.TIntArrayList;\r
-import gnu.trove.set.hash.THashSet;\r
-\r
-public class CHRCodeGenerator {\r
-    \r
-    public static final TypeDesc FACT_ID_TYPE = TypeDesc.INT;\r
-    public static final String CHRHashIndex_name = "org/simantics/scl/runtime/chr/CHRHashIndex";\r
-    public static final TypeDesc CHRHashIndex = TypeDesc.forClass(CHRHashIndex_name);\r
-    public static final String FactActivationQueue_name = "org/simantics/scl/runtime/chr/FactActivationQueue";\r
-    public static final TypeDesc FactActivationQueue = TypeDesc.forClass(FactActivationQueue_name);\r
-    public static final String Fact_name = "org/simantics/scl/runtime/chr/Fact";\r
-    public static final TypeDesc Fact = TypeDesc.forClass(Fact_name);\r
-    public static final String QUEUE = "queue";\r
-    \r
-    private static class StoreInitialization {\r
-        final int access;\r
-        final String fieldName;\r
-        final TypeDesc fieldType;\r
-        final String className;\r
-        public StoreInitialization(int access, String fieldName, TypeDesc fieldType, String className) {\r
-            this.access = access;\r
-            this.fieldName = fieldName;\r
-            this.fieldType = fieldType;\r
-            this.className = className;\r
-        }\r
-    }\r
-    \r
-    public static void generateStore(ModuleBuilder moduleBuilder, CHRRuleset ruleset) {\r
-        ClassBuilder storeClassBuilder = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, ruleset.storeClassName, "java/lang/Object");\r
-        if(ruleset.parameters == null)\r
-            ruleset.parameters = new BoundVar[0];\r
-        ruleset.parameterTypeDescs = moduleBuilder.getJavaTypeTranslator().getTypeDescs(ruleset.parameters); \r
-        \r
-        ArrayList<StoreInitialization> hashIndexInitializations = new ArrayList<>();\r
-        for(CHRConstraint constraint : ruleset.constraints)\r
-            generateFact(storeClassBuilder, constraint, hashIndexInitializations);\r
-        \r
-        // Fields\r
-        for(int i=0;i<ruleset.parameterTypeDescs.length;++i)\r
-            storeClassBuilder.addField(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "p" + i, ruleset.parameterTypeDescs[i]);\r
-        storeClassBuilder.addField(Opcodes.ACC_PUBLIC, "currentId", FACT_ID_TYPE);\r
-        for(StoreInitialization ini : hashIndexInitializations)\r
-            storeClassBuilder.addField(ini.access, ini.fieldName, ini.fieldType);\r
-        storeClassBuilder.addField(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, QUEUE, FactActivationQueue);\r
-        \r
-        // Constructors\r
-        \r
-        {\r
-            MethodBuilderBase mb = storeClassBuilder.addConstructor(Opcodes.ACC_PUBLIC, ruleset.parameterTypeDescs);\r
-            mb.loadThis();\r
-            mb.invokeConstructor(storeClassBuilder.getSuperClassName(), Constants.EMPTY_TYPEDESC_ARRAY);\r
-            for(int i=0;i<ruleset.parameterTypeDescs.length;++i) {\r
-                mb.loadThis();\r
-                mb.loadLocal(mb.getParameter(i));\r
-                mb.storeField(ruleset.storeClassName, "p" + i, ruleset.parameterTypeDescs[i]);\r
-            }\r
-            mb.loadThis();\r
-            mb.loadConstant(1);\r
-            mb.storeField(storeClassBuilder.getClassName(), "currentId", TypeDesc.INT);\r
-            for(StoreInitialization ini : hashIndexInitializations) {\r
-                mb.loadThis();\r
-                mb.newObject(ini.className);\r
-                mb.dup();\r
-                mb.invokeConstructor(ini.className, Constants.EMPTY_TYPEDESC_ARRAY);\r
-                mb.storeField(ruleset.storeClassName, ini.fieldName, ini.fieldType);\r
-            }\r
-            {\r
-                mb.loadThis();\r
-                mb.newObject(FactActivationQueue_name);\r
-                mb.dup();\r
-                mb.loadConstant(ruleset.priorityCount);\r
-                mb.invokeConstructor(FactActivationQueue_name, new TypeDesc[] {TypeDesc.INT});\r
-                mb.storeField(ruleset.storeClassName, QUEUE, FactActivationQueue);\r
-            }\r
-            mb.returnVoid();\r
-            mb.finish();\r
-        }\r
-        \r
-        // Activate\r
-        \r
-        {\r
-            MethodBuilderBase mb = storeClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "activate", TypeDesc.VOID, new TypeDesc[] {TypeDesc.INT});\r
-            mb.loadThis();\r
-            mb.loadField(ruleset.storeClassName, QUEUE, FactActivationQueue);\r
-            mb.loadThis();\r
-            mb.loadLocal(mb.getParameter(0));\r
-            mb.invokeVirtual(FactActivationQueue_name, "activate", TypeDesc.VOID, new TypeDesc[] {TypeDesc.OBJECT, TypeDesc.INT});\r
-            mb.returnVoid();\r
-            mb.finish();\r
-        }\r
-        \r
-        moduleBuilder.addClass(storeClassBuilder);\r
-    }\r
-    \r
-    private static void generateFact(ClassBuilder storeClassBuilder, CHRConstraint constraint, ArrayList<StoreInitialization> hashIndexInitializations) {\r
-        CHRRuleset ruleset = constraint.parentRuleset;\r
-        boolean supportsRemoval = constraint.mayBeRemoved();\r
-        \r
-        ModuleBuilder moduleBuilder = storeClassBuilder.getModuleBuilder();\r
-        JavaTypeTranslator jtt = moduleBuilder.getJavaTypeTranslator();\r
-        TypeDesc storeTypeDesc = storeClassBuilder.getType();\r
-        TypeDesc[] storeTypeDescArray = new TypeDesc[] { storeTypeDesc };\r
-        \r
-        String factClassName = storeClassBuilder.getClassName() + "$" + constraint.name;\r
-        TypeDesc factTypeDesc = TypeDesc.forClass(factClassName);\r
-        ClassBuilder factClassBuilder = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, factClassName, "java/lang/Object", Fact_name);\r
-        \r
-        // Fields\r
-        \r
-        /* public int id;\r
-           public int c0; // key\r
-           public int c1;\r
-           public ExampleFact bfPrev;\r
-           public ExampleFact bfNext;\r
-         */\r
-        TypeDesc[] parameterTypeDescs = jtt.toTypeDescs(constraint.parameterTypes);\r
-        factClassBuilder.addField(Opcodes.ACC_PUBLIC, "id", FACT_ID_TYPE);\r
-        for(int i=0;i<constraint.parameterTypes.length;++i)\r
-            factClassBuilder.addField(Opcodes.ACC_PUBLIC, "c" + i, parameterTypeDescs[i]);\r
-        \r
-        for(IndexInfo indexInfo : constraint.getIndices()) {\r
-            if(supportsRemoval)\r
-                factClassBuilder.addField(Opcodes.ACC_PUBLIC, indexInfo.indexName + "Prev", factTypeDesc);\r
-            factClassBuilder.addField(Opcodes.ACC_PUBLIC, indexInfo.indexName + "Next", factTypeDesc);\r
-            \r
-            String hashIndexField = constraint.name + "$" + indexInfo.indexName;\r
-            if(indexInfo.indexMask == 0) {\r
-                // If there are now bound parameters, use just a direct reference to a fact\r
-                storeClassBuilder.addField(Opcodes.ACC_PUBLIC, hashIndexField, factTypeDesc);\r
-            }\r
-            else {\r
-                ClassBuilder hashClass = generateSpecializedHashIndex(storeClassBuilder, constraint, indexInfo, factTypeDesc, factClassName);\r
-                moduleBuilder.addClass(hashClass);\r
-                hashIndexInitializations.add(new StoreInitialization(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, hashIndexField, CHRHashIndex, hashClass.getClassName()));\r
-            }\r
-        }\r
-        \r
-        // Method: get\r
-        \r
-        hashIndexInitializations.add(new StoreInitialization(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, constraint.name + "$temp", factTypeDesc, factClassName));\r
-\r
-        \r
-        {\r
-            /*\r
-            public ExampleFact ExampleFact$bf(int c0) {\r
-                ExampleFact$temp.c0 = c0;\r
-                return (ExampleFact)ExampleFact_bfIndex.getEqual(ExampleFact$temp);\r
-            }\r
-            */\r
-            for(IndexInfo indexInfo : constraint.getIndices()) {\r
-                /*if(indexInfo.indexMask == 0) {\r
-                    MethodBuilderBase mb = storeClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, constraint.name + "$" + indexInfo.indexName,\r
-                            factTypeDesc, Constants.EMPTY_TYPEDESC_ARRAY);\r
-                    mb.loadThis();\r
-                    mb.loadField(storeClassBuilder.getClassName(), constraint.name + "$" + indexInfo.indexName, factTypeDesc);\r
-                    mb.returnValue(factTypeDesc);\r
-                    mb.finish();\r
-                }*/\r
-                if(indexInfo.indexMask != 0) {\r
-                    ArrayList<TypeDesc> getParameterTypeDescs = new ArrayList<TypeDesc>(constraint.parameterTypes.length);\r
-                    for(int i=0;i<constraint.parameterTypes.length;++i)\r
-                        if(((indexInfo.indexMask>>i)&1)==1)\r
-                            getParameterTypeDescs.add(parameterTypeDescs[i]);\r
-                    MethodBuilderBase mb = storeClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, constraint.name + "$" + indexInfo.indexName, factTypeDesc,\r
-                            getParameterTypeDescs.toArray(new TypeDesc[getParameterTypeDescs.size()]));\r
-                    mb.loadThis();\r
-                    mb.loadField(storeClassBuilder.getClassName(), constraint.name + "$temp", factTypeDesc);\r
-                    LocalVariable tempFactVar = mb.createLocalVariable("temp", factTypeDesc);\r
-                    mb.storeLocal(tempFactVar);\r
-                    int parameterId=0;\r
-                    for(int i=0;i<constraint.parameterTypes.length;++i)\r
-                        if(((indexInfo.indexMask>>i)&1)==1) {\r
-                            mb.loadLocal(tempFactVar);\r
-                            mb.loadLocal(mb.getParameter(parameterId++));\r
-                            mb.storeField(factClassName, "c"+i, parameterTypeDescs[i]);\r
-                        }\r
-\r
-                    mb.loadThis();\r
-                    mb.loadField(storeClassBuilder.getClassName(), constraint.name + "$" + indexInfo.indexName, CHRHashIndex);\r
-                    mb.loadLocal(tempFactVar);\r
-                    mb.invokeVirtual(CHRHashIndex_name, supportsRemoval ? "getEqual" : "getEqualNoRemovals", TypeDesc.OBJECT, Constants.OBJECTS[1]);\r
-                    mb.checkCast(factTypeDesc);\r
-                \r
-                    mb.returnValue(factTypeDesc);\r
-                    mb.finish();\r
-                }\r
-            }   \r
-        }\r
-        \r
-        // Method: add\r
-        \r
-        {\r
-            MethodBuilderBase mb = factClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "add", TypeDesc.VOID, storeTypeDescArray);\r
-            LocalVariable storeParameter = mb.getParameter(0);\r
-            for(IndexInfo indexInfo : constraint.getIndices()) {\r
-                String linkedListPrev = indexInfo.indexName + "Prev";\r
-                String linkedListNext = indexInfo.indexName + "Next";\r
-                String storeHashIndexName = constraint.name + "$" + indexInfo.indexName;\r
-                \r
-                // public void add(ExampleStore store) {\r
-                //     bfNext = (ExampleFact)store.ExampleFact_bfIndex.addFreshAndReturnOld(this);\r
-                //     if(bfNext != null)\r
-                //         bfNext.bfPrev = this;\r
-                // }\r
-                \r
-                if(indexInfo.indexMask == 0) {\r
-                    mb.loadThis();\r
-                    mb.loadLocal(storeParameter);\r
-                    mb.loadField(storeClassBuilder.getClassName(), storeHashIndexName, factTypeDesc);\r
-                    if(supportsRemoval)\r
-                        mb.dupX1();\r
-                    mb.storeField(factClassName, linkedListNext, factTypeDesc);\r
-                    if(supportsRemoval) {\r
-                        Label cont = new Label();\r
-                        mb.ifNullBranch(cont, true);\r
-                        mb.loadThis();\r
-                        mb.loadField(factClassName, linkedListNext, factTypeDesc);\r
-                        mb.loadThis();\r
-                        mb.storeField(factClassName, linkedListPrev, factTypeDesc);\r
-                        mb.setLocation(cont);\r
-                    }\r
-                    mb.loadLocal(storeParameter);\r
-                    mb.loadThis();\r
-                    mb.storeField(storeClassBuilder.getClassName(), storeHashIndexName, factTypeDesc);\r
-                }\r
-                else {\r
-                    // bfNext = (ExampleFact)store.ExampleFact_bfIndex.addFreshAndReturnOld(this);\r
-                    mb.loadThis();\r
-                    mb.loadLocal(storeParameter);\r
-                    mb.loadField(storeClassBuilder.getClassName(), storeHashIndexName, CHRHashIndex);\r
-                    mb.loadThis();\r
-                    mb.invokeVirtual(CHRHashIndex_name, supportsRemoval ? "addFreshAndReturnOld" : "addFreshAndReturnOld", TypeDesc.OBJECT, Constants.OBJECTS[1]);\r
-                    mb.checkCast(factTypeDesc);\r
-                    if(supportsRemoval)\r
-                        mb.dupX1();\r
-                    mb.storeField(factClassName, linkedListNext, factTypeDesc);\r
-                    // leaves bfNext on the stack\r
-\r
-                    //if(bfNext != null)\r
-                    //    bfNext.bfPrev = this;\r
-                    if(supportsRemoval) {\r
-                        Label cont = new Label();\r
-                        mb.ifNullBranch(cont, true);\r
-                        mb.loadThis();\r
-                        mb.loadField(factClassName, linkedListNext, factTypeDesc);\r
-                        mb.loadThis();\r
-                        mb.storeField(factClassName, linkedListPrev, factTypeDesc);\r
-                        mb.setLocation(cont);\r
-                    }\r
-                }\r
-            }            \r
-            if(!constraint.isPassive()) {\r
-                mb.loadLocal(storeParameter);\r
-                mb.loadField(storeClassBuilder.getClassName(), QUEUE, FactActivationQueue);\r
-                mb.loadConstant(constraint.getMinimumPriority());\r
-                mb.loadThis();\r
-                mb.invokeVirtual(FactActivationQueue_name, "add", TypeDesc.VOID, new TypeDesc[] {TypeDesc.INT, Fact});\r
-            }\r
-            mb.returnVoid();\r
-            mb.finish();\r
-        }\r
-        \r
-        // Method: remove\r
-\r
-        if(supportsRemoval) {\r
-            // public void remove(ExampleStore store) {\r
-            //     if(bfPrev == null) {\r
-            //         if(bfNext == null)\r
-            //             store.ExampleFact_bfIndex.removeKnownToExistKey(this);\r
-            //         else {\r
-            //             bfNext.bfPrev = null;\r
-            //             store.ExampleFact_bfIndex.replaceKnownToExistKey(this, bfNext);\r
-            //         }\r
-            //     }\r
-            //     else {\r
-            //         bfPrev.bfNext = bfNext;\r
-            //         if(bfNext != null)\r
-            //             bfNext.bfPrev = bfPrev;\r
-            //     }\r
-            // }\r
-            \r
-            MethodBuilderBase mb = factClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "remove", TypeDesc.VOID, storeTypeDescArray);\r
-            LocalVariable storeParameter = mb.getParameter(0);\r
-            for(IndexInfo indexInfo : constraint.getIndices()) {\r
-                String linkedListPrev = indexInfo.indexName + "Prev";\r
-                String linkedListNext = indexInfo.indexName + "Next";\r
-                String storeHashIndexName = constraint.name + "$" + indexInfo.indexName;\r
-                \r
-                Label nextIndex = mb.createLabel();\r
-                \r
-                // if(bfPrev == null) {\r
-                mb.loadThis();\r
-                mb.loadField(factClassName, linkedListPrev, factTypeDesc);\r
-                Label else1 = new Label();\r
-                mb.ifNullBranch(else1, false);\r
-                \r
-                //     if(bfNext == null)\r
-                mb.loadThis();\r
-                mb.loadField(factClassName, linkedListNext, factTypeDesc);\r
-                Label else2 = new Label();\r
-                mb.ifNullBranch(else2, false);\r
-                \r
-                //         store.ExampleFact_bfIndex.removeKnownToExistKey(this);\r
-                if(indexInfo.indexMask == 0) {\r
-                    mb.loadLocal(storeParameter);\r
-                    mb.loadNull();\r
-                    mb.storeField(storeClassBuilder.getClassName(), storeHashIndexName, factTypeDesc);\r
-                }\r
-                else {\r
-                    mb.loadLocal(storeParameter);\r
-                    mb.loadField(storeClassBuilder.getClassName(), storeHashIndexName, CHRHashIndex);\r
-                    mb.loadThis();\r
-                    mb.invokeVirtual(CHRHashIndex_name, "removeKnownToExistKey", TypeDesc.VOID, Constants.OBJECTS[1]);\r
-                }\r
-                mb.branch(nextIndex);\r
-                \r
-                //     else {\r
-                mb.setLocation(else2);\r
-                //         bfNext.bfPrev = null;\r
-                mb.loadThis();\r
-                mb.loadField(factClassName, linkedListNext, factTypeDesc);\r
-                mb.loadNull();\r
-                mb.storeField(factClassName, linkedListPrev, factTypeDesc);\r
-                //         store.ExampleFact_bfIndex.replaceKnownToExistKey(this, bfNext);\r
-                if(indexInfo.indexMask == 0) {\r
-                    mb.loadLocal(storeParameter);\r
-                    mb.loadThis();\r
-                    mb.loadField(factClassName, linkedListNext, factTypeDesc);\r
-                    mb.storeField(storeClassBuilder.getClassName(), storeHashIndexName, factTypeDesc);\r
-                }\r
-                else {\r
-                    mb.loadLocal(storeParameter);\r
-                    mb.loadField(storeClassBuilder.getClassName(), storeHashIndexName, CHRHashIndex);\r
-                    mb.loadThis();\r
-                    mb.loadThis();\r
-                    mb.loadField(factClassName, linkedListNext, factTypeDesc);\r
-                    mb.invokeVirtual(CHRHashIndex_name, "replaceKnownToExistKey", TypeDesc.VOID, Constants.OBJECTS[2]);\r
-                }\r
-                mb.branch(nextIndex);\r
-                //     }\r
-                \r
-                // else {\r
-                mb.setLocation(else1);\r
-                //     bfPrev.bfNext = bfNext;\r
-                mb.loadThis();\r
-                mb.loadField(factClassName, linkedListPrev, factTypeDesc);\r
-                mb.loadThis();\r
-                mb.loadField(factClassName, linkedListNext, factTypeDesc);\r
-                mb.storeField(factClassName, linkedListNext, factTypeDesc);\r
-                //     if(bfNext != null)\r
-                mb.loadThis();\r
-                mb.loadField(factClassName, linkedListNext, factTypeDesc);\r
-                Label else3 = new Label();\r
-                mb.ifNullBranch(else3, true);\r
-                //         bfNext.bfPrev = bfPrev;\r
-                mb.loadThis();\r
-                mb.loadField(factClassName, linkedListNext, factTypeDesc);\r
-                mb.loadThis();\r
-                mb.loadField(factClassName, linkedListPrev, factTypeDesc);\r
-                mb.storeField(factClassName, linkedListPrev, factTypeDesc);\r
-                mb.setLocation(else3);\r
-                mb.branch(nextIndex);\r
-                // }\r
-                \r
-                mb.setLocation(nextIndex);\r
-            }\r
-            mb.loadThis();\r
-            mb.loadConstant(-1);\r
-            mb.storeField(factClassName, "id", FACT_ID_TYPE);\r
-            mb.returnVoid();\r
-            mb.finish();\r
-        }\r
-        \r
-        // Method: isAlive\r
-\r
-        {\r
-            // @Override\r
-            // public boolean isAlive() {\r
-            //     return id >= 0;\r
-            // }\r
-            \r
-            MethodBuilderBase mb = factClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "isAlive", TypeDesc.BOOLEAN, Constants.EMPTY_TYPEDESC_ARRAY);\r
-            if(supportsRemoval) {\r
-                mb.loadThis();\r
-                mb.loadField(factClassName, "id", FACT_ID_TYPE);\r
-\r
-                Label thenBranch = mb.createLabel();\r
-                mb.ifZeroComparisonBranch(thenBranch, "<");\r
-                mb.loadConstant(true);\r
-                mb.returnValue(TypeDesc.BOOLEAN);\r
-\r
-                mb.setLocation(thenBranch);\r
-                mb.loadConstant(false);\r
-                mb.returnValue(TypeDesc.BOOLEAN);\r
-            }\r
-            else {\r
-                mb.loadConstant(true);\r
-                mb.returnValue(TypeDesc.BOOLEAN);\r
-            }\r
-            mb.finish();\r
-        }\r
-        \r
-        // activate parts\r
-        \r
-        THashSet<BoundVar> usedParameters = new THashSet<BoundVar>();\r
-        for(int i=0;i<constraint.plans.size();++i) {\r
-            PrioritizedPlan plan = constraint.plans.get(i);\r
-            MethodBuilder mb = factClassBuilder.addMethod(Opcodes.ACC_PUBLIC, "activate" + i, TypeDesc.BOOLEAN, new TypeDesc[] {storeTypeDesc});\r
-            LocalVariable storeVar = mb.getParameter(0);\r
-            LocalVariable factVar = new LocalVariable(0, factTypeDesc);\r
-            mb.setLocalVariable(ruleset.this_, storeVar);\r
-            mb.setLocalVariable(plan.implementation.getParameters()[0], factVar);\r
-            \r
-            // Set closure parameters\r
-            usedParameters.clear();\r
-            plan.implementation.forValRefs(valRef -> {\r
-                if(valRef.getBinding() instanceof BoundVar)\r
-                    usedParameters.add((BoundVar)valRef.getBinding());\r
-            });\r
-            for(int j=0;j<ruleset.parameters.length;++j) {\r
-                BoundVar parameter = ruleset.parameters[j];\r
-                if(!usedParameters.contains(parameter))\r
-                    continue;\r
-                mb.loadLocal(storeVar);\r
-                mb.loadField(storeClassBuilder.getClassName(), "p"+j, ruleset.parameterTypeDescs[j]);\r
-                mb.store(parameter);\r
-            }\r
-            \r
-            // Generate code\r
-            //System.out.println("=== activate" + i + " ==========================================================");\r
-            //System.out.println(plan.implementation);\r
-            plan.implementation.markGenerateOnFly();\r
-            plan.implementation.generateCodeWithAlreadyPreparedParameters(mb);\r
-            mb.finish();\r
-        }\r
-        \r
-        // Method: activate\r
-\r
-        {\r
-            // @Override\r
-            // public int activate(Object context, int priority) {\r
-            //     return -1;\r
-            // }\r
-            \r
-            MethodBuilderBase mb = factClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "activate", TypeDesc.INT, new TypeDesc[] {TypeDesc.OBJECT, TypeDesc.INT});\r
-            Label defaultLabel = mb.createLabel();\r
-            \r
-            if(!constraint.isPassive()) {\r
-                // Check if the fact is alive\r
-                mb.loadThis();\r
-                mb.loadField(factClassName, "id", TypeDesc.INT);\r
-                mb.ifZeroComparisonBranch(defaultLabel, "<");\r
-\r
-                mb.loadLocal(mb.getParameter(0));\r
-                mb.checkCast(storeTypeDesc);\r
-                LocalVariable storeVariable = new LocalVariable(1, storeTypeDesc);\r
-                mb.storeLocal(storeVariable);\r
-\r
-                TIntArrayList priorities = new TIntArrayList(constraint.plans.size());\r
-                ArrayList<Label> labels = new ArrayList<Label>();\r
-                int lastPriority = -1;\r
-                for(PrioritizedPlan plan : constraint.plans)\r
-                    if(plan.priority != lastPriority) {\r
-                        priorities.add(plan.priority);\r
-                        labels.add(mb.createLabel());\r
-                        lastPriority = plan.priority;\r
-                    }\r
-\r
-                mb.loadLocal(mb.getParameter(1));\r
-                mb.switch_(priorities.toArray(), labels.toArray(new Label[labels.size()]), defaultLabel);\r
-                int labelId = -1;\r
-                for(int i=0;i<constraint.plans.size();++i) {\r
-                    PrioritizedPlan plan = constraint.plans.get(i);\r
-                    if(labelId == -1 || plan.priority != priorities.get(labelId)) {\r
-                        if(labelId >= 0) {\r
-                            mb.loadConstant(plan.priority);\r
-                            mb.returnValue(TypeDesc.INT);\r
-                        }\r
-                        ++labelId;\r
-                        mb.setLocation(labels.get(labelId));\r
-                    }\r
-                    mb.loadThis();\r
-                    mb.loadLocal(storeVariable);\r
-                    mb.invokeVirtual(factClassName, "activate" + i, TypeDesc.BOOLEAN, new TypeDesc[] {storeTypeDesc});\r
-                    mb.ifZeroComparisonBranch(defaultLabel, "==");\r
-                }\r
-                mb.setLocation(defaultLabel);\r
-            }\r
-            mb.loadConstant(-1);\r
-            mb.returnValue(TypeDesc.INT);\r
-            mb.finish();\r
-        }\r
-        \r
-        // Constructors\r
-        \r
-        {\r
-            // public ExampleFact(int id, int c0, int c1) {\r
-            //     this.id = id;            \r
-            //     this.c0 = c0;\r
-            //     this.c1 = c1;\r
-            // }\r
-            \r
-            TypeDesc[] constructorParameters = new TypeDesc[parameterTypeDescs.length+1];\r
-            constructorParameters[0] = FACT_ID_TYPE;\r
-            for(int i=0;i<parameterTypeDescs.length;++i)\r
-                constructorParameters[i+1] = parameterTypeDescs[i];\r
-            MethodBuilderBase mb = factClassBuilder.addConstructor(Opcodes.ACC_PUBLIC, constructorParameters);\r
-            mb.loadThis();\r
-            mb.invokeConstructor(factClassBuilder.getSuperClassName(), Constants.EMPTY_TYPEDESC_ARRAY);\r
-            mb.loadThis();\r
-            mb.loadLocal(mb.getParameter(0));\r
-            mb.storeField(factClassName, "id", FACT_ID_TYPE);\r
-            for(int i=0;i<constraint.parameterTypes.length;++i) {\r
-                mb.loadThis();\r
-                mb.loadLocal(mb.getParameter(i+1));\r
-                mb.storeField(factClassName, "c" + i, parameterTypeDescs[i]);\r
-            }\r
-            mb.returnVoid();\r
-            mb.finish();\r
-        }\r
-        factClassBuilder.addDefaultConstructor();\r
-        \r
-        moduleBuilder.addClass(factClassBuilder);\r
-    }\r
-\r
-    private static ClassBuilder generateSpecializedHashIndex(ClassBuilder storeClassBuilder, CHRConstraint constraint, IndexInfo indexInfo, TypeDesc factClassTypeDesc, String factClassName) {\r
-        // new CHRHashIndex() {\r
-        //     @Override\r
-        //     protected boolean keyEquals(Object a, Object b) {\r
-        //         return ((ExampleFact)a).c0 == ((ExampleFact)b).c0;\r
-        //     }\r
-        //     @Override\r
-        //     protected int keyHashCode(Object key) {\r
-        //         return ((ExampleFact)key).c0;\r
-        //     }\r
-        // }\r
-\r
-        ModuleBuilder moduleBuilder = storeClassBuilder.getModuleBuilder();\r
-        JavaTypeTranslator jtt = moduleBuilder.getJavaTypeTranslator();\r
-        \r
-        String hashIndexClassName = factClassName + "$" + indexInfo.indexName; \r
-        ClassBuilder hashIndexClassBuilder = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, hashIndexClassName, "org/simantics/scl/runtime/chr/CHRHashIndex");\r
-        \r
-        // Method: keyEquals\r
-\r
-        {\r
-\r
-            // @Override\r
-            // protected boolean keyEquals(Object a, Object b) {\r
-            //     return ((ExampleFact)a).c0 == ((ExampleFact)b).c0;\r
-            // }\r
-            \r
-            MethodBuilderBase mb = hashIndexClassBuilder.addMethodBase(Opcodes.ACC_PROTECTED, "keyEquals", TypeDesc.BOOLEAN, Constants.OBJECTS[2]);\r
-            mb.loadLocal(mb.getParameter(0));\r
-            mb.checkCast(factClassTypeDesc);\r
-            LocalVariable aVar = mb.createLocalVariable("a", factClassTypeDesc);\r
-            mb.storeLocal(aVar);\r
-            \r
-            mb.loadLocal(mb.getParameter(1));\r
-            mb.checkCast(factClassTypeDesc);\r
-            LocalVariable bVar = mb.createLocalVariable("b", factClassTypeDesc);\r
-            mb.storeLocal(bVar);\r
-\r
-            Label failure = mb.createLabel();\r
-            \r
-            int curMask = indexInfo.indexMask;\r
-            for(int i=0;i<constraint.parameterTypes.length;++i,curMask>>=1)\r
-                if((curMask&1) == 1) {\r
-                    TypeDesc fieldTypeDesc = jtt.toTypeDesc(constraint.parameterTypes[i]);\r
-                    if(fieldTypeDesc.equals(TypeDesc.VOID))\r
-                        continue;\r
-                    mb.loadLocal(aVar);\r
-                    mb.loadField(factClassName, "c"+i, fieldTypeDesc);\r
-                    \r
-                    mb.loadLocal(bVar);\r
-                    mb.loadField(factClassName, "c"+i, fieldTypeDesc);\r
-\r
-                    CodeBuilderUtils.equals(mb, fieldTypeDesc, failure);\r
-                }\r
-            mb.loadConstant(true);\r
-            mb.returnValue(TypeDesc.BOOLEAN);\r
-            \r
-            mb.setLocation(failure);\r
-            mb.loadConstant(false);\r
-            mb.returnValue(TypeDesc.BOOLEAN);\r
-            mb.finish();\r
-        }\r
-        \r
-        // Method: keyHashCode\r
-\r
-        {\r
-            // @Override\r
-            // protected int keyHashCode(Object key) {\r
-            //     return (0x811C9DC5^((ExampleFact)key).c0)*16777619;\r
-            // }\r
-            \r
-            MethodBuilderBase mb = hashIndexClassBuilder.addMethodBase(Opcodes.ACC_PROTECTED, "keyHashCode", TypeDesc.INT, Constants.OBJECTS[1]);\r
-            mb.loadLocal(mb.getParameter(0));\r
-            mb.checkCast(factClassTypeDesc);\r
-            LocalVariable factVar = mb.createLocalVariable("fact", factClassTypeDesc);\r
-            mb.storeLocal(factVar);\r
-\r
-            mb.loadConstant(0x811C9DC5);\r
-\r
-            int curMask = indexInfo.indexMask;\r
-            for(int i=0;i<constraint.parameterTypes.length;++i,curMask>>=1)\r
-                if((curMask&1) == 1) {\r
-                    TypeDesc fieldTypeDesc = jtt.toTypeDesc(constraint.parameterTypes[i]);\r
-                    if(fieldTypeDesc.equals(TypeDesc.VOID))\r
-                        continue;\r
-                    mb.loadLocal(factVar);\r
-                    mb.loadField(factClassName, "c"+i, fieldTypeDesc);\r
-                    CodeBuilderUtils.hashCode(mb, fieldTypeDesc);\r
-                    mb.math(Opcodes.IXOR);\r
-                    mb.loadConstant(16777619);\r
-                    mb.math(Opcodes.IMUL);\r
-\r
-                }\r
-            mb.returnValue(TypeDesc.INT);\r
-            mb.finish();\r
-        }\r
-\r
-        hashIndexClassBuilder.addDefaultConstructor();\r
-        \r
-        return hashIndexClassBuilder;\r
-    }\r
-}\r
+package org.simantics.scl.compiler.internal.codegen.chr;
+
+import java.util.ArrayList;
+
+import org.cojen.classfile.TypeDesc;
+import org.objectweb.asm.Opcodes;
+import org.simantics.scl.compiler.elaboration.chr.CHRRuleset;
+import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint;
+import org.simantics.scl.compiler.internal.codegen.references.BoundVar;
+import org.simantics.scl.compiler.internal.codegen.utils.ClassBuilder;
+import org.simantics.scl.compiler.internal.codegen.utils.Constants;
+import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilderBase;
+import org.simantics.scl.compiler.internal.codegen.utils.ModuleBuilder;
+
+public class CHRCodeGenerator {
+
+    public static final TypeDesc FACT_ID_TYPE = TypeDesc.INT;
+    private static final String FactActivationQueue_name = "org/simantics/scl/runtime/chr/FactActivationQueue";
+    private static final TypeDesc FactActivationQueue = TypeDesc.forClass(FactActivationQueue_name);
+    private static final String QUEUE = "queue";
+
+    public static void generateStore(ModuleBuilder moduleBuilder, CHRRuleset ruleset) {
+        ClassBuilder storeClassBuilder = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, ruleset.storeClassName, "java/lang/Object");
+        if(ruleset.parameters == null)
+            ruleset.parameters = new BoundVar[0];
+        ruleset.parameterTypeDescs = moduleBuilder.getJavaTypeTranslator().getTypeDescs(ruleset.parameters); 
+
+        ArrayList<StoreInitialization> hashIndexInitializations = new ArrayList<>();
+        for(CHRConstraint constraint : ruleset.constraints)
+            generateFact(storeClassBuilder, constraint, hashIndexInitializations);
+
+        // Fields
+        for(int i=0;i<ruleset.parameterTypeDescs.length;++i) {
+            TypeDesc typeDesc = ruleset.parameterTypeDescs[i];
+            if(typeDesc.equals(TypeDesc.VOID))
+                continue;
+            storeClassBuilder.addField(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "p" + i, typeDesc);
+        }
+        storeClassBuilder.addField(Opcodes.ACC_PUBLIC, "currentId", FACT_ID_TYPE);
+        for(StoreInitialization ini : hashIndexInitializations)
+            storeClassBuilder.addField(ini.access, ini.fieldName, ini.fieldType);
+        storeClassBuilder.addField(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, QUEUE, FactActivationQueue);
+
+        // Constructors
+
+        {
+            MethodBuilderBase mb = storeClassBuilder.addConstructor(Opcodes.ACC_PUBLIC, ruleset.parameterTypeDescs);
+            mb.loadThis();
+            mb.invokeConstructor(storeClassBuilder.getSuperClassName(), Constants.EMPTY_TYPEDESC_ARRAY);
+            for(int i=0;i<ruleset.parameterTypeDescs.length;++i) {
+                TypeDesc typeDesc = ruleset.parameterTypeDescs[i];
+                if(typeDesc.equals(TypeDesc.VOID))
+                    continue;
+                mb.loadThis();
+                mb.loadLocal(mb.getParameter(i));
+                mb.storeField(ruleset.storeClassName, "p" + i, ruleset.parameterTypeDescs[i]);
+            }
+            mb.loadThis();
+            mb.loadConstant(1);
+            mb.storeField(storeClassBuilder.getClassName(), "currentId", TypeDesc.INT);
+            for(StoreInitialization ini : hashIndexInitializations) {
+                mb.loadThis();
+                mb.newObject(ini.className);
+                mb.dup();
+                mb.invokeConstructor(ini.className, Constants.EMPTY_TYPEDESC_ARRAY);
+                mb.storeField(ruleset.storeClassName, ini.fieldName, ini.fieldType);
+            }
+            {
+                mb.loadThis();
+                mb.newObject(FactActivationQueue_name);
+                mb.dup();
+                mb.loadConstant(ruleset.priorityCount);
+                mb.invokeConstructor(FactActivationQueue_name, new TypeDesc[] {TypeDesc.INT});
+                mb.storeField(ruleset.storeClassName, QUEUE, FactActivationQueue);
+            }
+            mb.returnVoid();
+            mb.finish();
+        }
+
+        // Activate
+
+        {
+            MethodBuilderBase mb = storeClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "activate", TypeDesc.VOID, new TypeDesc[] {TypeDesc.INT});
+            mb.loadThis();
+            mb.loadField(ruleset.storeClassName, QUEUE, FactActivationQueue);
+            mb.loadThis();
+            mb.loadLocal(mb.getParameter(0));
+            mb.invokeVirtual(FactActivationQueue_name, "activate", TypeDesc.VOID, new TypeDesc[] {TypeDesc.OBJECT, TypeDesc.INT});
+            mb.returnVoid();
+            mb.finish();
+        }
+
+        moduleBuilder.addClass(storeClassBuilder);
+    }
+
+    private static void generateFact(ClassBuilder storeClassBuilder, CHRConstraint constraint, ArrayList<StoreInitialization> hashIndexInitializations) {
+        CHRFactCodeGenerator generator = new CHRFactCodeGenerator(storeClassBuilder, constraint);
+        generator.generate(hashIndexInitializations);
+    }
+}