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