]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/utils/CodeBuilderUtils.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / internal / codegen / utils / CodeBuilderUtils.java
diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/utils/CodeBuilderUtils.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/internal/codegen/utils/CodeBuilderUtils.java
new file mode 100644 (file)
index 0000000..ec44be7
--- /dev/null
@@ -0,0 +1,238 @@
+package org.simantics.scl.compiler.internal.codegen.utils;
+
+import java.util.Arrays;
+
+import org.cojen.classfile.TypeDesc;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+import org.simantics.scl.compiler.internal.codegen.references.Val;
+import org.simantics.scl.compiler.internal.codegen.types.JavaTypeTranslator;
+import org.simantics.scl.compiler.types.Type;
+
+public class CodeBuilderUtils {
+    
+    /**
+     * Creates fields c0,...,c{N-1} to the given class, where c is fieldNamePrefix and N is the length of types.
+     * Creates also a constructor for the fields. 
+     * @param classFile
+     * @param fieldModifiers
+     * @param fieldNamePrefix
+     * @param types
+     */
+    public static void makeRecord(ClassBuilder classBuilder, String recordName, int fieldModifiers, String fieldNamePrefix, TypeDesc[] types,
+            boolean generateEqualsAndHashCode) {
+        // Create fields
+        for(int i=0;i<types.length;++i)
+            if(!types[i].equals(TypeDesc.VOID))
+                classBuilder.addField(fieldModifiers, fieldNamePrefix+i, types[i]);
+        
+        // Create constructor        
+        MethodBuilderBase mb = classBuilder.addConstructor(
+                types.length == 0 ? Opcodes.ACC_PRIVATE : Opcodes.ACC_PUBLIC, 
+                JavaTypeTranslator.filterVoid(types));
+        mb.loadThis();
+        mb.invokeConstructor(classBuilder.getSuperClassName(), Constants.EMPTY_TYPEDESC_ARRAY);
+        if(types.length == 0) {  
+            TypeDesc thisClass = classBuilder.getType();
+            classBuilder.addField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, "INSTANCE", thisClass);
+            
+            MethodBuilderBase inimb = classBuilder.addInitializerBase();
+            inimb.newObject(thisClass);
+            inimb.dup();
+            inimb.invokeConstructor(classBuilder.getClassName(), Constants.EMPTY_TYPEDESC_ARRAY);
+            inimb.storeStaticField(classBuilder.getClassName(), "INSTANCE", thisClass);
+            inimb.returnVoid();
+            inimb.finish();
+        }
+        else {
+            for(int i=0,j=0;i<types.length;++i) {
+                if(!types[i].equals(TypeDesc.VOID)) {
+                    mb.loadThis();
+                    mb.loadLocal(mb.getParameter(j++));
+                    mb.storeField(classBuilder.getClassName(), fieldNamePrefix+i, types[i]);            
+                }
+            }
+        }
+        mb.returnVoid();
+        mb.finish();
+        
+        // Create toString
+        {
+            MethodBuilderBase tsmb = classBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "toString", TypeDesc.STRING, Constants.EMPTY_TYPEDESC_ARRAY);
+            
+            if(types.length > 0) {
+                tsmb.newObject(TypeDesc.forClass(StringBuilder.class));
+                tsmb.dup();
+                tsmb.invokeConstructor("java/lang/StringBuilder", Constants.EMPTY_TYPEDESC_ARRAY);
+                
+                // build string
+                tsmb.loadConstant("(" + recordName);
+                StringBuilder_appendString(tsmb);
+                for(int i=0;i<types.length;++i) {
+                    if(types[i].equals(TypeDesc.VOID)) {
+                        tsmb.loadConstant(" ()");
+                        StringBuilder_appendString(tsmb);
+                    }
+                    else {
+                        tsmb.loadConstant(" ");
+                        StringBuilder_appendString(tsmb);
+                        tsmb.loadThis();
+                        tsmb.loadField(classBuilder.getClassName(), fieldNamePrefix+i, types[i]);
+                        StringBuilder_appendObject(tsmb, types[i]);
+                    }
+                }
+                tsmb.loadConstant(")");
+                StringBuilder_appendString(tsmb);
+                
+                // return
+                tsmb.invokeVirtual("java/lang/StringBuilder", "toString", TypeDesc.STRING, Constants.EMPTY_TYPEDESC_ARRAY);
+            }
+            else
+                tsmb.loadConstant(recordName);
+            tsmb.returnValue(TypeDesc.STRING);
+            tsmb.finish();
+        }
+        
+        if(generateEqualsAndHashCode) {
+            // Create equals
+            {
+                TypeDesc CLASS = TypeDesc.forClass(Class.class);
+
+                MethodBuilderBase tsmb = classBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "equals", TypeDesc.BOOLEAN, Constants.OBJECTS[1]);
+                LocalVariable parameter = tsmb.getParameter(0);
+                Label success = tsmb.createLabel();
+                Label failure = tsmb.createLabel();
+
+                // Check type
+                tsmb.loadThis();
+                tsmb.loadLocal(parameter);
+                tsmb.ifComparisonBranch(success, "==", TypeDesc.OBJECT);
+                tsmb.loadLocal(parameter);
+                tsmb.ifNullBranch(failure, true);
+                tsmb.loadLocal(parameter);
+                tsmb.invokeVirtual("java/lang/Object", "getClass", CLASS, Constants.EMPTY_TYPEDESC_ARRAY);
+                tsmb.loadThis();
+                tsmb.invokeVirtual("java/lang/Object", "getClass", CLASS, Constants.EMPTY_TYPEDESC_ARRAY);
+                tsmb.ifComparisonBranch(failure, "!=", CLASS);
+                tsmb.loadLocal(parameter);
+                tsmb.checkCast(classBuilder.getType());
+                LocalVariable other = tsmb.createLocalVariable("other", classBuilder.getType());
+                tsmb.storeLocal(other);
+
+                // Compare fields
+                for(int i=0;i<types.length;++i) {
+                    TypeDesc type = types[i];
+                    if(type.equals(TypeDesc.VOID))
+                        continue;
+                    tsmb.loadThis();
+                    tsmb.loadField(classBuilder.getClassName(), fieldNamePrefix+i, type);
+                    tsmb.loadLocal(other);
+                    tsmb.loadField(classBuilder.getClassName(), fieldNamePrefix+i, type);
+                    if(type.isPrimitive())
+                        tsmb.ifComparisonBranch(failure, "!=", type);
+                    else {
+                        Label isNull = tsmb.createLabel();
+                        Label finished = tsmb.createLabel();
+                        tsmb.swap();
+                        tsmb.dup();
+                        tsmb.ifNullBranch(isNull, true);
+                        tsmb.swap();
+                        tsmb.invokeVirtual("java/lang/Object", "equals", TypeDesc.BOOLEAN, Constants.OBJECTS[1]);
+                        tsmb.ifZeroComparisonBranch(failure, "==");
+                        tsmb.branch(finished);
+                        tsmb.setLocation(isNull);
+                        tsmb.pop();
+                        tsmb.ifNullBranch(failure, false);
+                        tsmb.setLocation(finished);
+                    }
+                }
+
+                // Return
+                tsmb.setLocation(success);
+                tsmb.loadConstant(true);
+                tsmb.returnValue(TypeDesc.BOOLEAN);
+                tsmb.setLocation(failure);
+                tsmb.loadConstant(false);
+                tsmb.returnValue(TypeDesc.BOOLEAN);
+                tsmb.finish();
+            }
+
+            // Create hashCode
+            {
+                MethodBuilderBase tsmb = classBuilder.addMethodBase(Opcodes.ACC_PUBLIC, "hashCode", TypeDesc.INT, Constants.EMPTY_TYPEDESC_ARRAY);
+                tsmb.loadConstant(recordName.hashCode());
+                for(int i=0;i<types.length;++i) {
+                    TypeDesc type = types[i];
+                    if(type.equals(TypeDesc.VOID))
+                        continue;
+                    tsmb.loadConstant(31);
+                    tsmb.math(Opcodes.IMUL);
+                    tsmb.loadThis();
+                    tsmb.loadField(classBuilder.getClassName(), fieldNamePrefix+i, type);
+                    switch(type.getTypeCode()) {
+                    case TypeDesc.INT_CODE:
+                        break;
+                    case TypeDesc.OBJECT_CODE: {
+                        Label isNull = tsmb.createLabel();
+                        Label finished = tsmb.createLabel();
+                        tsmb.dup();
+                        tsmb.ifNullBranch(isNull, true);
+                        tsmb.invokeVirtual("java/lang/Object", "hashCode", TypeDesc.INT, Constants.EMPTY_TYPEDESC_ARRAY);
+                        tsmb.branch(finished);
+                        tsmb.setLocation(isNull);
+                        tsmb.pop();
+                        tsmb.loadConstant(0);
+                        tsmb.setLocation(finished);
+                    } break;
+                    case TypeDesc.DOUBLE_CODE:
+                        tsmb.invokeStatic("java/lang/Double", "doubleToLongBits", TypeDesc.LONG, new TypeDesc[] { TypeDesc.DOUBLE });
+                    case TypeDesc.LONG_CODE:
+                        tsmb.dup2();
+                        tsmb.loadConstant(32);
+                        tsmb.math(Opcodes.LSHR);
+                        tsmb.math(Opcodes.LXOR);
+                        tsmb.convert(TypeDesc.LONG, TypeDesc.INT);
+                        break;
+                    case TypeDesc.FLOAT_CODE:
+                        tsmb.invokeStatic("java/lang/Float", "floatToIntBits", TypeDesc.INT, new TypeDesc[] { TypeDesc.FLOAT });
+                        break;
+                    default:
+                        tsmb.convert(type, TypeDesc.INT);
+                    }
+                    tsmb.math(Opcodes.IADD);
+                }
+                tsmb.returnValue(TypeDesc.INT);
+                tsmb.finish();
+            }
+        }
+    }
+    
+    public static void constructRecord(TypeDesc clazz, MethodBuilder mb,
+            Type[] parameterTypes, Val... parameters) {
+        if(parameters.length == 0) {
+            mb.loadStaticField(clazz, "INSTANCE", clazz);
+        }
+        else {
+            mb.newObject(clazz);
+            mb.dup();
+            for(int i=0;i<parameters.length;++i)
+                mb.push(parameters[i], parameterTypes[i]);
+            JavaTypeTranslator tt = mb.moduleBuilder.getJavaTypeTranslator();
+            mb.invokeConstructor(clazz, JavaTypeTranslator.filterVoid( 
+                    tt.toTypeDescs(Arrays.copyOf(parameterTypes, parameters.length))));
+        }
+    }
+    
+    public static void StringBuilder_appendString(MethodBuilderBase mb) {
+        mb.invokeVirtual("java/lang/StringBuilder", "append", TypeDesc.forClass("java.lang.StringBuilder"), 
+                new TypeDesc[] {TypeDesc.STRING});
+    }
+    
+    public static void StringBuilder_appendObject(MethodBuilderBase mb, TypeDesc type) {
+        if(!type.isPrimitive() && type != TypeDesc.STRING)
+            type = TypeDesc.OBJECT;
+        mb.invokeVirtual("java/lang/StringBuilder", "append", TypeDesc.forClass("java.lang.StringBuilder"), 
+                new TypeDesc[] {type});
+    }
+    
+}