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