]> gerrit.simantics Code Review - simantics/platform.git/blob
af06463bbe7a1672f2bb9fe2e8ea10e97b4b5aa7
[simantics/platform.git] /
1 package org.simantics.scl.compiler.internal.codegen.chr;
2
3 import java.util.ArrayList;
4
5 import org.cojen.classfile.TypeDesc;
6 import org.objectweb.asm.Label;
7 import org.objectweb.asm.Opcodes;
8 import org.simantics.scl.compiler.elaboration.chr.CHRRuleset;
9 import org.simantics.scl.compiler.elaboration.chr.plan.PrioritizedPlan;
10 import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint;
11 import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint.IndexInfo;
12 import org.simantics.scl.compiler.internal.codegen.references.BoundVar;
13 import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator;
14 import org.simantics.scl.compiler.internal.codegen.utils.ClassBuilder;
15 import org.simantics.scl.compiler.internal.codegen.utils.CodeBuilderUtils;
16 import org.simantics.scl.compiler.internal.codegen.utils.Constants;
17 import org.simantics.scl.compiler.internal.codegen.utils.LocalVariable;
18 import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;
19 import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilderBase;
20 import org.simantics.scl.compiler.internal.codegen.utils.ModuleBuilder;
21
22 import gnu.trove.list.array.TIntArrayList;
23 import gnu.trove.set.hash.THashSet;
24
25 public class CHRCodeGenerator {
26     
27     public static final TypeDesc FACT_ID_TYPE = TypeDesc.INT;
28     public static final String CHRHashIndex_name = "org/simantics/scl/runtime/chr/CHRHashIndex";
29     public static final TypeDesc CHRHashIndex = TypeDesc.forClass(CHRHashIndex_name);
30     public static final String FactActivationQueue_name = "org/simantics/scl/runtime/chr/FactActivationQueue";
31     public static final TypeDesc FactActivationQueue = TypeDesc.forClass(FactActivationQueue_name);
32     public static final String Fact_name = "org/simantics/scl/runtime/chr/Fact";
33     public static final TypeDesc Fact = TypeDesc.forClass(Fact_name);
34     public static final String QUEUE = "queue";
35     
36     private static class StoreInitialization {
37         final int access;
38         final String fieldName;
39         final TypeDesc fieldType;
40         final String className;
41         public StoreInitialization(int access, String fieldName, TypeDesc fieldType, String className) {
42             this.access = access;
43             this.fieldName = fieldName;
44             this.fieldType = fieldType;
45             this.className = className;
46         }
47     }
48     
49     public static void generateStore(ModuleBuilder moduleBuilder, CHRRuleset ruleset) {
50         ClassBuilder storeClassBuilder = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, ruleset.storeClassName, "java/lang/Object");
51         if(ruleset.parameters == null)
52             ruleset.parameters = new BoundVar[0];
53         ruleset.parameterTypeDescs = moduleBuilder.getJavaTypeTranslator().getTypeDescs(ruleset.parameters); 
54         
55         ArrayList<StoreInitialization> hashIndexInitializations = new ArrayList<>();
56         for(CHRConstraint constraint : ruleset.constraints)
57             generateFact(storeClassBuilder, constraint, hashIndexInitializations);
58         
59         // Fields
60         for(int i=0;i<ruleset.parameterTypeDescs.length;++i)
61             storeClassBuilder.addField(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, "p" + i, ruleset.parameterTypeDescs[i]);
62         storeClassBuilder.addField(Opcodes.ACC_PUBLIC, "currentId", FACT_ID_TYPE);
63         for(StoreInitialization ini : hashIndexInitializations)
64             storeClassBuilder.addField(ini.access, ini.fieldName, ini.fieldType);
65         storeClassBuilder.addField(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, QUEUE, FactActivationQueue);
66         
67         // Constructors
68         
69         {
70             MethodBuilderBase mb = storeClassBuilder.addConstructor(Opcodes.ACC_PUBLIC, ruleset.parameterTypeDescs);
71             mb.loadThis();
72             mb.invokeConstructor(storeClassBuilder.getSuperClassName(), Constants.EMPTY_TYPEDESC_ARRAY);
73             for(int i=0;i<ruleset.parameterTypeDescs.length;++i) {
74                 mb.loadThis();
75                 mb.loadLocal(mb.getParameter(i));
76                 mb.storeField(ruleset.storeClassName, "p" + i, ruleset.parameterTypeDescs[i]);
77             }
78             mb.loadThis();
79             mb.loadConstant(1);
80             mb.storeField(storeClassBuilder.getClassName(), "currentId", TypeDesc.INT);
81             for(StoreInitialization ini : hashIndexInitializations) {
82                 mb.loadThis();
83                 mb.newObject(ini.className);
84                 mb.dup();
85                 mb.invokeConstructor(ini.className, Constants.EMPTY_TYPEDESC_ARRAY);
86                 mb.storeField(ruleset.storeClassName, ini.fieldName, ini.fieldType);
87             }
88             {
89                 mb.loadThis();
90                 mb.newObject(FactActivationQueue_name);
91                 mb.dup();
92                 mb.loadConstant(ruleset.priorityCount);
93                 mb.invokeConstructor(FactActivationQueue_name, new TypeDesc[] {TypeDesc.INT});
94                 mb.storeField(ruleset.storeClassName, QUEUE, FactActivationQueue);
95             }
96             mb.returnVoid();
97             mb.finish();
98         }
99         
100         // Activate
101         
102         {
103             MethodBuilderBase mb = storeClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "activate", TypeDesc.VOID, new TypeDesc[] {TypeDesc.INT});
104             mb.loadThis();
105             mb.loadField(ruleset.storeClassName, QUEUE, FactActivationQueue);
106             mb.loadThis();
107             mb.loadLocal(mb.getParameter(0));
108             mb.invokeVirtual(FactActivationQueue_name, "activate", TypeDesc.VOID, new TypeDesc[] {TypeDesc.OBJECT, TypeDesc.INT});
109             mb.returnVoid();
110             mb.finish();
111         }
112         
113         moduleBuilder.addClass(storeClassBuilder);
114     }
115     
116     private static void generateFact(ClassBuilder storeClassBuilder, CHRConstraint constraint, ArrayList<StoreInitialization> hashIndexInitializations) {
117         CHRRuleset ruleset = constraint.parentRuleset;
118         boolean supportsRemoval = constraint.mayBeRemoved();
119         
120         ModuleBuilder moduleBuilder = storeClassBuilder.getModuleBuilder();
121         JavaTypeTranslator jtt = moduleBuilder.getJavaTypeTranslator();
122         TypeDesc storeTypeDesc = storeClassBuilder.getType();
123         TypeDesc[] storeTypeDescArray = new TypeDesc[] { storeTypeDesc };
124         
125         String factClassName = storeClassBuilder.getClassName() + "$" + constraint.name;
126         TypeDesc factTypeDesc = TypeDesc.forClass(factClassName);
127         ClassBuilder factClassBuilder = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, factClassName, "java/lang/Object", Fact_name);
128         
129         // Fields
130         
131         /* public int id;
132            public int c0; // key
133            public int c1;
134            public ExampleFact bfPrev;
135            public ExampleFact bfNext;
136          */
137         TypeDesc[] parameterTypeDescs = jtt.toTypeDescs(constraint.parameterTypes);
138         factClassBuilder.addField(Opcodes.ACC_PUBLIC, "id", FACT_ID_TYPE);
139         for(int i=0;i<constraint.parameterTypes.length;++i)
140             factClassBuilder.addField(Opcodes.ACC_PUBLIC, "c" + i, parameterTypeDescs[i]);
141         
142         for(IndexInfo indexInfo : constraint.getIndices()) {
143             if(supportsRemoval)
144                 factClassBuilder.addField(Opcodes.ACC_PUBLIC, indexInfo.indexName + "Prev", factTypeDesc);
145             factClassBuilder.addField(Opcodes.ACC_PUBLIC, indexInfo.indexName + "Next", factTypeDesc);
146             
147             String hashIndexField = constraint.name + "$" + indexInfo.indexName;
148             if(indexInfo.indexMask == 0) {
149                 // If there are now bound parameters, use just a direct reference to a fact
150                 storeClassBuilder.addField(Opcodes.ACC_PUBLIC, hashIndexField, factTypeDesc);
151             }
152             else {
153                 ClassBuilder hashClass = generateSpecializedHashIndex(storeClassBuilder, constraint, indexInfo, factTypeDesc, factClassName);
154                 moduleBuilder.addClass(hashClass);
155                 hashIndexInitializations.add(new StoreInitialization(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, hashIndexField, CHRHashIndex, hashClass.getClassName()));
156             }
157         }
158         
159         // Method: get
160         
161         hashIndexInitializations.add(new StoreInitialization(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, constraint.name + "$temp", factTypeDesc, factClassName));
162
163         
164         {
165             /*
166             public ExampleFact ExampleFact$bf(int c0) {
167                 ExampleFact$temp.c0 = c0;
168                 return (ExampleFact)ExampleFact_bfIndex.getEqual(ExampleFact$temp);
169             }
170             */
171             for(IndexInfo indexInfo : constraint.getIndices()) {
172                 /*if(indexInfo.indexMask == 0) {
173                     MethodBuilderBase mb = storeClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, constraint.name + "$" + indexInfo.indexName,
174                             factTypeDesc, Constants.EMPTY_TYPEDESC_ARRAY);
175                     mb.loadThis();
176                     mb.loadField(storeClassBuilder.getClassName(), constraint.name + "$" + indexInfo.indexName, factTypeDesc);
177                     mb.returnValue(factTypeDesc);
178                     mb.finish();
179                 }*/
180                 if(indexInfo.indexMask != 0) {
181                     ArrayList<TypeDesc> getParameterTypeDescs = new ArrayList<TypeDesc>(constraint.parameterTypes.length);
182                     for(int i=0;i<constraint.parameterTypes.length;++i)
183                         if(((indexInfo.indexMask>>i)&1)==1)
184                             getParameterTypeDescs.add(parameterTypeDescs[i]);
185                     MethodBuilderBase mb = storeClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, constraint.name + "$" + indexInfo.indexName, factTypeDesc,
186                             getParameterTypeDescs.toArray(new TypeDesc[getParameterTypeDescs.size()]));
187                     mb.loadThis();
188                     mb.loadField(storeClassBuilder.getClassName(), constraint.name + "$temp", factTypeDesc);
189                     LocalVariable tempFactVar = mb.createLocalVariable("temp", factTypeDesc);
190                     mb.storeLocal(tempFactVar);
191                     int parameterId=0;
192                     for(int i=0;i<constraint.parameterTypes.length;++i)
193                         if(((indexInfo.indexMask>>i)&1)==1) {
194                             mb.loadLocal(tempFactVar);
195                             mb.loadLocal(mb.getParameter(parameterId++));
196                             mb.storeField(factClassName, "c"+i, parameterTypeDescs[i]);
197                         }
198
199                     mb.loadThis();
200                     mb.loadField(storeClassBuilder.getClassName(), constraint.name + "$" + indexInfo.indexName, CHRHashIndex);
201                     mb.loadLocal(tempFactVar);
202                     mb.invokeVirtual(CHRHashIndex_name, supportsRemoval ? "getEqual" : "getEqualNoRemovals", TypeDesc.OBJECT, Constants.OBJECTS[1]);
203                     mb.checkCast(factTypeDesc);
204                 
205                     mb.returnValue(factTypeDesc);
206                     mb.finish();
207                 }
208             }   
209         }
210         
211         // Method: add
212         
213         {
214             MethodBuilderBase mb = factClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "add", TypeDesc.VOID, storeTypeDescArray);
215             LocalVariable storeParameter = mb.getParameter(0);
216             for(IndexInfo indexInfo : constraint.getIndices()) {
217                 String linkedListPrev = indexInfo.indexName + "Prev";
218                 String linkedListNext = indexInfo.indexName + "Next";
219                 String storeHashIndexName = constraint.name + "$" + indexInfo.indexName;
220                 
221                 // public void add(ExampleStore store) {
222                 //     bfNext = (ExampleFact)store.ExampleFact_bfIndex.addFreshAndReturnOld(this);
223                 //     if(bfNext != null)
224                 //         bfNext.bfPrev = this;
225                 // }
226                 
227                 if(indexInfo.indexMask == 0) {
228                     mb.loadThis();
229                     mb.loadLocal(storeParameter);
230                     mb.loadField(storeClassBuilder.getClassName(), storeHashIndexName, factTypeDesc);
231                     if(supportsRemoval)
232                         mb.dupX1();
233                     mb.storeField(factClassName, linkedListNext, factTypeDesc);
234                     if(supportsRemoval) {
235                         Label cont = new Label();
236                         mb.ifNullBranch(cont, true);
237                         mb.loadThis();
238                         mb.loadField(factClassName, linkedListNext, factTypeDesc);
239                         mb.loadThis();
240                         mb.storeField(factClassName, linkedListPrev, factTypeDesc);
241                         mb.setLocation(cont);
242                     }
243                     mb.loadLocal(storeParameter);
244                     mb.loadThis();
245                     mb.storeField(storeClassBuilder.getClassName(), storeHashIndexName, factTypeDesc);
246                 }
247                 else {
248                     // bfNext = (ExampleFact)store.ExampleFact_bfIndex.addFreshAndReturnOld(this);
249                     mb.loadThis();
250                     mb.loadLocal(storeParameter);
251                     mb.loadField(storeClassBuilder.getClassName(), storeHashIndexName, CHRHashIndex);
252                     mb.loadThis();
253                     mb.invokeVirtual(CHRHashIndex_name, supportsRemoval ? "addFreshAndReturnOld" : "addFreshAndReturnOld", TypeDesc.OBJECT, Constants.OBJECTS[1]);
254                     mb.checkCast(factTypeDesc);
255                     if(supportsRemoval)
256                         mb.dupX1();
257                     mb.storeField(factClassName, linkedListNext, factTypeDesc);
258                     // leaves bfNext on the stack
259
260                     //if(bfNext != null)
261                     //    bfNext.bfPrev = this;
262                     if(supportsRemoval) {
263                         Label cont = new Label();
264                         mb.ifNullBranch(cont, true);
265                         mb.loadThis();
266                         mb.loadField(factClassName, linkedListNext, factTypeDesc);
267                         mb.loadThis();
268                         mb.storeField(factClassName, linkedListPrev, factTypeDesc);
269                         mb.setLocation(cont);
270                     }
271                 }
272             }            
273             if(!constraint.isPassive()) {
274                 mb.loadLocal(storeParameter);
275                 mb.loadField(storeClassBuilder.getClassName(), QUEUE, FactActivationQueue);
276                 mb.loadConstant(constraint.getMinimumPriority());
277                 mb.loadThis();
278                 mb.invokeVirtual(FactActivationQueue_name, "add", TypeDesc.VOID, new TypeDesc[] {TypeDesc.INT, Fact});
279             }
280             mb.returnVoid();
281             mb.finish();
282         }
283         
284         // Method: remove
285
286         if(supportsRemoval) {
287             // public void remove(ExampleStore store) {
288             //     if(bfPrev == null) {
289             //         if(bfNext == null)
290             //             store.ExampleFact_bfIndex.removeKnownToExistKey(this);
291             //         else {
292             //             bfNext.bfPrev = null;
293             //             store.ExampleFact_bfIndex.replaceKnownToExistKey(this, bfNext);
294             //         }
295             //     }
296             //     else {
297             //         bfPrev.bfNext = bfNext;
298             //         if(bfNext != null)
299             //             bfNext.bfPrev = bfPrev;
300             //     }
301             // }
302             
303             MethodBuilderBase mb = factClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "remove", TypeDesc.VOID, storeTypeDescArray);
304             LocalVariable storeParameter = mb.getParameter(0);
305             for(IndexInfo indexInfo : constraint.getIndices()) {
306                 String linkedListPrev = indexInfo.indexName + "Prev";
307                 String linkedListNext = indexInfo.indexName + "Next";
308                 String storeHashIndexName = constraint.name + "$" + indexInfo.indexName;
309                 
310                 Label nextIndex = mb.createLabel();
311                 
312                 // if(bfPrev == null) {
313                 mb.loadThis();
314                 mb.loadField(factClassName, linkedListPrev, factTypeDesc);
315                 Label else1 = new Label();
316                 mb.ifNullBranch(else1, false);
317                 
318                 //     if(bfNext == null)
319                 mb.loadThis();
320                 mb.loadField(factClassName, linkedListNext, factTypeDesc);
321                 Label else2 = new Label();
322                 mb.ifNullBranch(else2, false);
323                 
324                 //         store.ExampleFact_bfIndex.removeKnownToExistKey(this);
325                 if(indexInfo.indexMask == 0) {
326                     mb.loadLocal(storeParameter);
327                     mb.loadNull();
328                     mb.storeField(storeClassBuilder.getClassName(), storeHashIndexName, factTypeDesc);
329                 }
330                 else {
331                     mb.loadLocal(storeParameter);
332                     mb.loadField(storeClassBuilder.getClassName(), storeHashIndexName, CHRHashIndex);
333                     mb.loadThis();
334                     mb.invokeVirtual(CHRHashIndex_name, "removeKnownToExistKey", TypeDesc.VOID, Constants.OBJECTS[1]);
335                 }
336                 mb.branch(nextIndex);
337                 
338                 //     else {
339                 mb.setLocation(else2);
340                 //         bfNext.bfPrev = null;
341                 mb.loadThis();
342                 mb.loadField(factClassName, linkedListNext, factTypeDesc);
343                 mb.loadNull();
344                 mb.storeField(factClassName, linkedListPrev, factTypeDesc);
345                 //         store.ExampleFact_bfIndex.replaceKnownToExistKey(this, bfNext);
346                 if(indexInfo.indexMask == 0) {
347                     mb.loadLocal(storeParameter);
348                     mb.loadThis();
349                     mb.loadField(factClassName, linkedListNext, factTypeDesc);
350                     mb.storeField(storeClassBuilder.getClassName(), storeHashIndexName, factTypeDesc);
351                 }
352                 else {
353                     mb.loadLocal(storeParameter);
354                     mb.loadField(storeClassBuilder.getClassName(), storeHashIndexName, CHRHashIndex);
355                     mb.loadThis();
356                     mb.loadThis();
357                     mb.loadField(factClassName, linkedListNext, factTypeDesc);
358                     mb.invokeVirtual(CHRHashIndex_name, "replaceKnownToExistKey", TypeDesc.VOID, Constants.OBJECTS[2]);
359                 }
360                 mb.branch(nextIndex);
361                 //     }
362                 
363                 // else {
364                 mb.setLocation(else1);
365                 //     bfPrev.bfNext = bfNext;
366                 mb.loadThis();
367                 mb.loadField(factClassName, linkedListPrev, factTypeDesc);
368                 mb.loadThis();
369                 mb.loadField(factClassName, linkedListNext, factTypeDesc);
370                 mb.storeField(factClassName, linkedListNext, factTypeDesc);
371                 //     if(bfNext != null)
372                 mb.loadThis();
373                 mb.loadField(factClassName, linkedListNext, factTypeDesc);
374                 Label else3 = new Label();
375                 mb.ifNullBranch(else3, true);
376                 //         bfNext.bfPrev = bfPrev;
377                 mb.loadThis();
378                 mb.loadField(factClassName, linkedListNext, factTypeDesc);
379                 mb.loadThis();
380                 mb.loadField(factClassName, linkedListPrev, factTypeDesc);
381                 mb.storeField(factClassName, linkedListPrev, factTypeDesc);
382                 mb.setLocation(else3);
383                 mb.branch(nextIndex);
384                 // }
385                 
386                 mb.setLocation(nextIndex);
387             }
388             mb.loadThis();
389             mb.loadConstant(-1);
390             mb.storeField(factClassName, "id", FACT_ID_TYPE);
391             mb.returnVoid();
392             mb.finish();
393         }
394         
395         // Method: isAlive
396
397         {
398             // @Override
399             // public boolean isAlive() {
400             //     return id >= 0;
401             // }
402             
403             MethodBuilderBase mb = factClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "isAlive", TypeDesc.BOOLEAN, Constants.EMPTY_TYPEDESC_ARRAY);
404             if(supportsRemoval) {
405                 mb.loadThis();
406                 mb.loadField(factClassName, "id", FACT_ID_TYPE);
407
408                 Label thenBranch = mb.createLabel();
409                 mb.ifZeroComparisonBranch(thenBranch, "<");
410                 mb.loadConstant(true);
411                 mb.returnValue(TypeDesc.BOOLEAN);
412
413                 mb.setLocation(thenBranch);
414                 mb.loadConstant(false);
415                 mb.returnValue(TypeDesc.BOOLEAN);
416             }
417             else {
418                 mb.loadConstant(true);
419                 mb.returnValue(TypeDesc.BOOLEAN);
420             }
421             mb.finish();
422         }
423         
424         // activate parts
425         
426         THashSet<BoundVar> usedParameters = new THashSet<BoundVar>();
427         for(int i=0;i<constraint.plans.size();++i) {
428             PrioritizedPlan plan = constraint.plans.get(i);
429             MethodBuilder mb = factClassBuilder.addMethod(Opcodes.ACC_PUBLIC, "activate" + i, TypeDesc.BOOLEAN, new TypeDesc[] {storeTypeDesc});
430             LocalVariable storeVar = mb.getParameter(0);
431             LocalVariable factVar = new LocalVariable(0, factTypeDesc);
432             mb.setLocalVariable(ruleset.this_, storeVar);
433             mb.setLocalVariable(plan.implementation.getParameters()[0], factVar);
434             
435             // Set closure parameters
436             usedParameters.clear();
437             plan.implementation.forValRefs(valRef -> {
438                 if(valRef.getBinding() instanceof BoundVar)
439                     usedParameters.add((BoundVar)valRef.getBinding());
440             });
441             for(int j=0;j<ruleset.parameters.length;++j) {
442                 BoundVar parameter = ruleset.parameters[j];
443                 if(!usedParameters.contains(parameter))
444                     continue;
445                 mb.loadLocal(storeVar);
446                 mb.loadField(storeClassBuilder.getClassName(), "p"+j, ruleset.parameterTypeDescs[j]);
447                 mb.store(parameter);
448             }
449             
450             // Generate code
451             //System.out.println("=== activate" + i + " ==========================================================");
452             //System.out.println(plan.implementation);
453             plan.implementation.markGenerateOnFly();
454             plan.implementation.generateCodeWithAlreadyPreparedParameters(mb);
455             mb.finish();
456         }
457         
458         // Method: activate
459
460         {
461             // @Override
462             // public int activate(Object context, int priority) {
463             //     return -1;
464             // }
465             
466             MethodBuilderBase mb = factClassBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "activate", TypeDesc.INT, new TypeDesc[] {TypeDesc.OBJECT, TypeDesc.INT});
467             Label defaultLabel = mb.createLabel();
468             
469             if(!constraint.isPassive()) {
470                 // Check if the fact is alive
471                 mb.loadThis();
472                 mb.loadField(factClassName, "id", TypeDesc.INT);
473                 mb.ifZeroComparisonBranch(defaultLabel, "<");
474
475                 mb.loadLocal(mb.getParameter(0));
476                 mb.checkCast(storeTypeDesc);
477                 LocalVariable storeVariable = new LocalVariable(1, storeTypeDesc);
478                 mb.storeLocal(storeVariable);
479
480                 TIntArrayList priorities = new TIntArrayList(constraint.plans.size());
481                 ArrayList<Label> labels = new ArrayList<Label>();
482                 int lastPriority = -1;
483                 for(PrioritizedPlan plan : constraint.plans)
484                     if(plan.priority != lastPriority) {
485                         priorities.add(plan.priority);
486                         labels.add(mb.createLabel());
487                         lastPriority = plan.priority;
488                     }
489
490                 mb.loadLocal(mb.getParameter(1));
491                 mb.switch_(priorities.toArray(), labels.toArray(new Label[labels.size()]), defaultLabel);
492                 int labelId = -1;
493                 for(int i=0;i<constraint.plans.size();++i) {
494                     PrioritizedPlan plan = constraint.plans.get(i);
495                     if(labelId == -1 || plan.priority != priorities.get(labelId)) {
496                         if(labelId >= 0) {
497                             mb.loadConstant(plan.priority);
498                             mb.returnValue(TypeDesc.INT);
499                         }
500                         ++labelId;
501                         mb.setLocation(labels.get(labelId));
502                     }
503                     mb.loadThis();
504                     mb.loadLocal(storeVariable);
505                     mb.invokeVirtual(factClassName, "activate" + i, TypeDesc.BOOLEAN, new TypeDesc[] {storeTypeDesc});
506                     mb.ifZeroComparisonBranch(defaultLabel, "==");
507                 }
508                 mb.setLocation(defaultLabel);
509             }
510             mb.loadConstant(-1);
511             mb.returnValue(TypeDesc.INT);
512             mb.finish();
513         }
514         
515         // Constructors
516         
517         {
518             // public ExampleFact(int id, int c0, int c1) {
519             //     this.id = id;            
520             //     this.c0 = c0;
521             //     this.c1 = c1;
522             // }
523             
524             TypeDesc[] constructorParameters = new TypeDesc[parameterTypeDescs.length+1];
525             constructorParameters[0] = FACT_ID_TYPE;
526             for(int i=0;i<parameterTypeDescs.length;++i)
527                 constructorParameters[i+1] = parameterTypeDescs[i];
528             MethodBuilderBase mb = factClassBuilder.addConstructor(Opcodes.ACC_PUBLIC, constructorParameters);
529             mb.loadThis();
530             mb.invokeConstructor(factClassBuilder.getSuperClassName(), Constants.EMPTY_TYPEDESC_ARRAY);
531             mb.loadThis();
532             mb.loadLocal(mb.getParameter(0));
533             mb.storeField(factClassName, "id", FACT_ID_TYPE);
534             for(int i=0;i<constraint.parameterTypes.length;++i) {
535                 mb.loadThis();
536                 mb.loadLocal(mb.getParameter(i+1));
537                 mb.storeField(factClassName, "c" + i, parameterTypeDescs[i]);
538             }
539             mb.returnVoid();
540             mb.finish();
541         }
542         factClassBuilder.addDefaultConstructor();
543         
544         moduleBuilder.addClass(factClassBuilder);
545     }
546
547     private static ClassBuilder generateSpecializedHashIndex(ClassBuilder storeClassBuilder, CHRConstraint constraint, IndexInfo indexInfo, TypeDesc factClassTypeDesc, String factClassName) {
548         // new CHRHashIndex() {
549         //     @Override
550         //     protected boolean keyEquals(Object a, Object b) {
551         //         return ((ExampleFact)a).c0 == ((ExampleFact)b).c0;
552         //     }
553         //     @Override
554         //     protected int keyHashCode(Object key) {
555         //         return ((ExampleFact)key).c0;
556         //     }
557         // }
558
559         ModuleBuilder moduleBuilder = storeClassBuilder.getModuleBuilder();
560         JavaTypeTranslator jtt = moduleBuilder.getJavaTypeTranslator();
561         
562         String hashIndexClassName = factClassName + "$" + indexInfo.indexName; 
563         ClassBuilder hashIndexClassBuilder = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, hashIndexClassName, "org/simantics/scl/runtime/chr/CHRHashIndex");
564         
565         // Method: keyEquals
566
567         {
568
569             // @Override
570             // protected boolean keyEquals(Object a, Object b) {
571             //     return ((ExampleFact)a).c0 == ((ExampleFact)b).c0;
572             // }
573             
574             MethodBuilderBase mb = hashIndexClassBuilder.addMethodBase(Opcodes.ACC_PROTECTED, "keyEquals", TypeDesc.BOOLEAN, Constants.OBJECTS[2]);
575             mb.loadLocal(mb.getParameter(0));
576             mb.checkCast(factClassTypeDesc);
577             LocalVariable aVar = mb.createLocalVariable("a", factClassTypeDesc);
578             mb.storeLocal(aVar);
579             
580             mb.loadLocal(mb.getParameter(1));
581             mb.checkCast(factClassTypeDesc);
582             LocalVariable bVar = mb.createLocalVariable("b", factClassTypeDesc);
583             mb.storeLocal(bVar);
584
585             Label failure = mb.createLabel();
586             
587             int curMask = indexInfo.indexMask;
588             for(int i=0;i<constraint.parameterTypes.length;++i,curMask>>=1)
589                 if((curMask&1) == 1) {
590                     TypeDesc fieldTypeDesc = jtt.toTypeDesc(constraint.parameterTypes[i]);
591                     if(fieldTypeDesc.equals(TypeDesc.VOID))
592                         continue;
593                     mb.loadLocal(aVar);
594                     mb.loadField(factClassName, "c"+i, fieldTypeDesc);
595                     
596                     mb.loadLocal(bVar);
597                     mb.loadField(factClassName, "c"+i, fieldTypeDesc);
598
599                     CodeBuilderUtils.equals(mb, fieldTypeDesc, failure);
600                 }
601             mb.loadConstant(true);
602             mb.returnValue(TypeDesc.BOOLEAN);
603             
604             mb.setLocation(failure);
605             mb.loadConstant(false);
606             mb.returnValue(TypeDesc.BOOLEAN);
607             mb.finish();
608         }
609         
610         // Method: keyHashCode
611
612         {
613             // @Override
614             // protected int keyHashCode(Object key) {
615             //     return (0x811C9DC5^((ExampleFact)key).c0)*16777619;
616             // }
617             
618             MethodBuilderBase mb = hashIndexClassBuilder.addMethodBase(Opcodes.ACC_PROTECTED, "keyHashCode", TypeDesc.INT, Constants.OBJECTS[1]);
619             mb.loadLocal(mb.getParameter(0));
620             mb.checkCast(factClassTypeDesc);
621             LocalVariable factVar = mb.createLocalVariable("fact", factClassTypeDesc);
622             mb.storeLocal(factVar);
623
624             mb.loadConstant(0x811C9DC5);
625
626             int curMask = indexInfo.indexMask;
627             for(int i=0;i<constraint.parameterTypes.length;++i,curMask>>=1)
628                 if((curMask&1) == 1) {
629                     TypeDesc fieldTypeDesc = jtt.toTypeDesc(constraint.parameterTypes[i]);
630                     if(fieldTypeDesc.equals(TypeDesc.VOID))
631                         continue;
632                     mb.loadLocal(factVar);
633                     mb.loadField(factClassName, "c"+i, fieldTypeDesc);
634                     CodeBuilderUtils.hashCode(mb, fieldTypeDesc);
635                     mb.math(Opcodes.IXOR);
636                     mb.loadConstant(16777619);
637                     mb.math(Opcodes.IMUL);
638
639                 }
640             mb.returnValue(TypeDesc.INT);
641             mb.finish();
642         }
643
644         hashIndexClassBuilder.addDefaultConstructor();
645         
646         return hashIndexClassBuilder;
647     }
648 }