]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/constants/FunctionValue.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / constants / FunctionValue.java
diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/constants/FunctionValue.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/constants/FunctionValue.java
new file mode 100644 (file)
index 0000000..d37b750
--- /dev/null
@@ -0,0 +1,201 @@
+package org.simantics.scl.compiler.constants;
+
+import java.util.Arrays;
+
+import org.cojen.classfile.TypeDesc;
+import org.objectweb.asm.Opcodes;
+import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
+import org.simantics.scl.compiler.internal.codegen.references.Val;
+import org.simantics.scl.compiler.internal.codegen.types.BTypes;
+import org.simantics.scl.compiler.internal.codegen.utils.ClassBuilder;
+import org.simantics.scl.compiler.internal.codegen.utils.CodeBuilderUtils;
+import org.simantics.scl.compiler.internal.codegen.utils.Constants;
+import org.simantics.scl.compiler.internal.codegen.utils.JavaNamingPolicy;
+import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;
+import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilderBase;
+import org.simantics.scl.compiler.internal.codegen.utils.ModuleBuilder;
+import org.simantics.scl.compiler.internal.codegen.utils.TransientClassBuilder;
+import org.simantics.scl.compiler.runtime.MutableClassLoader;
+import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
+import org.simantics.scl.compiler.types.TVar;
+import org.simantics.scl.compiler.types.Type;
+import org.simantics.scl.compiler.types.Types;
+import org.simantics.scl.compiler.types.exceptions.MatchException;
+
+import gnu.trove.map.hash.THashMap;
+
+public abstract class FunctionValue extends Constant {
+
+    TVar[] typeParameters;
+    Type returnType;
+    protected Type[] parameterTypes;
+    Type effect;
+        
+    public FunctionValue(TVar[] typeParameters, Type effect, Type returnType, Type ... parameterTypes) {
+        super(Types.forAll(typeParameters, 
+                Types.functionE(parameterTypes, effect, returnType)));
+        this.typeParameters = typeParameters;
+        this.returnType = returnType;
+        this.parameterTypes = parameterTypes;
+        this.effect = effect;
+    }
+
+    public Type getReturnType() {
+        return returnType;
+    }
+
+    public Type[] getParameterTypes() {
+        return parameterTypes;
+    }
+    
+    public TVar[] getTypeParameters() {
+        return typeParameters;
+    }
+    
+    @Override
+    public int getArity() {
+        return parameterTypes.length;
+    }
+
+    @Override
+    public void push(MethodBuilder mb) {
+        apply(mb, Type.EMPTY_ARRAY);
+    }
+        
+    @Override
+    public Type apply(MethodBuilder mb, Type[] typeParameters, Val... parameters) {
+        int arity = getArity();
+        
+        /*System.out.println("MONADIC APPLICATION " + this);
+        System.out.println("    call arity: " + parameters.length);
+        System.out.println("    func arity: " + arity);
+        System.out.println("    func monadic: " + isMonadic());
+        */
+        if(parameters.length < arity) {
+            ModuleBuilder moduleBuilder = mb.getModuleBuilder();            
+            TypeDesc closureClass = moduleBuilder.getClosure(this, parameters.length);
+            CodeBuilderUtils.constructRecord(closureClass, mb, parameterTypes, parameters);
+            return Types.function(Arrays.copyOfRange(parameterTypes, parameters.length, parameterTypes.length), returnType);
+        }
+        else if(parameters.length > arity) {
+            Type returnType = applyExact(mb, Arrays.copyOf(parameters, arity));
+            mb.pushBoxed(Arrays.copyOfRange(parameters, arity, parameters.length));
+            int remainingArity = parameters.length - arity;
+            mb.genericApply(remainingArity);
+            
+            if(typeParameters.length > 0)
+                returnType = returnType.replace(this.typeParameters, typeParameters);
+            try {
+                returnType = BTypes.matchFunction(returnType, remainingArity)[remainingArity];                
+            } catch (MatchException e) {
+                throw new InternalCompilerError("Tried to apply value of type " + returnType + " with " + remainingArity + " parameters.");
+            }
+            mb.unbox(returnType);
+            return returnType;
+        }
+        else {
+            return applyExact(mb, parameters);
+        }
+    }
+        
+    public abstract Type applyExact(MethodBuilder mb, Val[] parameters);
+    
+    @Override
+    public int getEffectiveArity() {
+        return parameterTypes.length;
+    }
+    
+
+    @Override
+    public Object realizeValue(TransientClassBuilder builder) {
+        THashMap<Constant, Object> valueCache = builder.classLoader.getConstantCache();
+        if(valueCache != null) {
+            Object cachedResult = valueCache.get(this);
+            if(cachedResult != null)
+                return cachedResult;
+        }
+        
+        int arity = getEffectiveArity();
+        if(arity == 0)
+            return super.realizeValue(builder);        
+        
+        String packageName = builder.classLoader.getFreshPackageName();
+        String moduleName = packageName + "/Temp";
+        JavaNamingPolicy policy = new JavaNamingPolicy(moduleName);
+        ModuleBuilder moduleBuilder = new ModuleBuilder(policy, builder.javaTypeTranslator);
+        
+        ClassBuilder classFile;
+        if(arity <= Constants.MAX_FUNCTION_PARAMETER_COUNT) {
+            if(SCLCompilerConfiguration.TRACE_METHOD_CREATION)
+                System.out.println("Create class " + policy.getModuleClassName());
+            classFile = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, policy.getModuleClassName(), 
+                    MethodBuilderBase.getClassName(Constants.FUNCTION_IMPL[arity]));
+            classFile.setSourceFile("_SCL_FunctionValue");
+            classFile.addDefaultConstructor();
+    
+            MethodBuilder mb =classFile.addMethod(Opcodes.ACC_PUBLIC, "apply", TypeDesc.OBJECT, Constants.OBJECTS[arity]);
+            Val[] parameters = new Val[arity];
+            for(int i=0;i<arity;++i)
+                parameters[i] = new LocalVariableConstant(parameterTypes[i], mb.getParameter(i));
+            prepare(mb);
+            mb.box(applyExact(mb, parameters));
+            mb.returnValue(TypeDesc.OBJECT);
+            mb.finish();
+        }
+        else {
+            if(SCLCompilerConfiguration.TRACE_METHOD_CREATION)
+                System.out.println("Create class " + policy.getModuleClassName());
+            classFile = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, policy.getModuleClassName(), 
+                    MethodBuilderBase.getClassName(Constants.FUNCTION_N_IMPL));
+            classFile.setSourceFile("_SCL_FunctionValue");
+            
+            // Constructor
+            { 
+                MethodBuilderBase mb = classFile.addConstructor(Opcodes.ACC_PUBLIC, Constants.EMPTY_TYPEDESC_ARRAY);
+                mb.loadThis();
+                mb.loadConstant(arity);
+                mb.invokeConstructor(MethodBuilderBase.getClassName(Constants.FUNCTION_N_IMPL), new TypeDesc[] {TypeDesc.INT});
+                mb.returnVoid();
+                mb.finish();
+            }
+            
+            // doApply
+            MethodBuilder mb = classFile.addMethod(Opcodes.ACC_PUBLIC, "doApply", TypeDesc.OBJECT, 
+                    new TypeDesc[] {TypeDesc.forClass(Object[].class)});
+            Val[] parameters = new Val[arity];
+            for(int i=0;i<arity;++i)
+                parameters[i] = new LocalBoxedArrayElementConstant(parameterTypes[i], 
+                        mb.getParameter(0), i);
+            applyExact(mb, parameters);
+            mb.box(returnType);
+            mb.returnValue(TypeDesc.OBJECT);
+            mb.finish();
+        }
+        
+        /* Add a toString() method that returns the function name */
+        MethodBuilder mb2 = classFile.addMethod(Opcodes.ACC_PUBLIC, "toString", TypeDesc.STRING, Constants.OBJECTS[0]);
+        mb2.loadConstant(this.toString());
+        mb2.returnValue(TypeDesc.STRING);
+        mb2.finish();
+        
+        moduleBuilder.addClass(classFile);
+        
+        MutableClassLoader classLoader = builder.classLoader;
+        classLoader.addClasses(moduleBuilder.getClasses());
+        try {
+            Object result = classLoader.loadClass(policy.getModuleClassName().replace('/', '.')).newInstance();
+            if(valueCache != null) {
+                valueCache.put(this, result);
+                if(TRACE_REALIZATION)
+                    System.out.println("/REALIZED/ " + this + " " + getClass().getSimpleName());
+            }
+            return result;
+        } catch (InstantiationException e) {
+            throw new InternalCompilerError(e);
+        } catch (IllegalAccessException e) {
+            throw new InternalCompilerError(e);
+        } catch (ClassNotFoundException e) {
+            throw new InternalCompilerError(e);
+        }
+    }
+}