]> gerrit.simantics Code Review - simantics/platform.git/blob
81f7b117a82f61f7ade860cd1362d5b4a85559e3
[simantics/platform.git] /
1 package org.simantics.scl.compiler.internal.codegen.utils;
2
3 import java.util.Arrays;
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.internal.codegen.references.Val;
9 import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator;
10 import org.simantics.scl.compiler.types.Type;
11
12 public class CodeBuilderUtils {
13     
14     /**
15      * Creates fields c0,...,c{N-1} to the given class, where c is fieldNamePrefix and N is the length of types.
16      * Creates also a constructor for the fields. 
17      * @param classFile
18      * @param fieldModifiers
19      * @param fieldNamePrefix
20      * @param types
21      */
22     public static void makeRecord(ClassBuilder classBuilder, String recordName, int fieldModifiers, String fieldNamePrefix, TypeDesc[] types,
23             boolean generateEqualsAndHashCode) {
24         // Create fields
25         for(int i=0;i<types.length;++i)
26             if(!types[i].equals(TypeDesc.VOID))
27                 classBuilder.addField(fieldModifiers, fieldNamePrefix+i, types[i]);
28         
29         // Create constructor        
30         MethodBuilderBase mb = classBuilder.addConstructor(
31                 types.length == 0 ? Opcodes.ACC_PRIVATE : Opcodes.ACC_PUBLIC, 
32                 JavaTypeTranslator.filterVoid(types));
33         mb.loadThis();
34         mb.invokeConstructor(classBuilder.getSuperClassName(), Constants.EMPTY_TYPEDESC_ARRAY);
35         if(types.length == 0) {  
36             TypeDesc thisClass = classBuilder.getType();
37             classBuilder.addField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, "INSTANCE", thisClass);
38             
39             MethodBuilderBase inimb = classBuilder.addInitializerBase();
40             inimb.newObject(thisClass);
41             inimb.dup();
42             inimb.invokeConstructor(classBuilder.getClassName(), Constants.EMPTY_TYPEDESC_ARRAY);
43             inimb.storeStaticField(classBuilder.getClassName(), "INSTANCE", thisClass);
44             inimb.returnVoid();
45             inimb.finish();
46         }
47         else {
48             for(int i=0,j=0;i<types.length;++i) {
49                 if(!types[i].equals(TypeDesc.VOID)) {
50                     mb.loadThis();
51                     mb.loadLocal(mb.getParameter(j++));
52                     mb.storeField(classBuilder.getClassName(), fieldNamePrefix+i, types[i]);            
53                 }
54             }
55         }
56         mb.returnVoid();
57         mb.finish();
58         
59         // Create toString
60         {
61             MethodBuilderBase tsmb = classBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "toString", TypeDesc.STRING, Constants.EMPTY_TYPEDESC_ARRAY);
62             
63             if(types.length > 0) {
64                 tsmb.newObject(TypeDesc.forClass(StringBuilder.class));
65                 tsmb.dup();
66                 tsmb.invokeConstructor("java/lang/StringBuilder", Constants.EMPTY_TYPEDESC_ARRAY);
67                 
68                 // build string
69                 tsmb.loadConstant("(" + recordName);
70                 StringBuilder_appendString(tsmb);
71                 for(int i=0;i<types.length;++i) {
72                     if(types[i].equals(TypeDesc.VOID)) {
73                         tsmb.loadConstant(" ()");
74                         StringBuilder_appendString(tsmb);
75                     }
76                     else {
77                         tsmb.loadConstant(" ");
78                         StringBuilder_appendString(tsmb);
79                         tsmb.loadThis();
80                         tsmb.loadField(classBuilder.getClassName(), fieldNamePrefix+i, types[i]);
81                         StringBuilder_appendObject(tsmb, types[i]);
82                     }
83                 }
84                 tsmb.loadConstant(")");
85                 StringBuilder_appendString(tsmb);
86                 
87                 // return
88                 tsmb.invokeVirtual("java/lang/StringBuilder", "toString", TypeDesc.STRING, Constants.EMPTY_TYPEDESC_ARRAY);
89             }
90             else
91                 tsmb.loadConstant(recordName);
92             tsmb.returnValue(TypeDesc.STRING);
93             tsmb.finish();
94         }
95         
96         if(generateEqualsAndHashCode)
97             implementHashCodeAndEquals(classBuilder, recordName, fieldNamePrefix, types);
98     }
99
100     public static void implementHashCodeAndEquals(ClassBuilder classBuilder, String recordName, String fieldNamePrefix, TypeDesc[] types) {
101         // Create equals
102         {
103             TypeDesc CLASS = TypeDesc.forClass(Class.class);
104
105             MethodBuilderBase tsmb = classBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "equals", TypeDesc.BOOLEAN, Constants.OBJECTS[1]);
106             LocalVariable parameter = tsmb.getParameter(0);
107             Label success = tsmb.createLabel();
108             Label failure = tsmb.createLabel();
109
110             // Check type
111             tsmb.loadThis();
112             tsmb.loadLocal(parameter);
113             tsmb.ifComparisonBranch(success, "==", TypeDesc.OBJECT);
114             tsmb.loadLocal(parameter);
115             tsmb.ifNullBranch(failure, true);
116             tsmb.loadLocal(parameter);
117             tsmb.invokeVirtual("java/lang/Object", "getClass", CLASS, Constants.EMPTY_TYPEDESC_ARRAY);
118             tsmb.loadThis();
119             tsmb.invokeVirtual("java/lang/Object", "getClass", CLASS, Constants.EMPTY_TYPEDESC_ARRAY);
120             tsmb.ifComparisonBranch(failure, "!=", CLASS);
121             tsmb.loadLocal(parameter);
122             tsmb.checkCast(classBuilder.getType());
123             LocalVariable other = tsmb.createLocalVariable("other", classBuilder.getType());
124             tsmb.storeLocal(other);
125
126             // Compare fields
127             for(int i=0;i<types.length;++i) {
128                 TypeDesc type = types[i];
129                 if(type.equals(TypeDesc.VOID))
130                     continue;
131                 tsmb.loadThis();
132                 tsmb.loadField(classBuilder.getClassName(), fieldNamePrefix+i, type);
133                 tsmb.loadLocal(other);
134                 tsmb.loadField(classBuilder.getClassName(), fieldNamePrefix+i, type);
135                 equals(tsmb, type, failure);
136             }
137
138             // Return
139             tsmb.setLocation(success);
140             tsmb.loadConstant(true);
141             tsmb.returnValue(TypeDesc.BOOLEAN);
142             tsmb.setLocation(failure);
143             tsmb.loadConstant(false);
144             tsmb.returnValue(TypeDesc.BOOLEAN);
145             tsmb.finish();
146         }
147
148         // Create hashCode
149         {
150             MethodBuilderBase tsmb = classBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "hashCode", TypeDesc.INT, Constants.EMPTY_TYPEDESC_ARRAY);
151             tsmb.loadConstant(recordName.hashCode());
152             for(int i=0;i<types.length;++i) {
153                 TypeDesc type = types[i];
154                 if(type.equals(TypeDesc.VOID))
155                     continue;
156                 tsmb.loadConstant(31);
157                 tsmb.math(Opcodes.IMUL);
158                 tsmb.loadThis();
159                 tsmb.loadField(classBuilder.getClassName(), fieldNamePrefix+i, type);
160                 hashCode(tsmb, type);
161                 tsmb.math(Opcodes.IADD);
162             }
163             tsmb.returnValue(TypeDesc.INT);
164             tsmb.finish();
165         }
166     }
167     
168     public static void equals(MethodBuilderBase mb, TypeDesc typeDesc, Label failure) {
169         if(typeDesc.isPrimitive())
170             mb.ifComparisonBranch(failure, "!=", typeDesc);
171         else {
172             Label isNull = mb.createLabel();
173             Label finished = mb.createLabel();
174             mb.swap();
175             mb.dup();
176             mb.ifNullBranch(isNull, true);
177             mb.swap();
178             mb.invokeVirtual("java/lang/Object", "equals", TypeDesc.BOOLEAN, Constants.OBJECTS[1]);
179             mb.ifZeroComparisonBranch(failure, "==");
180             mb.branch(finished);
181             mb.setLocation(isNull);
182             mb.pop();
183             mb.ifNullBranch(failure, false);
184             mb.setLocation(finished);
185         }
186     }
187     
188     /**
189      * Calculates the hash code of a value in stack.
190      */
191     public static void hashCode(MethodBuilderBase mb, TypeDesc typeDesc) {
192         switch(typeDesc.getTypeCode()) {
193         case TypeDesc.INT_CODE:
194             break;
195         case TypeDesc.OBJECT_CODE: {
196             Label isNull = mb.createLabel();
197             Label finished = mb.createLabel();
198             mb.dup();
199             mb.ifNullBranch(isNull, true);
200             mb.invokeVirtual("java/lang/Object", "hashCode", TypeDesc.INT, Constants.EMPTY_TYPEDESC_ARRAY);
201             mb.branch(finished);
202             mb.setLocation(isNull);
203             mb.pop();
204             mb.loadConstant(0);
205             mb.setLocation(finished);
206         } break;
207         case TypeDesc.DOUBLE_CODE:
208             mb.invokeStatic("java/lang/Double", "doubleToLongBits", TypeDesc.LONG, new TypeDesc[] { TypeDesc.DOUBLE });
209         case TypeDesc.LONG_CODE:
210             mb.dup2();
211             mb.loadConstant(32);
212             mb.math(Opcodes.LSHR);
213             mb.math(Opcodes.LXOR);
214             mb.convert(TypeDesc.LONG, TypeDesc.INT);
215             break;
216         case TypeDesc.FLOAT_CODE:
217             mb.invokeStatic("java/lang/Float", "floatToIntBits", TypeDesc.INT, new TypeDesc[] { TypeDesc.FLOAT });
218             break;
219         default:
220             mb.convert(typeDesc, TypeDesc.INT);
221         }
222     }
223     
224     public static void constructRecord(TypeDesc clazz, MethodBuilder mb,
225             Type[] parameterTypes, Val... parameters) {
226         if(parameters.length == 0) {
227             mb.loadStaticField(clazz, "INSTANCE", clazz);
228         }
229         else {
230             mb.newObject(clazz);
231             mb.dup();
232             for(int i=0;i<parameters.length;++i)
233                 mb.push(parameters[i], parameterTypes[i]);
234             JavaTypeTranslator tt = mb.moduleBuilder.getJavaTypeTranslator();
235             mb.invokeConstructor(clazz, JavaTypeTranslator.filterVoid( 
236                     tt.toTypeDescs(Arrays.copyOf(parameterTypes, parameters.length))));
237         }
238     }
239     
240     public static void StringBuilder_appendString(MethodBuilderBase mb) {
241         mb.invokeVirtual("java/lang/StringBuilder", "append", TypeDesc.forClass("java.lang.StringBuilder"), 
242                 new TypeDesc[] {TypeDesc.STRING});
243     }
244     
245     public static void StringBuilder_appendObject(MethodBuilderBase mb, TypeDesc type) {
246         if(!type.isPrimitive() && type != TypeDesc.STRING)
247             type = TypeDesc.OBJECT;
248         mb.invokeVirtual("java/lang/StringBuilder", "append", TypeDesc.forClass("java.lang.StringBuilder"), 
249                 new TypeDesc[] {type});
250     }
251     
252 }