]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/constants/FunctionValue.java
dcef289f02763d9521532f30bc02bd7242714e52
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / constants / FunctionValue.java
1 package org.simantics.scl.compiler.constants;
2
3 import java.util.Arrays;
4
5 import org.cojen.classfile.TypeDesc;
6 import org.objectweb.asm.Opcodes;
7 import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
8 import org.simantics.scl.compiler.internal.codegen.references.Val;
9 import org.simantics.scl.compiler.internal.codegen.types.BTypes;
10 import org.simantics.scl.compiler.internal.codegen.utils.ClassBuilder;
11 import org.simantics.scl.compiler.internal.codegen.utils.CodeBuilderUtils;
12 import org.simantics.scl.compiler.internal.codegen.utils.Constants;
13 import org.simantics.scl.compiler.internal.codegen.utils.JavaNamingPolicy;
14 import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;
15 import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilderBase;
16 import org.simantics.scl.compiler.internal.codegen.utils.ModuleBuilder;
17 import org.simantics.scl.compiler.internal.codegen.utils.TransientClassBuilder;
18 import org.simantics.scl.compiler.runtime.MutableClassLoader;
19 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
20 import org.simantics.scl.compiler.types.TVar;
21 import org.simantics.scl.compiler.types.Type;
22 import org.simantics.scl.compiler.types.Types;
23 import org.simantics.scl.compiler.types.exceptions.MatchException;
24
25 import gnu.trove.map.hash.THashMap;
26
27 public abstract class FunctionValue extends Constant {
28
29     TVar[] typeParameters;
30     Type returnType;
31     protected Type[] parameterTypes;
32     Type effect;
33         
34     public FunctionValue(TVar[] typeParameters, Type effect, Type returnType, Type ... parameterTypes) {
35         super(Types.forAll(typeParameters, 
36                 Types.functionE(parameterTypes, effect, returnType)));
37         this.typeParameters = typeParameters;
38         this.returnType = returnType;
39         this.parameterTypes = parameterTypes;
40         this.effect = effect;
41     }
42
43     public Type getReturnType() {
44         return returnType;
45     }
46
47     public Type[] getParameterTypes() {
48         return parameterTypes;
49     }
50     
51     public TVar[] getTypeParameters() {
52         return typeParameters;
53     }
54     
55     @Override
56     public int getArity() {
57         return parameterTypes.length;
58     }
59
60     @Override
61     public void push(MethodBuilder mb) {
62         apply(mb, Type.EMPTY_ARRAY);
63     }
64         
65     @Override
66     public Type apply(MethodBuilder mb, Type[] typeParameters, Val... parameters) {
67         int arity = getArity();
68         
69         /*System.out.println("MONADIC APPLICATION " + this);
70         System.out.println("    call arity: " + parameters.length);
71         System.out.println("    func arity: " + arity);
72         System.out.println("    func monadic: " + isMonadic());
73         */
74         if(parameters.length < arity) {
75             ModuleBuilder moduleBuilder = mb.getModuleBuilder();            
76             TypeDesc closureClass = moduleBuilder.getClosure(this, parameters.length);
77             CodeBuilderUtils.constructRecord(closureClass, mb, parameterTypes, parameters);
78             return Types.function(Arrays.copyOfRange(parameterTypes, parameters.length, parameterTypes.length), returnType);
79         }
80         else if(parameters.length > arity) {
81             Type returnType = applyExact(mb, Arrays.copyOf(parameters, arity));
82             mb.pushBoxed(Arrays.copyOfRange(parameters, arity, parameters.length));
83             int remainingArity = parameters.length - arity;
84             mb.genericApply(remainingArity);
85             
86             if(typeParameters.length > 0)
87                 returnType = returnType.replace(this.typeParameters, typeParameters);
88             try {
89                 returnType = BTypes.matchFunction(returnType, remainingArity)[remainingArity];                
90             } catch (MatchException e) {
91                 throw new InternalCompilerError("Tried to apply value of type " + returnType + " with " + remainingArity + " parameters.");
92             }
93             mb.unbox(returnType);
94             return returnType;
95         }
96         else {
97             return applyExact(mb, parameters);
98         }
99     }
100         
101     public abstract Type applyExact(MethodBuilder mb, Val[] parameters);
102     
103     @Override
104     public int getEffectiveArity() {
105         return parameterTypes.length;
106     }
107     
108
109     @Override
110     public Object realizeValue(TransientClassBuilder builder) {
111         THashMap<Constant, Object> valueCache = builder.classLoader.getConstantCache();
112         if(valueCache != null) {
113             Object cachedResult = valueCache.get(this);
114             if(cachedResult != null)
115                 return cachedResult;
116         }
117         
118         int arity = getEffectiveArity();
119         if(arity == 0)
120             return super.realizeValue(builder);        
121         
122         String packageName = builder.classLoader.getFreshPackageName();
123         String moduleName = packageName + "/Temp";
124         JavaNamingPolicy policy = new JavaNamingPolicy(moduleName);
125         ModuleBuilder moduleBuilder = new ModuleBuilder(policy, builder.javaTypeTranslator);
126         
127         ClassBuilder classFile;
128         if(arity <= Constants.MAX_FUNCTION_PARAMETER_COUNT) {
129             if(SCLCompilerConfiguration.TRACE_METHOD_CREATION)
130                 System.out.println("Create class " + policy.getModuleClassName());
131             classFile = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, policy.getModuleClassName(), 
132                     MethodBuilderBase.getClassName(Constants.FUNCTION_IMPL[arity]));
133             classFile.setSourceFile("_SCL_FunctionValue");
134             classFile.addDefaultConstructor();
135     
136             MethodBuilder mb =classFile.addMethod(Opcodes.ACC_PUBLIC, "apply", TypeDesc.OBJECT, Constants.OBJECTS[arity]);
137             Val[] parameters = new Val[arity];
138             for(int i=0;i<arity;++i)
139                 parameters[i] = new LocalVariableConstant(parameterTypes[i], mb.getParameter(i));
140             prepare(mb);
141             mb.box(applyExact(mb, parameters));
142             mb.returnValue(TypeDesc.OBJECT);
143             mb.finish();
144         }
145         else {
146             if(SCLCompilerConfiguration.TRACE_METHOD_CREATION)
147                 System.out.println("Create class " + policy.getModuleClassName());
148             classFile = new ClassBuilder(moduleBuilder, Opcodes.ACC_PUBLIC, policy.getModuleClassName(), 
149                     MethodBuilderBase.getClassName(Constants.FUNCTION_N_IMPL));
150             classFile.setSourceFile("_SCL_FunctionValue");
151             
152             // Constructor
153             { 
154                 MethodBuilderBase mb = classFile.addConstructorBase(Opcodes.ACC_PUBLIC, Constants.EMPTY_TYPEDESC_ARRAY);
155                 mb.loadThis();
156                 mb.loadConstant(arity);
157                 mb.invokeConstructor(MethodBuilderBase.getClassName(Constants.FUNCTION_N_IMPL), new TypeDesc[] {TypeDesc.INT});
158                 mb.returnVoid();
159                 mb.finish();
160             }
161             
162             // doApply
163             MethodBuilder mb = classFile.addMethod(Opcodes.ACC_PUBLIC, "doApply", TypeDesc.OBJECT, 
164                     new TypeDesc[] {TypeDesc.forClass(Object[].class)});
165             Val[] parameters = new Val[arity];
166             for(int i=0;i<arity;++i)
167                 parameters[i] = new LocalBoxedArrayElementConstant(parameterTypes[i], 
168                         mb.getParameter(0), i);
169             applyExact(mb, parameters);
170             mb.box(returnType);
171             mb.returnValue(TypeDesc.OBJECT);
172             mb.finish();
173         }
174         
175         /* Add a toString() method that returns the function name */
176         MethodBuilder mb2 = classFile.addMethod(Opcodes.ACC_PUBLIC, "toString", TypeDesc.STRING, Constants.OBJECTS[0]);
177         mb2.loadConstant(this.toString());
178         mb2.returnValue(TypeDesc.STRING);
179         mb2.finish();
180         
181         moduleBuilder.addClass(classFile);
182         
183         MutableClassLoader classLoader = builder.classLoader;
184         classLoader.addClasses(moduleBuilder.getClasses());
185         try {
186             Object result = classLoader.loadClass(policy.getModuleClassName().replace('/', '.')).newInstance();
187             if(valueCache != null) {
188                 valueCache.put(this, result);
189                 if(TRACE_REALIZATION)
190                     System.out.println("/REALIZED/ " + this + " " + getClass().getSimpleName());
191             }
192             return result;
193         } catch (InstantiationException e) {
194             throw new InternalCompilerError(e);
195         } catch (IllegalAccessException e) {
196             throw new InternalCompilerError(e);
197         } catch (ClassNotFoundException e) {
198             throw new InternalCompilerError(e);
199         }
200     }
201 }