1 package org.simantics.scl.compiler.internal.codegen.utils;
3 import java.util.Arrays;
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;
12 public class CodeBuilderUtils {
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.
18 * @param fieldModifiers
19 * @param fieldNamePrefix
22 public static void makeRecord(ClassBuilder classBuilder, String recordName, int fieldModifiers, String fieldNamePrefix, TypeDesc[] types,
23 boolean generateEqualsAndHashCode) {
25 for(int i=0;i<types.length;++i)
26 if(!types[i].equals(TypeDesc.VOID))
27 classBuilder.addField(fieldModifiers, fieldNamePrefix+i, types[i]);
30 MethodBuilderBase mb = classBuilder.addConstructor(
31 types.length == 0 ? Opcodes.ACC_PRIVATE : Opcodes.ACC_PUBLIC,
32 JavaTypeTranslator.filterVoid(types));
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);
39 MethodBuilderBase inimb = classBuilder.addInitializerBase();
40 inimb.newObject(thisClass);
42 inimb.invokeConstructor(classBuilder.getClassName(), Constants.EMPTY_TYPEDESC_ARRAY);
43 inimb.storeStaticField(classBuilder.getClassName(), "INSTANCE", thisClass);
48 for(int i=0,j=0;i<types.length;++i) {
49 if(!types[i].equals(TypeDesc.VOID)) {
51 mb.loadLocal(mb.getParameter(j++));
52 mb.storeField(classBuilder.getClassName(), fieldNamePrefix+i, types[i]);
61 MethodBuilderBase tsmb = classBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "toString", TypeDesc.STRING, Constants.EMPTY_TYPEDESC_ARRAY);
63 if(types.length > 0) {
64 tsmb.newObject(TypeDesc.forClass(StringBuilder.class));
66 tsmb.invokeConstructor("java/lang/StringBuilder", Constants.EMPTY_TYPEDESC_ARRAY);
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);
77 tsmb.loadConstant(" ");
78 StringBuilder_appendString(tsmb);
80 tsmb.loadField(classBuilder.getClassName(), fieldNamePrefix+i, types[i]);
81 StringBuilder_appendObject(tsmb, types[i]);
84 tsmb.loadConstant(")");
85 StringBuilder_appendString(tsmb);
88 tsmb.invokeVirtual("java/lang/StringBuilder", "toString", TypeDesc.STRING, Constants.EMPTY_TYPEDESC_ARRAY);
91 tsmb.loadConstant(recordName);
92 tsmb.returnValue(TypeDesc.STRING);
96 if(generateEqualsAndHashCode)
97 implementHashCodeAndEquals(classBuilder, recordName, fieldNamePrefix, types);
100 public static void implementHashCodeAndEquals(ClassBuilder classBuilder, String recordName, String fieldNamePrefix, TypeDesc[] types) {
103 TypeDesc CLASS = TypeDesc.forClass(Class.class);
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();
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);
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);
127 for(int i=0;i<types.length;++i) {
128 TypeDesc type = types[i];
129 if(type.equals(TypeDesc.VOID))
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);
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);
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))
156 tsmb.loadConstant(31);
157 tsmb.math(Opcodes.IMUL);
159 tsmb.loadField(classBuilder.getClassName(), fieldNamePrefix+i, type);
160 hashCode(tsmb, type);
161 tsmb.math(Opcodes.IADD);
163 tsmb.returnValue(TypeDesc.INT);
168 public static void equals(MethodBuilderBase mb, TypeDesc typeDesc, Label failure) {
169 if(typeDesc.isPrimitive())
170 mb.ifComparisonBranch(failure, "!=", typeDesc);
172 Label isNull = mb.createLabel();
173 Label finished = mb.createLabel();
176 mb.ifNullBranch(isNull, true);
178 mb.invokeVirtual("java/lang/Object", "equals", TypeDesc.BOOLEAN, Constants.OBJECTS[1]);
179 mb.ifZeroComparisonBranch(failure, "==");
181 mb.setLocation(isNull);
183 mb.ifNullBranch(failure, false);
184 mb.setLocation(finished);
189 * Calculates the hash code of a value in stack.
191 public static void hashCode(MethodBuilderBase mb, TypeDesc typeDesc) {
192 switch(typeDesc.getTypeCode()) {
193 case TypeDesc.INT_CODE:
195 case TypeDesc.OBJECT_CODE: {
196 Label isNull = mb.createLabel();
197 Label finished = mb.createLabel();
199 mb.ifNullBranch(isNull, true);
200 mb.invokeVirtual("java/lang/Object", "hashCode", TypeDesc.INT, Constants.EMPTY_TYPEDESC_ARRAY);
202 mb.setLocation(isNull);
205 mb.setLocation(finished);
207 case TypeDesc.DOUBLE_CODE:
208 mb.invokeStatic("java/lang/Double", "doubleToLongBits", TypeDesc.LONG, new TypeDesc[] { TypeDesc.DOUBLE });
209 case TypeDesc.LONG_CODE:
212 mb.math(Opcodes.LSHR);
213 mb.math(Opcodes.LXOR);
214 mb.convert(TypeDesc.LONG, TypeDesc.INT);
216 case TypeDesc.FLOAT_CODE:
217 mb.invokeStatic("java/lang/Float", "floatToIntBits", TypeDesc.INT, new TypeDesc[] { TypeDesc.FLOAT });
220 mb.convert(typeDesc, TypeDesc.INT);
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);
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))));
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});
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});