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