1 package org.simantics.scl.compiler.constants;
3 import java.util.Arrays;
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;
27 import gnu.trove.map.hash.THashMap;
29 public abstract class FunctionValue extends Constant {
31 private static final Logger LOGGER = LoggerFactory.getLogger(FunctionValue.class);
33 TVar[] typeParameters;
35 protected Type[] parameterTypes;
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;
47 public Type getReturnType() {
51 public Type[] getParameterTypes() {
52 return parameterTypes;
55 public TVar[] getTypeParameters() {
56 return typeParameters;
60 public int getArity() {
61 return parameterTypes.length;
65 public void push(MethodBuilder mb) {
66 apply(mb, Type.EMPTY_ARRAY);
70 public Type apply(MethodBuilder mb, Type[] typeParameters, Val... parameters) {
71 int arity = getArity();
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());
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);
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);
90 if(typeParameters.length > 0)
91 returnType = returnType.replace(this.typeParameters, typeParameters);
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.");
101 return applyExact(mb, parameters);
105 public abstract Type applyExact(MethodBuilder mb, Val[] parameters);
108 public int getEffectiveArity() {
109 return parameterTypes.length;
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)
122 int arity = getEffectiveArity();
124 return super.realizeValue(builder);
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);
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();
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));
145 mb.box(applyExact(mb, parameters));
146 mb.returnValue(TypeDesc.OBJECT);
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");
158 MethodBuilderBase mb = classFile.addConstructorBase(Opcodes.ACC_PUBLIC, Constants.EMPTY_TYPEDESC_ARRAY);
160 mb.loadConstant(arity);
161 mb.invokeConstructor(MethodBuilderBase.getClassName(Constants.FUNCTION_N_IMPL), new TypeDesc[] {TypeDesc.INT});
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);
175 mb.returnValue(TypeDesc.OBJECT);
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);
185 moduleBuilder.addClass(classFile);
187 MutableClassLoader classLoader = builder.classLoader;
188 classLoader.addClasses(moduleBuilder.getClasses());
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());
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);