/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * JavaDefinitionBuilder.java * Creation date: Oct 6, 2003. * By: Edward Lam */ package org.openquark.cal.internal.machine.lecc; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.openquark.cal.compiler.CompilerMessageLogger; import org.openquark.cal.compiler.CoreFunction; import org.openquark.cal.compiler.DataConstructor; import org.openquark.cal.compiler.Expression; import org.openquark.cal.compiler.ExpressionAnalyzer; import org.openquark.cal.compiler.FieldName; import org.openquark.cal.compiler.ModuleName; import org.openquark.cal.compiler.ModuleTypeInfo; import org.openquark.cal.compiler.QualifiedName; import org.openquark.cal.compiler.TypeConstructor; import org.openquark.cal.compiler.TypeExpr; import org.openquark.cal.internal.javamodel.JavaClassRep; import org.openquark.cal.internal.javamodel.JavaConstructor; import org.openquark.cal.internal.javamodel.JavaExpression; import org.openquark.cal.internal.javamodel.JavaFieldDeclaration; import org.openquark.cal.internal.javamodel.JavaMethod; import org.openquark.cal.internal.javamodel.JavaOperator; import org.openquark.cal.internal.javamodel.JavaStatement; import org.openquark.cal.internal.javamodel.JavaTypeName; import org.openquark.cal.internal.javamodel.JavaExpression.Assignment; import org.openquark.cal.internal.javamodel.JavaExpression.CastExpression; import org.openquark.cal.internal.javamodel.JavaExpression.ClassInstanceCreationExpression; import org.openquark.cal.internal.javamodel.JavaExpression.InstanceOf; import org.openquark.cal.internal.javamodel.JavaExpression.JavaField; import org.openquark.cal.internal.javamodel.JavaExpression.LiteralWrapper; import org.openquark.cal.internal.javamodel.JavaExpression.LocalName; import org.openquark.cal.internal.javamodel.JavaExpression.LocalVariable; import org.openquark.cal.internal.javamodel.JavaExpression.MethodInvocation; import org.openquark.cal.internal.javamodel.JavaExpression.MethodVariable; import org.openquark.cal.internal.javamodel.JavaExpression.OperatorExpression; import org.openquark.cal.internal.javamodel.JavaStatement.AssertStatement; import org.openquark.cal.internal.javamodel.JavaStatement.Block; import org.openquark.cal.internal.javamodel.JavaStatement.ExpressionStatement; import org.openquark.cal.internal.javamodel.JavaStatement.IfThenElseStatement; import org.openquark.cal.internal.javamodel.JavaStatement.JavaDocComment; import org.openquark.cal.internal.javamodel.JavaStatement.LineComment; import org.openquark.cal.internal.javamodel.JavaStatement.LocalVariableDeclaration; import org.openquark.cal.internal.javamodel.JavaStatement.ReturnStatement; import org.openquark.cal.internal.javamodel.JavaStatement.SwitchStatement; import org.openquark.cal.internal.javamodel.JavaStatement.ThrowStatement; import org.openquark.cal.internal.javamodel.JavaStatement.UnconditionalLoop; import org.openquark.cal.internal.machine.CodeGenerationException; import org.openquark.cal.internal.machine.lecc.SCJavaDefn.KernelLiteral; import org.openquark.cal.internal.machine.lecc.SCJavaDefn.ReferencedDCInfo; import org.openquark.cal.internal.machine.lecc.SCJavaDefn.Scheme; import org.openquark.cal.internal.runtime.lecc.LECCMachineConfiguration; import org.openquark.cal.internal.runtime.lecc.RTFullApp; import org.openquark.cal.internal.serialization.RecordInputStream; import org.openquark.cal.internal.serialization.RecordOutputStream; import org.openquark.cal.machine.MachineFunction; import org.openquark.cal.module.Cal.Core.CAL_Prelude; /** * A class to build a Java representation of a CAL functional agent. * @author Edward Lam */ final class JavaDefinitionBuilder { static final JavaTypeName[] EMPTY_TYPE_NAME_ARRAY = new JavaTypeName[0]; private static final String ROOT_NODE = "$rootNode"; private static final MethodVariable METHODVAR_ROOT_NODE = new MethodVariable(ROOT_NODE); private static final String CURRENT_ROOT_NODE = "$currentRootNode"; private static final LocalVariable LOCALVAR_CURRENT_ROOT_NODE = new LocalVariable(CURRENT_ROOT_NODE, JavaTypeNames.RTVALUE); private static final MethodVariable METHODVAR_TAGVAL = new MethodVariable("tagVal"); private static final MethodVariable METHODVAR_ORDINAL = new MethodVariable("ordinal"); private static final MethodVariable METHODVAR_DEEPSEQ = new MethodVariable("deepSeq"); private static final MethodVariable METHODVAR_RHS = new MethodVariable("rhs"); private static final MethodVariable METHODVAR_FIELDINDEX = new MethodVariable("fieldIndex"); /** * Constructor for a JavaDefinitionBuilder */ private JavaDefinitionBuilder () { // Since the JavaDefinitionBuilder is stateless we do nothing here. // The constructor is made private to control creation. } /** * Get the object representation for a Java class representing the given supercombinator. * @param machineFunctions * @param module * @param codeGenerationStats - the object for collection code generation information, can be null * @return JavaClassRep an object representation for the given code label. * @throws CodeGenerationException */ static final JavaClassRep getSCDefinition(LECCModule.FunctionGroupInfo machineFunctions, LECCModule module, CodeGenerationStats codeGenerationStats) throws CodeGenerationException { return SCDefinitionBuilder.getSCDefinition(machineFunctions, module, codeGenerationStats); } /** * Get the object representation for a Java class representing the data type. * @param typeCons the type constructor. * @param module the LECCModule instance corresponding to the module defining the entity. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @param codeGenerationStats - the object for collection code generation information, can be null * @return JavaClassRep an object representation for the given code label. * @throws CodeGenerationException */ static final JavaClassRep getDataTypeDefinition(TypeConstructor typeCons, LECCModule module, CodeGenerationStats codeGenerationStats) throws CodeGenerationException { return DataTypeDefinitionBuilder.getDataTypeDefinition(typeCons, module, codeGenerationStats); } /** * Get the object representation for a Java class by name. * The returned class will not have any inner class info set. * * @param module the module in which the represented entity exists. * @param unqualifiedClassName the unqualified class name (ie. without the package name) * @return JavaClassRep an object representation for the given class. * @throws CodeGenerationException */ static final JavaClassRep getClassRep(LECCModule module, String unqualifiedClassName) throws CodeGenerationException { // Get the outer class name if this is an inner class. int lastDollarIndex = unqualifiedClassName.indexOf('$'); String outerClassName = lastDollarIndex < 0 ? unqualifiedClassName : unqualifiedClassName.substring(0, lastDollarIndex); ModuleName moduleName = module.getName(); // If the unqualified class name is the name of a class representing a type, get the type's name. String typeName = CALToJavaNames.getUnqualifiedTypeNameFromClassName(moduleName, outerClassName, module); // Note that below we pass in null for code generation stats // The stats object is used to track stats for generation of the whole module or program (which we aren't doing here). // We can pass a non-null object if in future we want to track stats for a single program. if (typeName != null) { // Get the class from the type definition builder. TypeConstructor typeConstructor = module.getModuleTypeInfo().getTypeConstructor(typeName); return DataTypeDefinitionBuilder.getClassRep(typeConstructor, unqualifiedClassName, module, null); } else { // Get the class from the function definition builder. String functionName = CALToJavaNames.getUnqualifiedFunctionNameFromClassName(moduleName, outerClassName, module); MachineFunction mf = module.getFunction(functionName); LECCModule.FunctionGroupInfo mfs = module.getFunctionGroupInfo(mf); return SCDefinitionBuilder.getClassRep(mfs, module, unqualifiedClassName, null); } } /** * Get the object representation for a Java class by name. * The returned class *will* inner class info set. * * @param module the module in which the represented entity exists. * @param unqualifiedClassName the unqualified class name (ie. without the package name) * @return JavaClassRep an object representation for the given class. * @throws CodeGenerationException */ static final JavaClassRep getClassRepWithInnerClasses(LECCModule module, String unqualifiedClassName) throws CodeGenerationException { // Get the outer class name if this is an inner class. int lastDollarIndex = unqualifiedClassName.indexOf('$'); String outerClassName = lastDollarIndex < 0 ? unqualifiedClassName : unqualifiedClassName.substring(0, lastDollarIndex); ModuleName moduleName = module.getName(); // If the unqualified class name is the name of a class representing a type, get the type's name. String typeName = CALToJavaNames.getUnqualifiedTypeNameFromClassName(moduleName, outerClassName, module); // Note that below we pass in null for code generation stats // The stats object is used to track stats for generation of the whole module or program (which we aren't doing here). // We can pass a non-null object if in future we want to track stats for a single program. if (typeName != null) { // Get the class from the type definition builder. TypeConstructor typeConstructor = module.getModuleTypeInfo().getTypeConstructor(typeName); return DataTypeDefinitionBuilder.getDataTypeDefinition(typeConstructor, module, null); } else { // Get the class from the function definition builder. String functionName = CALToJavaNames.getUnqualifiedFunctionNameFromClassName(moduleName, outerClassName, module); MachineFunction mf = module.getFunction(functionName); LECCModule.FunctionGroupInfo mfs = module.getFunctionGroupInfo(mf); return SCDefinitionBuilder.getSCDefinition(mfs, module, null); } } /** * Returns a LiteralWrapper instance which is the default value for the given type. * @param forType * @return a LiteralWrapper instance which is the default value for the given type. */ static final LiteralWrapper getDefaultValueForType (JavaTypeName forType) { if (forType == null) { return LiteralWrapper.NULL; } else if (forType.equals(JavaTypeName.BOOLEAN)) { return LiteralWrapper.FALSE; } else if (forType.equals(JavaTypeName.BYTE)) { return LiteralWrapper.make(Byte.valueOf(((byte) 0))); } else if (forType.equals(JavaTypeName.CHAR)) { return LiteralWrapper.make(Character.valueOf(' ')); } else if (forType.equals(JavaTypeName.DOUBLE)) { return LiteralWrapper.make(new Double(-1)); } else if (forType.equals(JavaTypeName.FLOAT)) { return LiteralWrapper.make(new Float(-1)); } else if (forType.equals(JavaTypeName.INT)) { return LiteralWrapper.make(Integer.valueOf(-1)); } else if (forType.equals(JavaTypeName.LONG)) { return LiteralWrapper.make(Long.valueOf(-1)); } else if (forType.equals(JavaTypeName.SHORT)) { return LiteralWrapper.make(Short.valueOf(((short) -1))); } else { return LiteralWrapper.NULL; } } /** * An SCDefinitionBuilder builds an internal object representation of a Java * class file for a supercombinator. * * @author Edward Lam */ static final class SCDefinitionBuilder { private static final JavaTypeName _0TypeName = JavaTypeName.make(RTFullApp.General._0.class); private static final String functionTagFieldName = "scTag"; /** The name of the class for the sc being built. */ private final JavaTypeName className; /** The functions that will be in represented by the * generated class. */ private final LECCModule.FunctionGroupInfo functions; /** The Java class representation for the supercombinator. */ private JavaClassRep javaClassRep; /** * The LECCModule instance corresponding to either the module defining * the entity. This is used for obtaining the appropriate * {@link LECCModule.ClassNameMapper} for use in mapping names. */ private final LECCModule module; private static boolean CONSOLIDATE_FUNCTIONS = LECCMachineConfiguration.CONSOLIDATE_FUNCTION_BODIES; private final CodeGenerationStats codeGenerationStats; /** * An array of JavaTypeName of size two. Both members are initialized to JavaTypeNames.RTVALUE. * This field is used when specifying argument types when creating a method call instance. */ private static final JavaTypeName[] TWO_RTVALUES = new JavaTypeName[]{JavaTypeNames.RTVALUE, JavaTypeNames.RTVALUE}; /** * Constructor for an SCDefinitionBuilder. * @param machineFunctions * @param module * @param codeGenerationStats - object to collect code generation info. May be null. */ private SCDefinitionBuilder(LECCModule.FunctionGroupInfo machineFunctions, LECCModule module, CodeGenerationStats codeGenerationStats) { this.className = CALToJavaNames.createTypeNameFromSC(QualifiedName.make(module.getName(), machineFunctions.getFunctionGroupName()), module); this.module = module; this.functions = machineFunctions; this.codeGenerationStats = codeGenerationStats; } /** * Get the object representation for a Java class representing a function by name. * The returned class will not have any inner class info set. * * @param mfs * @param module * @param unqualifiedClassName * @param codeGenerationStats * @return the corresponding class representation, or null if the name is not a class built by this builder. * @throws CodeGenerationException */ static JavaClassRep getClassRep(LECCModule.FunctionGroupInfo mfs, LECCModule module, String unqualifiedClassName, CodeGenerationStats codeGenerationStats) throws CodeGenerationException { SCDefinitionBuilder builder = new SCDefinitionBuilder (mfs, module, codeGenerationStats); // className.getUnqualifiedJavaSourceName() returns a string where inner classes are delimited with '.'. String unqualifiedBuilderClassName = builder.className.getUnqualifiedJavaSourceName().replace('.', '$'); if (unqualifiedClassName.equals(unqualifiedBuilderClassName)) { return builder.getOuterClassRep(); } for (final MachineFunction mf : mfs.getTopLevelCALFunctions()) { QualifiedName calFunctionName = mf.getQualifiedName(); if (unqualifiedClassName.equals(CALToJavaNames.createStrictInnerClassNameFromSC(calFunctionName, module))) { return builder.getStrictAppClass(mf); } if (unqualifiedClassName.equals(CALToJavaNames.createLazyInnerClassNameFromSC(calFunctionName, module))) { return builder.getLazyAppClass(mf); } } return null; } /** * Get the Java representation for the functional form of a given supercombinator. * This will include inner class info. * * @param machineFunctions * @param module * @param codeGenerationStats - object for collecting code generation information * @return JavaClassRep * @throws CodeGenerationException */ static final JavaClassRep getSCDefinition(LECCModule.FunctionGroupInfo machineFunctions, LECCModule module, CodeGenerationStats codeGenerationStats) throws CodeGenerationException { SCDefinitionBuilder instance = new SCDefinitionBuilder (machineFunctions, module, codeGenerationStats); return instance.generateSCDefinition (); } /** * Create the getModuleName() method. * public final String getModuleName() { * return "ModuleName"; * } * */ private final void createMethod_getModuleName () { JavaMethod jm = new JavaMethod (Modifier.PUBLIC | Modifier.FINAL, JavaTypeName.STRING, "getModuleName"); javaClassRep.addMethod(jm); jm.addStatement(new ReturnStatement(LiteralWrapper.make(module.getName().toSourceText()))); } /** * Create the getUnqualifiedName() method. * public final String getModuleName() { * return "unqualifiedName"; * } * */ private final void createMethod_getUnqualifiedName () { JavaMethod jm = new JavaMethod (Modifier.PUBLIC | Modifier.FINAL, JavaTypeName.STRING, "getUnqualifiedName"); javaClassRep.addMethod(jm); if (functions.getNFunctions() > 1) { SwitchStatement switchStatement = new SwitchStatement(new JavaField.Instance(null, SCDefinitionBuilder.functionTagFieldName, JavaTypeName.INT)); for (final MachineFunction mf : functions.getTopLevelCALFunctions()) { switchStatement.addCase( new SwitchStatement.IntCaseGroup( functions.getFunctionIndex(mf.getName()), new ReturnStatement(LiteralWrapper.make(mf.getName())))); } jm.addStatement(switchStatement); MethodInvocation mi = new MethodInvocation.Static (JavaTypeNames.RTVALUE, "badValue_Object", new JavaExpression[]{LiteralWrapper.NULL, LiteralWrapper.make("Bad index in getUnQualifiedName()")}, new JavaTypeName[]{JavaTypeName.ERRORINFO, JavaTypeName.STRING}, JavaTypeName.OBJECT); jm.addStatement(new ReturnStatement(new CastExpression(JavaTypeName.STRING, mi))); } else { for (final MachineFunction mf : functions.getTopLevelCALFunctions()) { jm.addStatement(new ReturnStatement(LiteralWrapper.make(mf.getName()))); } } } /** * Create the getQualifiedName() method. * public final String getQualifiedName () { * return "Module.unqualifiedName"; * } */ private final void createMethod_getQualifiedName () { JavaMethod jm = new JavaMethod (Modifier.PUBLIC | Modifier.FINAL, JavaTypeName.STRING, "getQualifiedName"); javaClassRep.addMethod(jm); if (functions.getNFunctions() > 1) { SwitchStatement switchStatement = new SwitchStatement(new JavaField.Instance(null, SCDefinitionBuilder.functionTagFieldName, JavaTypeName.INT)); for (final MachineFunction mf : functions.getTopLevelCALFunctions()) { switchStatement.addCase( new SwitchStatement.IntCaseGroup( functions.getFunctionIndex(mf.getName()), new ReturnStatement(LiteralWrapper.make(mf.getQualifiedName().getQualifiedName())))); } jm.addStatement(switchStatement); MethodInvocation mi = new MethodInvocation.Static (JavaTypeNames.RTVALUE, "badValue_Object", new JavaExpression[]{LiteralWrapper.NULL, LiteralWrapper.make("Bad index in getQualifiedName()")}, new JavaTypeName[]{JavaTypeName.ERRORINFO, JavaTypeName.STRING}, JavaTypeName.OBJECT); jm.addStatement(new ReturnStatement(new CastExpression(JavaTypeName.STRING, mi))); } else { for (final MachineFunction mf : functions.getTopLevelCALFunctions()) { jm.addStatement( new ReturnStatement(LiteralWrapper.make(mf.getQualifiedName().getQualifiedName()))); } } } /** * Generate the java class representation of the function for which this builder is responsible. * This representation will contain the relevant inner classes. * * @return the generated class representation. * @throws CodeGenerationException */ private final JavaClassRep generateSCDefinition () throws CodeGenerationException { if (this.javaClassRep == null) { generateOuterSCDefinition(); // Now go over the difference CAL functions represented by this class and // generate any special purpose application nodes. for (final MachineFunction mf : functions.getTopLevelCALFunctions()) { boolean hasStrictUnboxableArguments = false; for (int i = 0; i < mf.getArity(); ++i) { if (mf.getParameterStrictness()[i] && SCJavaDefn.canTypeBeUnboxed(mf.getParameterTypes()[i])) { hasStrictUnboxableArguments = true; } } boolean generateFnMethods = mf.getArity() > 0 && (mf.getArity() <= LECCMachineConfiguration.OPTIMIZED_APP_CHAIN_LENGTH || mf.isTailRecursive() || hasStrictUnboxableArguments); boolean generateApplicationClasses = generateFnMethods && (hasStrictUnboxableArguments || mf.isTailRecursive()); if (generateApplicationClasses) { createStrictAppClass(mf); // We only generate a lazy application node class for tail recursive functions with // an arity too large to be handled by one of the built-in lazy application node // classes. We want the lazy app node so that we can release the roots of the // arguments to the tail recursive function. if (mf.isTailRecursive() && mf.getArity() > LECCMachineConfiguration.OPTIMIZED_APP_CHAIN_LENGTH) { createLazyAppClass(mf); } } } } return javaClassRep; } /** * Build the let variable definition functions for this function. * This involves lifting the let variable definitions into their own * functions and then generating the corresponding Java methods. * @param sharedValues * @return a Collection of JavaMethod * @throws CodeGenerationException */ private Collection<JavaMethod> buildLetVarDefFunctions(SCJavaDefn.SharedValues sharedValues) throws CodeGenerationException { List<JavaMethod> methods = new ArrayList<JavaMethod>(); for (final MachineFunction mf : functions.getLiftedLetVarDefFunctions()) { // Generate three versions of letvar def function: // lazy, strict, unboxed. methods.add(buildLetVarDefFunction(mf, sharedValues, Scheme.C_SCHEME)); methods.add(buildLetVarDefFunction(mf, sharedValues, Scheme.E_SCHEME)); if (SCJavaDefn.canTypeBeUnboxed(mf.getResultType())) { methods.add(buildLetVarDefFunction(mf, sharedValues, Scheme.UNBOX_INTERNAL_SCHEME)); } } return methods; } /** * Generate the Java method for a lifted let variable definition function. * @param mf * @param sharedValues * @param scheme * @return the JavaMethod representing the let variable definition function. * @throws CodeGenerationException */ private JavaMethod buildLetVarDefFunction (MachineFunction mf, SCJavaDefn.SharedValues sharedValues, Scheme scheme) throws CodeGenerationException { int modifiers = Modifier.STATIC | Modifier.PRIVATE | Modifier.FINAL; SCJavaDefn javaDefn = new SCJavaDefn(mf, mf.getExpressionForm(), module, codeGenerationStats, sharedValues); // Figure out the methodName final int arity = javaDefn.getArity(); String methodName = CALToJavaNames.makeLetVarDefFunctionJavaName(javaDefn.getQualifiedName(), module) + "_"; if (scheme == Scheme.E_SCHEME) { methodName = methodName + "Strict"; } else if (scheme == Scheme.C_SCHEME) { methodName = methodName + "Lazy"; } else if (scheme == Scheme.UNBOX_INTERNAL_SCHEME) { methodName = methodName + "Unboxed"; } Block bodyBlock = javaDefn.genS_LetVarDef(scheme, (LECCLiftedLetVarMachineFunction)mf); // Figure out the arg names and types. String[] argNames = new String[arity + 1]; JavaTypeName[] argTypes = new JavaTypeName[arity + 1]; // Default type of argument is RTValue Arrays.fill(argTypes, JavaTypeNames.RTVALUE); // Fill in the argument names from the SC definition. for (int i = 0; i < arity; ++i) { argNames[i] = javaDefn.getJavaArgumentName(i); } // The last argument is the execution context. argNames[arity] = SCJavaDefn.EXECUTION_CONTEXT_NAME; argTypes[arity] = JavaTypeNames.RTEXECUTION_CONTEXT; // Try to get type info for this SC. for (int i = 0; i < arity; ++i) { if (javaDefn.isArgStrict(i) && javaDefn.isArgUnboxable(i)) { argTypes[i] = javaDefn.getArgumentTypeName(i); } } // Add the method to the class. JavaTypeName returnType = JavaTypeNames.RTVALUE; if (scheme == Scheme.UNBOX_INTERNAL_SCHEME) { returnType = SCJavaDefn.typeExprToTypeName(mf.getResultType()); } JavaMethod javaMethod = new JavaMethod(modifiers, returnType, argNames, argTypes, null, methodName); // Add the throws declaration javaMethod.addThrows(JavaTypeName.CAL_EXECUTOR_EXCEPTION); // If we are doing a sanity check on let variable behaviour we // want to check/set the flag associated with this method. if (LECCMachineConfiguration.SANITY_CHECK_LET_VARS) { JavaField flag = new JavaField.Instance(null, CALToJavaNames.cleanSCName(javaDefn.getFunctionName()) + "_flag_", JavaTypeName.BOOLEAN); JavaExpression exception = new JavaExpression.ClassInstanceCreationExpression( JavaTypeName.NULL_POINTER_EXCEPTION, LiteralWrapper.make("Double evaluation of " + javaDefn.getModuleName() + "." + CALToJavaNames.cleanSCName(javaDefn.getFunctionName()) + " in " + functions.getFunctionGroupName()), JavaTypeName.STRING); JavaStatement.IfThenElseStatement check = new JavaStatement.IfThenElseStatement(flag, new JavaStatement.ThrowStatement(exception)); javaMethod.addStatement(check); JavaExpression assign = new JavaExpression.Assignment(flag, LiteralWrapper.make(Boolean.TRUE)); javaMethod.addStatement(new ExpressionStatement(assign)); } // Add the body javaMethod.addStatement(bodyBlock); return javaMethod; } /** * Lift the let variables in the given function into their own * functions. * @param mf * @return the modified body of the given function. */ private Expression liftLetVars (MachineFunction mf) { // Lift the let variable definitions. ExpressionAnalyzer.LiftedLetVarResults liftedLetVarResults = ExpressionAnalyzer.liftLetVariables(mf, module); // For each lifted let variable create an LECCLiftedLetVarMachineFunction // and update the FunctionGroupInfo and Module to include the newly created // function. for (final ExpressionAnalyzer.LiftedLetVarInfo liftedLetVarInfo : liftedLetVarResults.getLiftedVarInfo()) { String[] paramNames = liftedLetVarInfo.getParameterNames(); TypeExpr[] paramTypes = liftedLetVarInfo.getParameterTypes(); int arity = liftedLetVarInfo.getArity(); String functionName = liftedLetVarInfo.getFunctionName(); boolean paramStrictness[] = liftedLetVarInfo.getParameterStrictness(); // We can fill in the strictness and type for parameters of the lifted // function which are simply the parameters of the containing function. for (int i = 0; i < arity; ++i) { for (int j = 0, k = mf.getArity(); j < k; ++j) { String containingArgName = mf.getParameterNames()[j]; if (containingArgName.equals(paramNames[i])) { paramTypes[i] = mf.getParameterTypes()[j]; paramStrictness[i] = mf.getParameterStrictness()[j]; break; } } } LECCLiftedLetVarMachineFunction lmf = new LECCLiftedLetVarMachineFunction( QualifiedName.make(module.getName(), functionName), arity, paramNames, paramTypes, paramStrictness, liftedLetVarInfo.getResultType(), liftedLetVarInfo.getExpression(), mf.isForAdjunct()); functions.addLiftedLetVarFunction(lmf, mf.getName()); module.addLiftedLetVarFunction(lmf, functions); } return liftedLetVarResults.getExpression(); } /** * Generate the java class representation of the function for which this builder is responsible. * The generated representation will be the outermost class definition only -- no inner classes will have been generated. * @throws CodeGenerationException */ private final void generateOuterSCDefinition() throws CodeGenerationException { if (this.javaClassRep == null) { // Get the fully-qualified superclass and class names; JavaTypeName superClassTypeName = JavaTypeNames.RTSUPERCOMBINATOR; if (functions.includesCAFs()) { superClassTypeName = JavaTypeNames.RTCAF; } // Construct the class access flags. //int classModifiers = Modifier.FINAL | Modifier.SUPER; int classModifiers = Modifier.PUBLIC | Modifier.FINAL; // No interfaces are implemented JavaTypeName[] interfaces = JavaDefinitionBuilder.EMPTY_TYPE_NAME_ARRAY; // Now instantiate the java class representation. this.javaClassRep = new JavaClassRep(className, superClassTypeName, classModifiers, interfaces); SCJavaDefn.SharedValues sharedValues = new SCJavaDefn.SharedValues(); // Build up a list of SCJavaDefn instances, once for each CAL function. // We call genS_SC_Boxed() to force the population of the SharedValues // instance. List<SCJavaDefn> scJavaDefns = new ArrayList<SCJavaDefn> (); for (final MachineFunction mf : functions.getTopLevelCALFunctions()) { // We want to actually work with a modified version of the function body. i.e. one // which has had let variable definitions lifted into their own functions. liftLetVars() // builds up a list of lifted functions which can be referenced later. Expression modifiedExpression = liftLetVars(mf); SCJavaDefn javaDefn = new SCJavaDefn(mf, modifiedExpression, module, codeGenerationStats, sharedValues); scJavaDefns.add(javaDefn); } // Now that the shared values have been initialized and all the let variable definitions have been // lifted we can generate the Java methods for each lifted let variable definition. // NOTE: We need to do this before we generate fields so that we build the correct set of // literal values. Collection<JavaMethod> letVarDefFunctions = buildLetVarDefFunctions(sharedValues); // Now that the let variable definition functions are in place we can compile the main function // bodies (which may refer to the lifted variable functions). for (final SCJavaDefn javaDefn : scJavaDefns) { javaDefn.genS_SC_Boxed(); } // Now that we have the information about referenced values // across the set of CAL functions we can create the class fields. createFields(sharedValues); // Create a constructor. createConstructor(); // Create a static factory method for accessing the instances for the // different supercombinators. createMethod_make(); // If this contains a CAF it needs to implement the method to release its cached result. createMethod_resetCachedResults (); // Retrieve the arity of the represented supercombinator. createMethod_getArity(); // Methods for determining information about the class instance. createMethod_getModuleName(); createMethod_getUnqualifiedName(); createMethod_getQualifiedName(); // Add in the let var definition methods so that they will appear immediately before the // 'f' methods. for (final JavaMethod jm : letVarDefFunctions) { javaClassRep.addMethod(jm); } Set<Integer> aritiesForfNMethods = new HashSet<Integer>(); for (final SCJavaDefn javaDefn : scJavaDefns) { /* * There are, potentially three methods which implement the * function logic: f, fnS, and fnL (where n is the function * arity). The f method is used in the general case of graph * reduction. It is passed the root of the application graph * and must unwind the graph to extract the values for the * function arguments. The fnL and fnS methods are called * when all the arguments are available and can be passed in * directly. The fnL method is used when all arguments are * available but it is not guaranteed that any strict * arguments are in WHNF. The fnS method is used when it can * be guaranteed that strict arguments are in WHNF. * * The fnL/S methods will potentially be called for arity of * up to SCJavaDefn.OPTIMIZED_APP_CHAIN_LENGTH, because that * is the maximum arity for which the runtime has special * purpose graph nodes for fully saturated applications. * * The other situation where the fnL/S methods will be * called is when a function is tail recursive or has * arguments which are strict and unboxable. In these cases * a graph node for fully saturated applications will be * generated, so we are not limited in the arity. */ boolean generateFnMethods = javaDefn.getArity() > 0 && (javaDefn.getArity() <= LECCMachineConfiguration.OPTIMIZED_APP_CHAIN_LENGTH || javaDefn.isTailRecursive() || javaDefn .hasStrictUnboxableArguments()); /* * Generally speaking the logic in each of the f methods * (i.e. f, fnL, fnS) is the same except for the inital * unpacking or evaluation of the arguments. It is therefore * possible to consolidate the functions so that the f and * fnL methods simply unpack/evaluate arguments and then * call the fnS method. The exception to this is with tail * recursive functions. Because a tail recursive function is * generated as a function containing a loop a call to the * function body can perform many reductions. This can cause * a problem since arguments, such as a list, that are * expanded still have their root held in the original * application node. As a result the root node will be held * in the execution context and the beginning of the * function body will clear the fields of the root node. * Similarly if the function bodies are consolidated the * call from f/fnL to fnS will hold the argument roots on * the stack, causing the same kind of space usage issue. As * a result we don't consolidate function bodies for tail * recursive functions. */ final boolean consolidateFunctions = SCDefinitionBuilder.CONSOLIDATE_FUNCTIONS && generateFnMethods; // Function body used when dealing with a general application. createMethod_fUnsaturated(javaDefn, consolidateFunctions); if (generateFnMethods) { aritiesForfNMethods.add(new Integer(javaDefn.getArity())); // Function body used when dealing with a fully saturated application in a lazy context. createMethod_fSaturatedLazy(javaDefn, consolidateFunctions); // Function body used when dealing with a fully saturated application in a strict context. createMethod_fSaturatedStrict(javaDefn); // If the return type of the CAL function is a type which can be unboxed // we need to generate the unboxed version of the fnS method. if (javaDefn.genS_SC_Unboxed() != null && javaDefn.getArity() > 0) { // Function body used when dealing with a fully saturated application in a strict context. createMethod_fUnboxedSaturatedStrict(javaDefn); } } } if (functions.getNFunctions() > 1) { createMethod_fSwitching(); } Map<Integer, Integer> arityToCount = functions.getArityToCountMap(); for (final Integer arity : arityToCount.keySet()) { if (functions.getNFunctions() > 1 && arity.intValue() > 0 && aritiesForfNMethods.contains(arity) && arity.intValue() <= LECCMachineConfiguration.OPTIMIZED_APP_CHAIN_LENGTH) { createMethod_fNSwitching(arity.intValue(), true, scJavaDefns); createMethod_fNSwitching(arity.intValue(), false, scJavaDefns); } } } } /** * If this generated class represents multiple supercombinators we may * need to generate a series of fnS functions, where n is the arity. * These functions are necessary in the case where a represented SC * has strict boxed arguments. In this case the runtime classes, * especially application nodes, will acess the fnS method. * The f method for an SC with strict unboxed arguments will always * be called either directly or from a special purpose application node, * so they aren't included here. * @param arity * @param strict * @param javaDefns * @throws CodeGenerationException */ private void createMethod_fNSwitching(int arity, boolean strict, List<SCJavaDefn> javaDefns) throws CodeGenerationException { int modifiers = Modifier.PUBLIC | Modifier.FINAL; // Add the method to the class. // Figure out the arg names and types. String[] argNames = new String[arity + 1]; JavaTypeName[] argTypes = new JavaTypeName[arity + 1]; JavaExpression[] argValues = new JavaExpression[arity+1]; Arrays.fill(argTypes, JavaTypeNames.RTVALUE); for (int i = 0; i < arity; ++i) { // If the argument is strict and primitive add $L to the name so that we // can use the declared name for the primitive value. argNames[i] = "$arg" + i; argValues[i] = new MethodVariable(argNames[i]); } argNames[arity] = SCJavaDefn.EXECUTION_CONTEXT_NAME; argTypes[arity] = JavaTypeNames.RTEXECUTION_CONTEXT; argValues[arity] = SCJavaDefn.EXECUTION_CONTEXT_VAR; String methodName = "f" + arity + (strict ? "S" : "L"); JavaMethod javaMethod = new JavaMethod(modifiers, JavaTypeNames.RTVALUE, argNames, argTypes, null, methodName); javaClassRep.addMethod(javaMethod); // Add the throws declaration javaMethod.addThrows(JavaTypeName.CAL_EXECUTOR_EXCEPTION); // At this point we want to switch based on the scTag and dispatch to the appropriate // sc specific f method. SwitchStatement switchStatement = new SwitchStatement(new JavaField.Instance(null, SCDefinitionBuilder.functionTagFieldName, JavaTypeName.INT)); for (int i = 0, n = javaDefns.size(); i < n; ++i) { SCJavaDefn javaDefn = javaDefns.get(i); if (javaDefn.getArity() != arity) { continue; } if (functions.getMachineFunction(javaDefn.getFunctionName()) instanceof LECCLiftedLetVarMachineFunction) { continue; } if (strict && javaDefn.hasStrictUnboxableArguments()) { continue; } methodName = functions.getFNamePrefix(javaDefn.getFunctionName()) + "f" + arity + (strict ? "S" : "L"); MethodInvocation mi = new MethodInvocation.Instance( null, methodName, argValues, argTypes, JavaTypeNames.RTVALUE, MethodInvocation.InvocationType.VIRTUAL); switchStatement.addCase( new SwitchStatement.IntCaseGroup(functions.getFunctionIndex(javaDefn.getFunctionName()), new ReturnStatement(mi))); } javaMethod.addStatement(switchStatement); // Handle the fallthrough. // If this is the lazy version of the 'f' method we want to defer to the // version in the superclass (i.e. RTValue). This can occur when there // is a lazy application of a functional argument, or an oversaturation. if (strict) { MethodInvocation mi = new MethodInvocation.Static (JavaTypeNames.RTVALUE, "badValue", LiteralWrapper.make("Bad scTag in 'f'."), JavaTypeName.STRING, JavaTypeNames.RTVALUE); javaMethod.addStatement(new ReturnStatement(mi)); } else { MethodInvocation mi = new MethodInvocation.Instance( null, "f" + arity + "L", this.javaClassRep.getSuperclassName(), argValues, argTypes, JavaTypeNames.RTVALUE, MethodInvocation.InvocationType.SPECIAL); javaMethod.addStatement(new JavaStatement.LineComment("This is an oversaturated lazy application.")); javaMethod.addStatement(new JavaStatement.LineComment("Usually this occurs when dealing with a lazy application of a function type argument.")); javaMethod.addStatement(new JavaStatement.LineComment("Defer to the base implementation in the super class.")); javaMethod.addStatement(new ReturnStatement(mi)); } } private void createMethod_fSwitching () { final int modifiers = Modifier.PUBLIC | Modifier.FINAL; // Add the method to the class. final String argNames[] = new String []{ROOT_NODE, SCJavaDefn.EXECUTION_CONTEXT_NAME}; final JavaTypeName argTypes[] = new JavaTypeName []{JavaTypeNames.RTRESULT_FUNCTION, JavaTypeNames.RTEXECUTION_CONTEXT}; final boolean[] argFinal = new boolean[] {true, true}; String methodName = "f"; final JavaMethod javaMethod = new JavaMethod(modifiers, JavaTypeNames.RTVALUE, argNames, argTypes, argFinal, methodName); javaClassRep.addMethod(javaMethod); // Add the throws declaration javaMethod.addThrows(JavaTypeName.CAL_EXECUTOR_EXCEPTION); // At this point we want to switch based on the scTag and dispatch to the appropriate // sc specific f method. SwitchStatement switchStatement = new SwitchStatement(new JavaField.Instance(null, SCDefinitionBuilder.functionTagFieldName, JavaTypeName.INT)); for (final MachineFunction mf : functions.getTopLevelCALFunctions()) { methodName = functions.getFNamePrefix(mf.getName()) + "f"; MethodInvocation mi = new MethodInvocation.Instance( null, methodName, new JavaExpression[]{new MethodVariable(ROOT_NODE), SCJavaDefn.EXECUTION_CONTEXT_VAR}, argTypes, JavaTypeNames.RTVALUE, MethodInvocation.InvocationType.VIRTUAL); switchStatement.addCase( new SwitchStatement.IntCaseGroup( functions.getFunctionIndex(mf.getName()), new ReturnStatement(mi))); } javaMethod.addStatement(switchStatement); // Handle the fallthrough. final MethodInvocation mi = new MethodInvocation.Static (JavaTypeNames.RTVALUE, "badValue", LiteralWrapper.make("Bad scTag in 'f'."), JavaTypeName.STRING, JavaTypeNames.RTVALUE); javaMethod.addStatement(new ReturnStatement(mi)); } /** * Get the object representation for a Java class representing the function for this class. * This will not have any inner class info set. * * @return the class representation of the function for this class. * @throws CodeGenerationException */ private JavaClassRep getOuterClassRep() throws CodeGenerationException { if (this.javaClassRep == null) { generateOuterSCDefinition(); } for (final MachineFunction mf : functions.getTopLevelCALFunctions()) { boolean hasStrictUnboxableArguments = false; for (int i = 0; i < mf.getArity(); ++i) { if (mf.getParameterStrictness()[i] && SCJavaDefn.canTypeBeUnboxed(mf.getParameterTypes()[i])) { hasStrictUnboxableArguments = true; } } boolean generateFnMethods = mf.getArity() > 0 && (mf.getArity() <= LECCMachineConfiguration.OPTIMIZED_APP_CHAIN_LENGTH || mf.isTailRecursive() || hasStrictUnboxableArguments); boolean generateApplicationClasses = generateFnMethods && (hasStrictUnboxableArguments || mf.isTailRecursive()); if (generateApplicationClasses) { // We need to mark this class as having // inner classes containing assertions. // This is done manually because we don't always // generate the inner classes at the same time // as the containing class. javaClassRep.setInnerClassContainsAssertions(); break; } } return javaClassRep; } /** * Create the fields. * private static final RTFunction instance = new ThisClass(); * literalDef1; * literalDef2; * ... * @param sharedValues */ private void createFields(final SCJavaDefn.SharedValues sharedValues) { // Create fields for the literal def symbols... // private final RTValue litSymbol = (litDefn.getKernelTypeClass()).make(litDefn.getConstructorExpression()); if (sharedValues.getNLiteralValues() > 0) { javaClassRep.addComment(new JavaStatement.MultiLineComment("CAL data instances for literal values.")); } for (final KernelLiteral kernelLiteral : sharedValues.getLiteralValues()) { kernelLiteral.addFieldDeclarations(javaClassRep); } // Instance of this class for each supercombinator it // represents. for (final MachineFunction mf : functions.getTopLevelCALFunctions()) { final int functionIndex = functions.getFunctionIndex(mf.getName()); final JavaExpression initializer; if (functions.getNFunctions() <= 1) { initializer = new ClassInstanceCreationExpression( className); } else { initializer = new ClassInstanceCreationExpression( className, new JavaExpression[]{LiteralWrapper.make(Integer.valueOf(functionIndex)), LiteralWrapper.make(Integer.valueOf(mf.getArity()))}, new JavaTypeName[]{JavaTypeName.INT, JavaTypeName.INT}); } final String fieldName = CALToJavaNames.getInstanceFieldName(mf.getQualifiedName(), module); final int instanceModifiers; if (mf.getArity() == 0) { //for a CAF or a zero-arity function, the instance field will never be referred to directly outside the class //so declare them private for safety sake. instanceModifiers = Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL; } else { instanceModifiers = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; } final JavaFieldDeclaration instanceDeclaration = new JavaFieldDeclaration(instanceModifiers, className, fieldName, initializer); if (functions.getNFunctions() <= 1) { instanceDeclaration.setJavaDoc(new JavaDocComment("Singleton instance of this class.")); } else { instanceDeclaration.setJavaDoc(new JavaDocComment("Instance of this class representing CAL function " + mf.getName() + ".")); } javaClassRep.addFieldDeclaration(instanceDeclaration); } if (sharedValues.getNStaticErrorInfo() > 0) { javaClassRep.addComment(new JavaStatement.MultiLineComment("ErrorInfo instances.")); } for (final String name : sharedValues.getStaticErrorInfoNames()) { final JavaExpression initializer = sharedValues.getStaticError(name); final int instanceModifiers = Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL; final JavaFieldDeclaration errorDeclaration = new JavaFieldDeclaration(instanceModifiers, JavaTypeName.ERRORINFO, name, initializer); javaClassRep.addFieldDeclaration(errorDeclaration); } // If this function references other supercombinators we need to set // up a field for each referenced SC, a flag to indicate the // initialization state of the referenced SC fields, and potentially an // object to be used as a synchronization mutex for the initialization method. final int referencedDCModifiers = Modifier.STATIC | Modifier.PRIVATE | Modifier.FINAL; // Create an instance field for each referenced data constructor. This will give us // a local reference that can be used in the body function. // e.g. RTFunction i_Foo; if (sharedValues.getNReferencedDCs() > 0) { javaClassRep.addComment(new JavaStatement.MultiLineComment("Data constructor class instances for all referenced data constructors.")); } for (final ReferencedDCInfo rfi : sharedValues.getReferencedDCs()) { DataConstructor dc = rfi.getDC(); JavaField jf = rfi.getJField(); JavaExpression fieldInitializer; if (dc.getArity() > 0) { // Just invoke the regular make invocation to get the singleton SC/DC instance. fieldInitializer = new MethodInvocation.Static(jf.getFieldType(), "make", JavaExpression.EMPTY_JAVA_EXPRESSION_ARRAY, JavaExpression.EMPTY_TYPE_NAME_ARRAY, jf.getFieldType()); } else { // This is a zero arity data constructor. We get the singleton instance by accessing // the DataType class factory method if the data type has more than one zero arity DC. TypeConstructor typeCons = dc.getTypeConstructor(); if (SCJavaDefn.isTagDC(dc, module)) { JavaTypeName typeClass = CALToJavaNames.createTypeNameFromType(typeCons, module); JavaTypeName tagDCTypeName = CALToJavaNames.createTypeNameForTagDCFromType(typeCons, module); Integer ordinal = Integer.valueOf(dc.getOrdinal()); fieldInitializer = new MethodInvocation.Static(typeClass, "getTagDC", LiteralWrapper.make(ordinal), JavaTypeName.INT, tagDCTypeName); } else { // Just invoke the regular make invocation to get the singleton SC/DC instance. fieldInitializer = new MethodInvocation.Static(jf.getFieldType(), "make", JavaExpression.EMPTY_JAVA_EXPRESSION_ARRAY, JavaExpression.EMPTY_TYPE_NAME_ARRAY, jf.getFieldType()); } } JavaFieldDeclaration dcDeclaration = new JavaFieldDeclaration( referencedDCModifiers, jf.getFieldType(), jf.getFieldName(), fieldInitializer); javaClassRep.addFieldDeclaration(dcDeclaration); } if (functions.includesCAFs()) { javaClassRep.addComment(new JavaStatement.MultiLineComment("Mappings of execution context to CAF instances.")); // Add an instance field to hold a Map of // ExecutionContext -> RTFullApp.General._0 // A map for associating instances of this SC with execution contexts. // This is only created for CAF functions. There are two reasons why CAFs // have an instance for each execution context. One is thread safety. The // other is so that different threads of execution can release cached CAF // results at will. for (final MachineFunction mf : functions.getTopLevelCALFunctions()) { if (mf.isCAF()) { //we do not synchronize the instance map, but rather the methods in the CAF class that mutate it. //this is because the make method has check-then-modify semantics, and so needs to be synchronized at the method //level anyways. JavaExpression instancesMapInit = new ClassInstanceCreationExpression(JavaTypeName.WEAK_HASH_MAP); String mapPrefix = functions.getFNamePrefix(mf.getName()); JavaFieldDeclaration instancesMap = new JavaFieldDeclaration ( Modifier.STATIC | Modifier.PRIVATE | Modifier.FINAL, JavaTypeName.MAP, mapPrefix + "$instancesMap", instancesMapInit); instancesMap.setJavaDoc(new JavaDocComment("Execution context -> instance map for " + mf.getName())); javaClassRep.addFieldDeclaration(instancesMap); } } } // We have two int instance fields to hold the tag, indicating which function // is represented by the class instance, and the arity of that function. // These fields are only needed if the class represents more than one function. if (functions.getNFunctions() > 1) { JavaFieldDeclaration scTagDeclaration = new JavaFieldDeclaration(Modifier.PRIVATE | Modifier.FINAL, JavaTypeName.INT, SCDefinitionBuilder.functionTagFieldName, null); List<String> commentLines = new ArrayList<String> (); commentLines.add ("Tag field indicating which CAL function this class instance represents."); for (int i = 0, n = functions.getTopLevelCALFunctions().size(); i < n; ++i) { String fName = functions.getFunctionNameFromIndex(i); commentLines.add(" " + i + " -> " + fName); } scTagDeclaration.setJavaDoc(new JavaStatement.JavaDocComment(commentLines)); javaClassRep.addFieldDeclaration(scTagDeclaration); JavaFieldDeclaration arityDeclaration = new JavaFieldDeclaration(Modifier.PRIVATE | Modifier.FINAL, JavaTypeName.INT, "arity", null); arityDeclaration.setJavaDoc(new JavaStatement.JavaDocComment("Field holding arity of represented function.")); javaClassRep.addFieldDeclaration(arityDeclaration); } if (LECCMachineConfiguration.SANITY_CHECK_LET_VARS) { // Create a boolean flag for each lifted let variable definition. for (final MachineFunction mf : functions.getLiftedLetVarDefFunctions()) { String flagName = CALToJavaNames.cleanSCName(mf.getName()) + "_flag_"; javaClassRep.addFieldDeclaration( new JavaFieldDeclaration(Modifier.PRIVATE, JavaTypeName.BOOLEAN, flagName, LiteralWrapper.make(Boolean.FALSE))); } } } /** * Add a static method to clear any cached CAF results. */ private void createMethod_resetCachedResults () { if (!functions.includesCAFs()) { return; } //todoBI it may be better to guard each instance map by synchronizing on it directly. However, currently the ASM bytecode //generator does not support synchronization blocks, and so this is adequate. final int modifiers = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL | Modifier.SYNCHRONIZED; JavaTypeName returnType = JavaTypeName.VOID; // Add the method to the class. JavaMethod javaMethod = new JavaMethod(modifiers, returnType, SCJavaDefn.EXECUTION_CONTEXT_NAME, JavaTypeNames.RTEXECUTION_CONTEXT, false, "resetCachedResults"); javaClassRep.addMethod(javaMethod); for (final MachineFunction mf : functions.getTopLevelCALFunctions() ) { if (!mf.isCAF()) { continue; } String mapPrefix = functions.getFNamePrefix(mf.getName()); JavaField instanceField = new JavaField.Static(className, mapPrefix+"$instancesMap", JavaTypeName.MAP); MethodInvocation remove = new MethodInvocation.Instance (instanceField, "remove", new JavaExpression[]{SCJavaDefn.EXECUTION_CONTEXT_VAR}, new JavaTypeName[]{JavaTypeName.OBJECT}, JavaTypeName.OBJECT, MethodInvocation.InvocationType.INTERFACE); javaMethod.addStatement (new ExpressionStatement(remove)); } } /** * Create the make() method. * public static final RTFunction make(int scTag) { * switch (scTag) { * } * } * * This method is used for creating instances of zero arity * functions and CAFs. */ private void createMethod_make() throws CodeGenerationException { if (!functions.includesCAFs() && !functions.includesZeroArityFunctions()) { return; } if (functions.includesZeroArityFunctions() && functions.getNFunctions() > 1) { throw new CodeGenerationException("zero-arity functions can only be in a component of size 1."); } final int modifiers; if (functions.includesCAFs()) { //todoBI it may be better to guard each instance map by synchronizing on it directly. However, currently the ASM bytecode //generator does not support synchronization blocks, and so this is adequate. modifiers = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL | Modifier.SYNCHRONIZED; } else { modifiers = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; } JavaTypeName returnType = JavaTypeNames.RTFUNCTION; // Add the method to the class. JavaMethod javaMethod; if (functions.getNCAFs() + functions.getNZeroArityFunctions() <= 1) { javaMethod = new JavaMethod(modifiers, returnType, new String[]{SCJavaDefn.EXECUTION_CONTEXT_NAME}, new JavaTypeName[]{JavaTypeNames.RTEXECUTION_CONTEXT}, null, "make"); } else { javaMethod = new JavaMethod(modifiers, returnType, new String[]{"scIndex", SCJavaDefn.EXECUTION_CONTEXT_NAME}, new JavaTypeName[]{JavaTypeName.INT, JavaTypeNames.RTEXECUTION_CONTEXT}, null, "make"); } javaClassRep.addMethod(javaMethod); // Add the body.. // Turn on this code to include a diagnostic print statement. // JavaExpression field = new JavaField.Static(JavaTypeName.make("java.lang.System"), "out", JavaTypeName.make("java.io.PrintStream")); // JavaExpression args[] = new JavaExpression[1]; // String s = javaDefn.getFunctionName() + ".make()"; // args[0] = LiteralWrapper.make(s); // JavaTypeName argTypes[] = new JavaTypeName[1]; // argTypes[0] = JavaTypeName.STRING; // JavaExpression me = new MethodInvocation.Instance (field, "println", args, argTypes, JavaTypeName.VOID, MethodInvocation.InvocationType.VIRTUAL); // JavaStatement se = new JavaStatement.ExpressionStatement(me); // javaMethod.addStatement(se); // For a CAF we cache 'instances' associated with execution contexts. In this case an // 'instance' is represented by an application of the supercombinator to zero arguments. // A zero arity function is different from a CAF in that we don't cache the 'instance'. // Regular functions we just return the singleton supercombinator instance. if (functions.getNCAFs() + functions.getNZeroArityFunctions() <= 1) { for (final MachineFunction mf : functions.getTopLevelCALFunctions()) { JavaField instanceField = new JavaField.Static(className, CALToJavaNames.getInstanceFieldName(mf.getQualifiedName(), module), className); if (mf.isCAF()) { Block b = new Block(); // This is a CAF (constant applicative form) we want to use the cached instance associated // with the execution context, if it exists. // If it doesn't exist we will create an instance and add it to the cache. String instanceMapFieldName = functions.getFNamePrefix(mf.getName()) + "$instancesMap"; JavaField instanceMapField = new JavaField.Static(className, instanceMapFieldName, JavaTypeName.MAP); // RTFunction newInstance = (RTFunction)$instancesMap.get($ec); JavaExpression.LocalVariable newInstanceVar = new JavaExpression.LocalVariable("newInstance", returnType); JavaExpression initializer = new JavaExpression.CastExpression( returnType, new MethodInvocation.Instance( instanceMapField, "get", SCJavaDefn.EXECUTION_CONTEXT_VAR, JavaTypeName.OBJECT, JavaTypeName.OBJECT, MethodInvocation.InvocationType.INTERFACE)); JavaStatement decl = new JavaStatement.LocalVariableDeclaration(newInstanceVar, initializer); b.addStatement(decl); // If no instance exists for the execution context create one and cache it. // newInstance == null JavaExpression comparison = new JavaExpression.OperatorExpression.Binary(JavaOperator.EQUALS_OBJECT, newInstanceVar, JavaExpression.LiteralWrapper.NULL); // newInstance = new RTFullApp.General_0($instance); // $instancesMap.put($ec, newInstance); JavaStatement.Block then = new JavaStatement.Block(); then.addStatement(new ExpressionStatement(new JavaExpression.Assignment(newInstanceVar, new JavaExpression.ClassInstanceCreationExpression(_0TypeName, instanceField, JavaTypeNames.RTSUPERCOMBINATOR)))); JavaExpression cacheValue = new MethodInvocation.Instance( instanceMapField, "put", new JavaExpression[] { SCJavaDefn.EXECUTION_CONTEXT_VAR, newInstanceVar }, new JavaTypeName[] { JavaTypeName.OBJECT, JavaTypeName.OBJECT }, JavaTypeName.OBJECT, MethodInvocation.InvocationType.INTERFACE); then.addStatement(new ExpressionStatement(cacheValue)); // Put the whole if expression together. JavaStatement ifthen = new JavaStatement.IfThenElseStatement(comparison, then); b.addStatement(ifthen); b.addStatement(new ReturnStatement(newInstanceVar)); javaMethod.addStatement(b); break; } else if (mf.getArity() == 0) { Block b = new Block(); // This is an unsafe method. i.e. a non-CAF function of arity zero. Since the // function can have side effects we need to create a new instance of the class // each time so that a previously evaluated value doesn't get re-used in place // of actually executing the function. JavaExpression.LocalVariable newInstanceVar = new JavaExpression.LocalVariable("newInstance", returnType); JavaExpression initializer = new ClassInstanceCreationExpression(_0TypeName, instanceField, JavaTypeNames.RTSUPERCOMBINATOR); JavaStatement decl = new JavaStatement.LocalVariableDeclaration(newInstanceVar, initializer); b.addStatement(decl); b.addStatement(new ReturnStatement(newInstanceVar)); javaMethod.addStatement(b); break; } } } else { SwitchStatement switchStatement = new SwitchStatement(new MethodVariable("scIndex")); for (final MachineFunction mf : functions.getTopLevelCALFunctions()) { JavaField instanceField = new JavaField.Static(className, CALToJavaNames.getInstanceFieldName(mf.getQualifiedName(), module), className); int functionIndex = functions.getFunctionIndex(mf.getName()); if (mf.isCAF()) { Block b = new Block(); // This is a CAF (constant applicative form) we want to use the cached instance associated // with the execution context, if it exists. // If it doesn't exist we will create an instance and add it to the cache. String instanceMapFieldName = functions.getFNamePrefix(mf.getName()) + "$instancesMap"; JavaField instanceMapField = new JavaField.Static(className, instanceMapFieldName, JavaTypeName.MAP); // RTFunction newInstance = (RTFunction)$instancesMap.get($ec); JavaExpression.LocalVariable newInstanceVar = new JavaExpression.LocalVariable("newInstance", returnType); JavaExpression initializer = new JavaExpression.CastExpression(returnType, new MethodInvocation.Instance (instanceMapField, "get", SCJavaDefn.EXECUTION_CONTEXT_VAR, JavaTypeName.OBJECT, JavaTypeName.OBJECT, MethodInvocation.InvocationType.INTERFACE)); JavaStatement decl = new JavaStatement.LocalVariableDeclaration(newInstanceVar, initializer); b.addStatement(decl); // If no instance exists for the execution context create one and cache it. // newInstance == null JavaExpression comparison = new JavaExpression.OperatorExpression.Binary(JavaOperator.EQUALS_OBJECT, newInstanceVar, JavaExpression.LiteralWrapper.NULL); // newInstance = new RTFullApp.General_0($instance); // $instancesMap.put($ec, newInstance); JavaStatement.Block then = new JavaStatement.Block(); then.addStatement(new ExpressionStatement(new JavaExpression.Assignment(newInstanceVar, new JavaExpression.ClassInstanceCreationExpression(_0TypeName, instanceField, JavaTypeNames.RTSUPERCOMBINATOR)))); JavaExpression cacheValue = new MethodInvocation.Instance(instanceMapField, "put", new JavaExpression[]{SCJavaDefn.EXECUTION_CONTEXT_VAR, newInstanceVar}, new JavaTypeName[]{JavaTypeName.OBJECT, JavaTypeName.OBJECT}, JavaTypeName.OBJECT, MethodInvocation.InvocationType.INTERFACE); then.addStatement(new ExpressionStatement(cacheValue)); // Put the whole if expression together. JavaStatement ifthen = new JavaStatement.IfThenElseStatement(comparison, then); b.addStatement(ifthen); b.addStatement(new ReturnStatement(newInstanceVar)); switchStatement.addCase( new SwitchStatement.IntCaseGroup( functionIndex, b)); } } MethodInvocation mi = new MethodInvocation.Static (JavaTypeNames.RTVALUE, "badValue", new JavaExpression[]{LiteralWrapper.NULL, LiteralWrapper.make("Illegal fall through to default case in " + functions.getFunctionGroupQualifiedName() + ".make().")}, new JavaTypeName[]{JavaTypeName.ERRORINFO, JavaTypeName.STRING}, JavaTypeNames.RTVALUE); switchStatement.addCase( new SwitchStatement.DefaultCase(new ReturnStatement(new CastExpression(JavaTypeNames.RTFUNCTION, mi)))); javaMethod.addStatement(switchStatement); } } /** * Create the no-argument constructor. * private ThisClass(int scIndex) { * } */ private void createConstructor() { int modifiers = Modifier.PRIVATE; // Add the method to the class. if (functions.getNFunctions() <= 1) { JavaConstructor javaConstructor = new JavaConstructor(modifiers, new String[]{}, new JavaTypeName[]{}, ((JavaTypeName.Reference.Object)className).getBaseName()); javaClassRep.addConstructor(javaConstructor); } else { JavaConstructor javaConstructor = new JavaConstructor(modifiers, new String[]{"scIndex", "arityValue"}, new JavaTypeName[]{JavaTypeName.INT, JavaTypeName.INT}, ((JavaTypeName.Reference.Object)className).getBaseName()); javaClassRep.addConstructor(javaConstructor); // Now fill in the constructor body. // assign the tag field. JavaField tagField = new JavaField.Instance(null, SCDefinitionBuilder.functionTagFieldName, JavaTypeName.INT); MethodVariable tagArgument = new MethodVariable("scIndex"); JavaExpression.Assignment tagAssign = new JavaExpression.Assignment(tagField, tagArgument); javaConstructor.addStatement(new ExpressionStatement(tagAssign)); // asign the arity. JavaExpression.Assignment assignArity = new JavaExpression.Assignment( new JavaField.Instance(null, "arity", JavaTypeName.INT), new MethodVariable("arityValue")); javaConstructor.addStatement (new ExpressionStatement (assignArity)); } } /** * Create the getArity() method. * public final int getArity() {return (javaDefn.getArity());} */ private void createMethod_getArity() { int modifiers = Modifier.PUBLIC | Modifier.FINAL; // Add the method to the class. JavaMethod javaMethod = new JavaMethod(modifiers, JavaTypeName.INT, "getArity"); javaClassRep.addMethod(javaMethod); // Add the body if (functions.getNFunctions() > 1) { JavaField arityField = new JavaField.Instance(null, "arity", JavaTypeName.INT); javaMethod.addStatement(new ReturnStatement(arityField)); } else { int arity = functions.getTopLevelCALFunctions().iterator().next().getArity(); javaMethod.addStatement(new ReturnStatement(LiteralWrapper.make(Integer.valueOf(arity)))); } } /** * Create the f() method for the non-fully-saturated case. * public final RTValue f(RTResultFunction $rootNode) throws org.openquark.cal.runtime.CALExecutor.CALExecutorException * @param javaDefn * @param consolidateFunctions * @throws CodeGenerationException */ private void createMethod_fUnsaturated(final SCJavaDefn javaDefn, final boolean consolidateFunctions) throws CodeGenerationException { final int arity = javaDefn.getArity(); final JavaMethod javaMethod; { final int modifiers = Modifier.PUBLIC | Modifier.FINAL; // Add the method to the class. final String[] argNames = new String []{ROOT_NODE, SCJavaDefn.EXECUTION_CONTEXT_NAME}; final JavaTypeName[] argTypes = new JavaTypeName []{JavaTypeNames.RTRESULT_FUNCTION, JavaTypeNames.RTEXECUTION_CONTEXT}; final boolean[] argFinal = new boolean[] {true, true}; final String methodNamePrefix = functions.getFNamePrefix(javaDefn.getFunctionName()); final String methodName = methodNamePrefix + "f"; javaMethod = new JavaMethod(modifiers, JavaTypeNames.RTVALUE, argNames, argTypes, argFinal, methodName); // Add the throws declaration javaMethod.addThrows(JavaTypeName.CAL_EXECUTOR_EXCEPTION); final JavaDocComment comment = new JavaDocComment(methodName); comment.addLine("This method implements the function logic of the CAL function " + javaDefn.getModuleName() + "." + javaDefn.getFunctionName()); javaMethod.setJavaDocComment(comment); } javaClassRep.addMethod(javaMethod); if (consolidateFunctions) { // Extract the arguments from the application chain. JavaStatement argumentExtraction = generateArgumentExtractors(javaDefn); javaMethod.addStatement(argumentExtraction); final JavaExpression[] args = new JavaExpression[arity + 1]; final JavaTypeName[] argTypes = new JavaTypeName[arity + 1]; Arrays.fill (argTypes, JavaTypeNames.RTVALUE); args[arity] = SCJavaDefn.EXECUTION_CONTEXT_VAR; argTypes[arity] = JavaTypeNames.RTEXECUTION_CONTEXT; // Call fnS with the extracted arguments. for (int i = 0; i < arity; ++i) { String javaName = javaDefn.getJavaArgumentName(i); if (javaDefn.isArgStrict(i) && javaDefn.isArgUnboxable(i)) { // We append the names of the arguments with $L to allow the declared name to // be used with the unboxed primitive value. javaName += "$L"; } JavaTypeName argType = JavaTypeNames.RTVALUE; if (javaDefn.isArgStrict(i) && javaDefn.isArgUnboxable(i)) { argType = javaDefn.getArgumentTypeName(i); } LocalVariable lv = new LocalVariable (javaName, JavaTypeNames.RTVALUE); args[i] = lv; if (javaDefn.isArgStrict(i)) { args[i] = SCJavaDefn.createInvocation(args[i], SCJavaDefn.EVALUATE, SCJavaDefn.EXECUTION_CONTEXT_VAR); args[i] = callLastRef(args[i], lv); if (javaDefn.isArgUnboxable(i)) { args[i] = SCJavaDefn.unboxValue(javaDefn.getArgumentTypeName(i), args[i]); argTypes[i] = argType; } } else { args[i] = callLastRef(args[i], lv); } } String methodNamePrefix2 = functions.getFnNamePrefix(javaDefn.getFunctionName()); JavaExpression mi = new MethodInvocation.Instance ( null, methodNamePrefix2 + "f" + arity + "S", args, argTypes, JavaTypeNames.RTVALUE, MethodInvocation.InvocationType.VIRTUAL); javaMethod.addStatement(new JavaStatement.ReturnStatement(mi)); } else { // Add statistics generation. addStatsBlock (javaMethod, javaDefn); // Extract function arguments from the application chain. JavaStatement argumentExtraction = generateArgumentExtractors(javaDefn); javaMethod.addStatement(argumentExtraction); javaMethod.addStatement (generateStrictArgEvaluationBlock(javaDefn)); String[] unboxedLocalVarNames = null; JavaTypeName[] unboxedLocalVarTypes = null; if (LECCMachineConfiguration.generateDebugCode() || LECCMachineConfiguration.generateDebugCode()) { unboxedLocalVarNames = new String[arity]; unboxedLocalVarTypes = new JavaTypeName[arity]; for (int i = 0; i < arity; ++i) { unboxedLocalVarNames[i] = javaDefn.getJavaArgumentName(i); if (javaDefn.isArgStrict(i) && javaDefn.isArgUnboxable(i)) { unboxedLocalVarTypes[i] = javaDefn.getArgumentTypeName(i); } else { unboxedLocalVarTypes[i] = JavaTypeNames.RTVALUE; } } } //Add the body Block bodyBlock = javaDefn.genS_SC_Boxed(); if (javaDefn.isTailRecursive()) { Block loopBodyBlock = new Block(); if (!LECCMachineConfiguration.nonInterruptibleRuntime()) { // Add a check of the quit flag at the top of the loop body. loopBodyBlock.addStatement(checkForQuit()); } if (LECCMachineConfiguration.SANITY_CHECK_LET_VARS) { loopBodyBlock.addStatement(resetLetVarFlags(javaDefn.getFunctionName())); } if (LECCMachineConfiguration.generateDebugCode()) { loopBodyBlock.addStatement(generateDebugCode(javaDefn, unboxedLocalVarNames, unboxedLocalVarTypes)); } loopBodyBlock.addStatement(bodyBlock); UnconditionalLoop whileStatement = new UnconditionalLoop (SCJavaDefn.TAIL_RECURSION_LOOP_LABEL, loopBodyBlock); javaMethod.addStatement (whileStatement); } else { if (LECCMachineConfiguration.SANITY_CHECK_LET_VARS) { javaMethod.addStatement(resetLetVarFlags(javaDefn.getFunctionName())); } if (LECCMachineConfiguration.generateDebugCode()) { javaMethod.addStatement(generateDebugCode(javaDefn, unboxedLocalVarNames, unboxedLocalVarTypes)); } javaMethod.addStatement(bodyBlock); } } } /** * Generate code to reset the flags used to track let variable definition function * usage. * @param originatingFunction * @return the code block that resets the flags. */ private Block resetLetVarFlags(String originatingFunction) { Block b = new Block(); if (LECCMachineConfiguration.SANITY_CHECK_LET_VARS) { Set<String> liftedFunctions = functions.getLiftedFunctionsFor(originatingFunction); if (liftedFunctions != null) { for (final String liftedFunctionName : liftedFunctions) { String flagName = CALToJavaNames.cleanSCName(liftedFunctionName) + "_flag_"; b.addStatement(new ExpressionStatement(new JavaExpression.Assignment(new JavaField.Instance(null, flagName, JavaTypeName.BOOLEAN), LiteralWrapper.make(Boolean.FALSE)))); } } } return b; } /** * @param javaDefn * @return A block which extracts the function arguments from an application chain. * @throws CodeGenerationException */ private JavaStatement generateArgumentExtractors (SCJavaDefn javaDefn) throws CodeGenerationException { final Block argExtractorBlock = new Block(); // Extract the arguments from the application chain. final int arity = javaDefn.getArity(); if (arity > 0) { argExtractorBlock.addStatement(new LineComment("Arguments")); } // Add argument extractors for (int i = arity - 1; i >= 0; i--) { // The name used for the extracted variable depends on whether the argument is of primitive type // and strict. If it is we append $L so that the actual name can be used for the unboxed primitive value. final String fixedUpVarName; { String basicVarName = javaDefn.getJavaArgumentName(i); if (javaDefn.isArgStrict(i) && javaDefn.isArgUnboxable(i)) { fixedUpVarName = basicVarName + "$L"; } else { fixedUpVarName = basicVarName; } } final JavaExpression initializer; if (i == arity - 1) { //$rootNode.getArgValue(); initializer = SCJavaDefn.createInvocation(METHODVAR_ROOT_NODE, SCJavaDefn.GETARGVALUE); } else if (i == arity - 2) { if (arity > 2) { //RTValue $currentRootNode; argExtractorBlock.addStatement(new LocalVariableDeclaration(LOCALVAR_CURRENT_ROOT_NODE)); //($currentRootNode = $rootNode.prevArg()).getArgValue(); initializer = SCJavaDefn.createInvocation( new Assignment(LOCALVAR_CURRENT_ROOT_NODE, SCJavaDefn.createInvocation(METHODVAR_ROOT_NODE, SCJavaDefn.PREVARG)), SCJavaDefn.GETARGVALUE); } else { //$rootNode.prevArg().getArgValue(); initializer = SCJavaDefn.createInvocation( SCJavaDefn.createInvocation(METHODVAR_ROOT_NODE, SCJavaDefn.PREVARG), SCJavaDefn.GETARGVALUE); } } else if (i == 0) { //$currentRootNode.prevArg().getArgValue(); initializer = SCJavaDefn.createInvocation( SCJavaDefn.createInvocation(LOCALVAR_CURRENT_ROOT_NODE, SCJavaDefn.PREVARG), SCJavaDefn.GETARGVALUE); } else { //($currentRootNode = $currentRootNode.prevArg()).getArgValue(); initializer = SCJavaDefn.createInvocation( new Assignment(LOCALVAR_CURRENT_ROOT_NODE, SCJavaDefn.createInvocation(LOCALVAR_CURRENT_ROOT_NODE, SCJavaDefn.PREVARG)), SCJavaDefn.GETARGVALUE); } LocalVariableDeclaration varDecl = new LocalVariableDeclaration(new LocalVariable(fixedUpVarName, JavaTypeNames.RTVALUE), initializer); argExtractorBlock.addStatement(varDecl); } // Make a call on the root node to free its member fields. // This frees them up for potential garbage collection. JavaStatement comment = new JavaStatement.LineComment("Release the fields in the root node to open them to garbage collection"); argExtractorBlock.addStatement(comment); JavaExpression clearMembers = new MethodInvocation.Instance( new MethodVariable(ROOT_NODE), "clearMembers", JavaTypeName.VOID, MethodInvocation.InvocationType.VIRTUAL); argExtractorBlock.addStatement(new ExpressionStatement(clearMembers)); return argExtractorBlock; } /** * Create a block of code that evaluates any arguments marked as strict. * @param javaDefn * @return the java statement (i.e. a block) that evaluates the strict arguments. * @throws CodeGenerationException */ private JavaStatement generateStrictArgEvaluationBlock(SCJavaDefn javaDefn) throws CodeGenerationException { JavaStatement.Block block = new JavaStatement.Block(); // Do evaluation of any arguments marked as strict. if (javaDefn.hasStrictArguments()) { // Do evaluation of any arguments marked as strict. block.addStatement(new LineComment("Evaluate any arguments marked as strict.")); for (int argIndex = 0; argIndex < javaDefn.getArity(); argIndex++) { if (javaDefn.isArgStrict(argIndex)) { String fixedUpVarName = javaDefn.getJavaArgumentName(argIndex); if (javaDefn.isArgUnboxable(argIndex)) { // In this case the argument was named with an appended $L so that we can declare a // local variable with the CAL argument name that is the unboxed primitive value. JavaExpression evaluate = SCJavaDefn.createInvocation(new LocalName(fixedUpVarName + "$L", JavaTypeNames.RTVALUE), SCJavaDefn.EVALUATE, SCJavaDefn.EXECUTION_CONTEXT_VAR); evaluate = SCJavaDefn.unboxValue(javaDefn.getArgumentTypeName(argIndex), evaluate); LocalVariableDeclaration lvd = new LocalVariableDeclaration(new LocalVariable(fixedUpVarName, javaDefn.getArgumentTypeName(argIndex)), evaluate); block.addStatement(lvd); } else { JavaExpression.Nameable argVar = new LocalName(fixedUpVarName, JavaTypeNames.RTVALUE); JavaExpression evaluate = SCJavaDefn.createInvocation(argVar, SCJavaDefn.EVALUATE, SCJavaDefn.EXECUTION_CONTEXT_VAR); Assignment assign = new Assignment(argVar, evaluate); block.addStatement(new ExpressionStatement (assign)); } } } } return block; } /** * Generate code to check the state of the quit request flag and throw an interrupt exception if it is set. * @return a java statement that checks if quit is requested and takes appropriate action. */ private static JavaStatement checkForQuit() { // Call RTExecutionContext.isQuitRequest() and if true throw RTValue.InterruptException MethodInvocation isQuitRequested = new MethodInvocation.Instance(SCJavaDefn.EXECUTION_CONTEXT_VAR, "isQuitRequested", JavaTypeName.BOOLEAN, MethodInvocation.InvocationType.VIRTUAL); ThrowStatement throwStatement = new ThrowStatement(new JavaExpression.JavaField.Static(JavaTypeNames.RTVALUE, "INTERRUPT_EXCEPTION", JavaTypeName.CAL_EXECUTOR_EXCEPTION)); IfThenElseStatement conditional = new IfThenElseStatement(isQuitRequested, throwStatement); return conditional; } /** * Create the the version of the 'f' method used when we encounter a fully saturated * application and the strict arguments are already evaluated. * @param javaDefn * @throws CodeGenerationException */ private void createMethod_fSaturatedStrict(SCJavaDefn javaDefn) throws CodeGenerationException { int modifiers = Modifier.PUBLIC | Modifier.FINAL; // Figure out the methodName final int arity = javaDefn.getArity(); String methodName = functions.getFnNamePrefix(javaDefn.getFunctionName()) + "f" + arity + "S"; Block bodyBlock = javaDefn.genS_SC_Boxed(); // Figure out the arg names and types. String[] argNames = new String[arity + 1]; JavaTypeName[] argTypes = new JavaTypeName[arity + 1]; // Default type of argument is RTValue Arrays.fill(argTypes, JavaTypeNames.RTVALUE); // Fill in the argument names from the SC definition. for (int i = 0; i < arity; ++i) { argNames[i] = javaDefn.getJavaArgumentName(i); } // The last argument is the execution context. argNames[arity] = SCJavaDefn.EXECUTION_CONTEXT_NAME; argTypes[arity] = JavaTypeNames.RTEXECUTION_CONTEXT; // Try to get type info for this SC. for (int i = 0; i < arity; ++i) { if (javaDefn.isArgStrict(i) && javaDefn.isArgUnboxable(i)) { argTypes[i] = javaDefn.getArgumentTypeName(i); } } // Add the method to the class. JavaMethod javaMethod = new JavaMethod(modifiers, JavaTypeNames.RTVALUE, argNames, argTypes, null, methodName); javaClassRep.addMethod(javaMethod); // Add the throws declaration javaMethod.addThrows(JavaTypeName.CAL_EXECUTOR_EXCEPTION); JavaDocComment comment = new JavaDocComment(methodName); comment.addLine("This method implements the function logic of the CAL function " + javaDefn.getModuleName() + "." + javaDefn.getFunctionName()); javaMethod.setJavaDocComment(comment); // Add statistics generation. addStatsBlock (javaMethod, javaDefn); // Add the body if (javaDefn.isTailRecursive()) { Block loopBodyBlock = new Block(); if (!LECCMachineConfiguration.nonInterruptibleRuntime()) { // Add a check of the quit flag at the top of the loop body. loopBodyBlock.addStatement(checkForQuit()); } if (LECCMachineConfiguration.SANITY_CHECK_LET_VARS) { loopBodyBlock.addStatement(resetLetVarFlags(javaDefn.getFunctionName())); } if (LECCMachineConfiguration.generateDebugCode()) { loopBodyBlock.addStatement(generateDebugCode(javaDefn, argNames, argTypes)); } loopBodyBlock.addStatement(bodyBlock); UnconditionalLoop whileStatement = new UnconditionalLoop (SCJavaDefn.TAIL_RECURSION_LOOP_LABEL, loopBodyBlock); javaMethod.addStatement (whileStatement); } else { if (LECCMachineConfiguration.SANITY_CHECK_LET_VARS) { javaMethod.addStatement(resetLetVarFlags(javaDefn.getFunctionName())); } if (LECCMachineConfiguration.generateDebugCode()) { javaMethod.addStatement(generateDebugCode(javaDefn, argNames, argTypes)); } javaMethod.addStatement(bodyBlock); } } /** * Create the the version of the 'f' method used when we encounter a fully saturated * application and the strict arguments are already evaluated. * @param javaDefn * @throws CodeGenerationException */ private void createMethod_fUnboxedSaturatedStrict(SCJavaDefn javaDefn) throws CodeGenerationException { int modifiers = Modifier.PUBLIC | Modifier.FINAL; JavaTypeName returnType = javaDefn.getResultType(); // Figure out the methodName final int arity = javaDefn.getArity(); String methodName = functions.getFnNamePrefix(javaDefn.getFunctionName()) + "fUnboxed"; if (arity > 0) { methodName = methodName + arity + "S"; } Block bodyBlock = javaDefn.genS_SC_Unboxed(); // Figure out the arg names and types. String[] argNames = new String[arity + 1]; JavaTypeName[] argTypes = new JavaTypeName[arity + 1]; // Default type of argument is RTValue Arrays.fill(argTypes, JavaTypeNames.RTVALUE); // Fill in the argument names from the SC definition. for (int i = 0; i < arity; ++i) { argNames[i] = javaDefn.getJavaArgumentName(i); } // The last argument is the execution context. argNames[arity] = SCJavaDefn.EXECUTION_CONTEXT_NAME; argTypes[arity] = JavaTypeNames.RTEXECUTION_CONTEXT; // Try to get type info for this SC. for (int i = 0; i < arity; ++i) { if (javaDefn.isArgStrict(i) && javaDefn.isArgUnboxable(i)) { argTypes[i] = javaDefn.getArgumentTypeName(i); } } // Add the method to the class. JavaMethod javaMethod = new JavaMethod(modifiers, returnType, argNames, argTypes, null, methodName); javaClassRep.addMethod(javaMethod); // Add a comment indicating which CAL function this // corresponds to. JavaDocComment comment = new JavaDocComment(methodName); comment.addLine("This method implements the logic of the CAL function " + javaDefn.getModuleName() + "." + javaDefn.getFunctionName()); comment.addLine("This version of the logic returns an unboxed value."); javaMethod.setJavaDocComment(comment); // Add the throws declaration javaMethod.addThrows(JavaTypeName.CAL_EXECUTOR_EXCEPTION); // Add statistics generation. addStatsBlock (javaMethod, javaDefn); // Add the body if (javaDefn.isTailRecursive()) { Block loopBodyBlock = new Block(); if (!LECCMachineConfiguration.nonInterruptibleRuntime()) { // Add a check of the quit flag at the top of the loop body. loopBodyBlock.addStatement(checkForQuit()); } if (LECCMachineConfiguration.SANITY_CHECK_LET_VARS) { loopBodyBlock.addStatement(resetLetVarFlags(javaDefn.getFunctionName())); } if (LECCMachineConfiguration.generateDebugCode()) { loopBodyBlock.addStatement(generateDebugCode(javaDefn, argNames, argTypes)); } loopBodyBlock.addStatement(bodyBlock); UnconditionalLoop whileStatement = new UnconditionalLoop (SCJavaDefn.TAIL_RECURSION_LOOP_LABEL, loopBodyBlock); javaMethod.addStatement (whileStatement); } else { if (LECCMachineConfiguration.SANITY_CHECK_LET_VARS) { javaMethod.addStatement(resetLetVarFlags(javaDefn.getFunctionName())); } if (LECCMachineConfiguration.generateDebugCode()) { javaMethod.addStatement(generateDebugCode(javaDefn, argNames, argTypes)); } javaMethod.addStatement(bodyBlock); } } /** * Create the the version of the 'f' method used when we encounter a fully saturated * application and the strict arguments aren't already evaluated. * @param javaDefn * @param consolidateFunctions * @throws CodeGenerationException */ private void createMethod_fSaturatedLazy(SCJavaDefn javaDefn, boolean consolidateFunctions) throws CodeGenerationException { int modifiers = Modifier.PUBLIC | Modifier.FINAL; // Figure out the methodName final int arity = javaDefn.getArity(); String methodNamePrefix = functions.getFnNamePrefix(javaDefn.getFunctionName()); String methodName = methodNamePrefix + "f" + arity + "L"; // Figure out the arg names and types. String[] argNames = new String[arity + 1]; JavaTypeName[] argTypes = new JavaTypeName[arity + 1]; Arrays.fill(argTypes, JavaTypeNames.RTVALUE); for (int i = 0; i < arity; ++i) { // If the argument is strict and primitive add $L to the name so that we // can use the declared name for the primitive value. argNames[i] = javaDefn.getJavaArgumentName(i); if (javaDefn.isArgStrict(i) && javaDefn.isArgUnboxable(i)) { argNames[i] = argNames[i] + "$L"; } } argNames[arity] = SCJavaDefn.EXECUTION_CONTEXT_NAME; argTypes[arity] = JavaTypeNames.RTEXECUTION_CONTEXT; // Add the method to the class. JavaMethod javaMethod = new JavaMethod(modifiers, JavaTypeNames.RTVALUE, argNames, argTypes, null, methodName); javaClassRep.addMethod(javaMethod); // Add the throws declaration javaMethod.addThrows(JavaTypeName.CAL_EXECUTOR_EXCEPTION); JavaDocComment comment = new JavaDocComment(methodName); comment.addLine("This method implements the function logic of the CAL function " + javaDefn.getModuleName() + "." + javaDefn.getFunctionName()); javaMethod.setJavaDocComment(comment); if (consolidateFunctions) { JavaExpression[] args = new JavaExpression[arity + 1]; argTypes = new JavaTypeName[arity + 1]; Arrays.fill (argTypes, JavaTypeNames.RTVALUE); for (int i = 0; i < arity; ++i) { String varName = javaDefn.getJavaArgumentName(i); if (javaDefn.isArgStrict(i) && javaDefn.isArgUnboxable(i)) { varName += "$L"; } MethodVariable mv = new MethodVariable(varName); args[i] = mv; if (javaDefn.hasStrictArguments() && javaDefn.isArgStrict(i)) { args[i] = SCJavaDefn.createInvocation(args[i], SCJavaDefn.EVALUATE, SCJavaDefn.EXECUTION_CONTEXT_VAR); args[i] = callLastRef(args[i], mv); if (javaDefn.isArgUnboxable(i)) { args[i] = SCJavaDefn.unboxValue(javaDefn.getArgumentTypeName(i), args[i]); argTypes[i] = javaDefn.getArgumentTypeName(i); } } else { args[i] = callLastRef(args[i], mv); } } args[arity] = SCJavaDefn.EXECUTION_CONTEXT_VAR; argTypes[arity] = JavaTypeNames.RTEXECUTION_CONTEXT; JavaExpression mi = new MethodInvocation.Instance ( null, methodNamePrefix + "f" + arity + "S", args, argTypes, JavaTypeNames.RTVALUE, MethodInvocation.InvocationType.VIRTUAL); javaMethod.addStatement(new JavaStatement.ReturnStatement(mi)); } else { Block bodyBlock = javaDefn.genS_SC_Boxed(); // Add statistics generation. addStatsBlock (javaMethod, javaDefn); javaMethod.addStatement(generateStrictArgEvaluationBlock(javaDefn)); String[] unboxedLocalVarNames = null; JavaTypeName[] unboxedLocalVarTypes = null; if (LECCMachineConfiguration.generateDebugCode() || LECCMachineConfiguration.generateDebugCode()) { unboxedLocalVarNames = new String[arity]; unboxedLocalVarTypes = new JavaTypeName[arity]; for (int i = 0; i < arity; ++i) { unboxedLocalVarNames[i] = javaDefn.getJavaArgumentName(i); if (javaDefn.isArgStrict(i) && javaDefn.isArgUnboxable(i)) { unboxedLocalVarTypes[i] = javaDefn.getArgumentTypeName(i); } else { unboxedLocalVarTypes[i] = JavaTypeNames.RTVALUE; } } } // Add the body if (javaDefn.isTailRecursive()) { Block loopBodyBlock = new Block(); if (!LECCMachineConfiguration.nonInterruptibleRuntime()) { // Add a check of the quit flag at the top of the loop body. loopBodyBlock.addStatement(checkForQuit()); } if (LECCMachineConfiguration.SANITY_CHECK_LET_VARS) { loopBodyBlock.addStatement(resetLetVarFlags(javaDefn.getFunctionName())); } if (LECCMachineConfiguration.generateDebugCode()) { loopBodyBlock.addStatement(generateDebugCode(javaDefn, unboxedLocalVarNames, unboxedLocalVarTypes)); } loopBodyBlock.addStatement(bodyBlock); UnconditionalLoop whileStatement = new UnconditionalLoop (SCJavaDefn.TAIL_RECURSION_LOOP_LABEL, loopBodyBlock); javaMethod.addStatement (whileStatement); } else { if (LECCMachineConfiguration.SANITY_CHECK_LET_VARS) { javaMethod.addStatement(resetLetVarFlags(javaDefn.getFunctionName())); } if (LECCMachineConfiguration.generateDebugCode()) { javaMethod.addStatement(generateDebugCode(javaDefn, unboxedLocalVarNames, unboxedLocalVarTypes)); } javaMethod.addStatement(bodyBlock); } } } /** * If generation of statistics is turned on this method * adds the appropriate code to augment the statistics. * @param javaMethod * @param javaDefn */ private void addStatsBlock (JavaMethod javaMethod, SCJavaDefn javaDefn) { if (LECCMachineConfiguration.generateStatistics()) { MethodInvocation mi = new MethodInvocation.Instance(SCJavaDefn.EXECUTION_CONTEXT_VAR, "incrementNMethodCalls", JavaTypeName.VOID, MethodInvocation.InvocationType.VIRTUAL); javaMethod.addStatement(new ExpressionStatement(mi)); } if (LECCMachineConfiguration.generateCallCounts()) { JavaExpression args[] = new JavaExpression[2]; JavaTypeName argTypes[] = new JavaTypeName[2]; args[0] = LiteralWrapper.make(javaDefn.getModuleName().toSourceText()); args[1] = LiteralWrapper.make(javaDefn.getFunctionName()); argTypes[0] = argTypes[1] = JavaTypeName.STRING; MethodInvocation mi = new MethodInvocation.Instance(SCJavaDefn.EXECUTION_CONTEXT_VAR, "scCalled", args, argTypes, JavaTypeName.VOID, MethodInvocation.InvocationType.VIRTUAL); javaMethod.addStatement(new ExpressionStatement(mi)); } } /** * Adds debug processing: * Tracing that prints (when tracing, and all tracing options are enabled): * -the name of the executing thread * -the name of the function and the argument values, in the applicative style of CAL textual syntax. * Halting on breakpoints. * etc. * * Note that the processing takes place after the arguments that are plinged are evaluated to WHNF. Thus the tracing * does not occur at the immediate entry of the generated f function, but a bit later. This is conceptually closer * to what the meaning of plinged arguments in CAL source is, in that they are evaluated to WHNF prior to evaluating * the body of the function to WHNF. It also makes it much easier to do proper tracing in tail recursive functions for * each recursive call. * * @param javaDefn * @param argNames String[] names of the arguments to be traced, in argument order. Only the first method.getArity() names * are used. Also, argNames may actually be Java local variable or method variable names: we generate code in the * implementation below using LocalName to handle both cases. * @param argTypes JavaTypeName[] types of the traced arguments. * @return JavaStatement a java statement that will perform the debug processing */ private JavaStatement generateDebugCode ( final SCJavaDefn javaDefn, final String[] argNames, final JavaTypeName[] argTypes) { if (!LECCMachineConfiguration.generateDebugCode()) { throw new IllegalStateException(); } // Add debug processing. This includes things such as function tracing // and halting on breakpoints. //if ($ec.isDebugProcessingNeeded("Prelude.take")) { // // $ec.debugProcessing("Prelude.take", // new RTValue[]{CAL_Int.make(take$nElements$1), take$list$2}); // //} //notice that for non-RTValue fields, we need to box //$ec.isBreakpointEnabled($functionNameField) JavaExpression isDebuggingNeededCheck = new MethodInvocation.Instance( SCJavaDefn.EXECUTION_CONTEXT_VAR, "isDebugProcessingNeeded", LiteralWrapper.make(javaDefn.getQualifiedName().getQualifiedName()), JavaTypeName.STRING, JavaTypeName.BOOLEAN, MethodInvocation.InvocationType.VIRTUAL); JavaStatement.Block debuggingNeededThenBlock = new Block(); JavaStatement isDebuggingNeededIfStatement = new JavaStatement.IfThenElseStatement(isDebuggingNeededCheck, debuggingNeededThenBlock); final int arity = javaDefn.getArity(); //new RTValue[]{CAL_Int.make(take$nElements$1), take$list$2} JavaExpression[] argValues = new JavaExpression[arity]; for (int i = 0; i < arity; ++i) { String javaArgName = argNames[i]; JavaTypeName javaArgType = argTypes[i]; JavaExpression javaArgValue = new LocalName(javaArgName, javaArgType); if (!javaArgType.equals(JavaTypeNames.RTVALUE)) { javaArgValue = SCJavaDefn.boxExpression(javaArgType, javaArgValue); } argValues[i] = javaArgValue; } JavaExpression argValueArrayCreation = new JavaExpression.ArrayCreationExpression(JavaTypeNames.RTVALUE, argValues); //$ec.debugProcessing("Prelude.take", // new RTValue[]{CAL_Int.make(take$nElements$1), take$list$2})); JavaExpression suspend = new MethodInvocation.Instance( SCJavaDefn.EXECUTION_CONTEXT_VAR, "debugProcessing", new JavaExpression[] { LiteralWrapper.make(javaDefn.getQualifiedName().getQualifiedName()), argValueArrayCreation }, new JavaTypeName[] { JavaTypeName.STRING, JavaTypeName.CAL_VALUE_ARRAY }, JavaTypeName.VOID, MethodInvocation.InvocationType.VIRTUAL); debuggingNeededThenBlock.addStatement(new ExpressionStatement(suspend)); return isDebuggingNeededIfStatement; } /** * Create an inner class which represents a fully saturated application of the SC to * evaluated/primitive values. Add this to the existing class rep. * @param mf * @throws CodeGenerationException */ private void createStrictAppClass (MachineFunction mf) throws CodeGenerationException { javaClassRep.addInnerClass(getStrictAppClass(mf)); } /** * Get a new inner class representation of a fully saturated application of the SC to evaluated/primitive values. * @param mf * @return the JavaClassRep for the strict application node class * @throws CodeGenerationException */ private JavaClassRep getStrictAppClass(MachineFunction mf) throws CodeGenerationException{ // Get the fully-qualified superclass and class names; JavaTypeName superClassTypeName = JavaTypeNames.RTFULLAPP; // Determine whether the sc is public or package protected. // Construct the class access flags. int classModifiers = Modifier.FINAL | Modifier.PUBLIC | Modifier.STATIC; // No interfaces are implemented JavaTypeName[] interfaces = JavaDefinitionBuilder.EMPTY_TYPE_NAME_ARRAY; JavaTypeName strictAppTypeName = CALToJavaNames.createStrictInnerTypeNameFromSC(mf.getQualifiedName(), module); // Now instantiate the java class representation. JavaClassRep strictAppClassRep = new JavaClassRep(strictAppTypeName, superClassTypeName, classModifiers, interfaces); //add the function field, which is a reference to the function singleton. JavaFieldDeclaration functionFieldDec = new JavaFieldDeclaration (Modifier.PRIVATE | Modifier.FINAL, className, "function", null); strictAppClassRep.addFieldDeclaration(functionFieldDec); JavaField functionField = new JavaField.Instance (null, "function", className); // This class has a member field for each argument to the SC. JavaField[] functionArgumentMemberFields = new JavaField [mf.getArity()]; for (int i = 0; i < mf.getArity(); ++i) { JavaFieldDeclaration fieldDec; JavaField field; String fieldName = CALToJavaNames.fixupVarName(mf.getParameterNames()[i]); if (mf.getParameterStrictness()[i] && SCJavaDefn.canTypeBeUnboxed(mf.getParameterTypes()[i])) { fieldDec = new JavaFieldDeclaration (Modifier.PRIVATE, SCJavaDefn.typeExprToTypeName(mf.getParameterTypes()[i]), fieldName, null); field = new JavaField.Instance (null, fieldName, SCJavaDefn.typeExprToTypeName(mf.getParameterTypes()[i])); } else { fieldDec = new JavaFieldDeclaration (Modifier.PRIVATE, JavaTypeNames.RTVALUE, fieldName, null); field = new JavaField.Instance (null, fieldName, JavaTypeNames.RTVALUE); } strictAppClassRep.addFieldDeclaration(fieldDec); functionArgumentMemberFields[i] = field; } // Add the constructor strictAppClassRep.addConstructor(createStrictAppClass_constructor(mf, functionField, functionArgumentMemberFields)); // Add the reduce method. strictAppClassRep.addMethod(createStrictAppClass_method_reduce(mf, functionField, strictAppTypeName, functionArgumentMemberFields)); // Add the clearMembers() method. JavaMethod clearMembers = new JavaMethod (Modifier.PUBLIC | Modifier.FINAL, JavaTypeName.VOID, "clearMembers"); //the line "function = null;" should not be added to clearMembers. function is a reference to a singleton //so that clearing it doesn't actually free any memory. However, not clearing it means that the the method //debug_getNodeStartText works as desired even in the case when result == null and clearMembers() has been called. //clearMembers.addStatement(new ExpressionStatement(new Assignment(functionField, LiteralWrapper.NULL))); for (int i = 0; i < functionArgumentMemberFields.length; ++i) { if (!mf.getParameterStrictness()[i] || !SCJavaDefn.canTypeBeUnboxed(mf.getParameterTypes()[i])) { Assignment a = new Assignment(functionArgumentMemberFields[i], LiteralWrapper.NULL); clearMembers.addStatement(new ExpressionStatement(a)); } } strictAppClassRep.addMethod (clearMembers); createAppClass_debugMethods(strictAppClassRep, functionArgumentMemberFields); // We need to mark this class as containing assertions. // This is done manually because we don't always // generate the inner classes at the same time // as the containing class. strictAppClassRep.setContainsAssertions(); return strictAppClassRep; } /** * Generates a call to RTValue.lastRef where the * second argument is nulled out. * @param keep - the first argument to lastRef * @param nullOut - the reference to be assigned null and passed as the second argument to lastRef * @return a call to RTValue.lastRef */ private JavaExpression callLastRef(JavaExpression keep, JavaExpression.Nameable nullOut) { return new MethodInvocation.Static( JavaTypeNames.RTVALUE, "lastRef", new JavaExpression[]{keep, new Assignment(nullOut, LiteralWrapper.NULL)}, TWO_RTVALUES, JavaTypeNames.RTVALUE); } /** * Generate the reduce method for the strict application class * @param mf * @param functionField * @param strictAppTypeName * @param functionArgumentMemberFields * @return the generated reduce method * @throws CodeGenerationException */ private JavaMethod createStrictAppClass_method_reduce (MachineFunction mf, JavaField functionField, JavaTypeName strictAppTypeName, JavaField[] functionArgumentMemberFields) throws CodeGenerationException { // Add the reduce method. JavaMethod reduce = new JavaMethod (Modifier.PROTECTED | Modifier.FINAL, JavaTypeNames.RTVALUE, SCJavaDefn.EXECUTION_CONTEXT_NAME, JavaTypeNames.RTEXECUTION_CONTEXT, false, "reduce"); // Add the throws declaration reduce.addThrows(JavaTypeName.CAL_EXECUTOR_EXCEPTION); // Add the body. // if (result == null) { // setResult ( // function.f4S( // RTValue.lastRef(arg1, arg1 = null), ...)); // } // return result; final JavaField resultField = new JavaField.Instance(null, "result", JavaTypeNames.RTVALUE); OperatorExpression condition = new OperatorExpression.Binary (JavaOperator.EQUALS_OBJECT, resultField, LiteralWrapper.NULL); Block then = new Block(); JavaExpression args[] = new JavaExpression[mf.getArity() + 1]; JavaTypeName[] argTypes = new JavaTypeName[mf.getArity()+1]; for (int i = 0; i < mf.getArity(); ++i) { args[i] = functionArgumentMemberFields[i]; argTypes[i] = JavaTypeNames.RTVALUE; if (mf.getParameterStrictness()[i] && SCJavaDefn.canTypeBeUnboxed(mf.getParameterTypes()[i])) { argTypes[i] = SCJavaDefn.typeExprToTypeName(mf.getParameterTypes()[i]); } if (argTypes[i].equals(JavaTypeNames.RTVALUE)) { args[i] = callLastRef(args[i], (JavaField)args[i]); } } args[args.length-1] = SCJavaDefn.EXECUTION_CONTEXT_VAR; argTypes[argTypes.length-1] = JavaTypeNames.RTEXECUTION_CONTEXT; String fMethodName = functions.getFnNamePrefix(mf.getName()) + "f" + mf.getArity() + "S"; MethodInvocation fn = new MethodInvocation.Instance (functionField, fMethodName, args, argTypes, JavaTypeNames.RTVALUE, MethodInvocation.InvocationType.VIRTUAL); MethodInvocation setResult = new MethodInvocation.Instance (null, "setResult", fn, JavaTypeNames.RTVALUE, JavaTypeName.VOID, MethodInvocation.InvocationType.VIRTUAL); then.addStatement(new ExpressionStatement(setResult)); //call clearMembers(). We don't need to do this for tail recursive functions since clearMembers() will be called as //a result of setting the root node above. if (!mf.isTailRecursive()) { then.addStatement(new ExpressionStatement(new MethodInvocation.Instance(null, "clearMembers", JavaTypeName.VOID, MethodInvocation.InvocationType.VIRTUAL))); } reduce.addStatement(new IfThenElseStatement(condition, then)); reduce.addStatement(new ReturnStatement(resultField)); return reduce; } /** * Defines the CalValue.debug_* methods within the generated RTFullApp(S/L) inner classes. * @param applicationClassRep the class rep for the RTFullApp(S/L) static inner class of the RTSupercombinator derived class. * @param memberFields the member fields of the RTFullApp(S/L) class (does not include the function field). */ private void createAppClass_debugMethods(JavaClassRep applicationClassRep, JavaField[] memberFields) { applicationClassRep.addMethod(createAppClass_method_debug_getNChildren(memberFields)); applicationClassRep.addMethod(createAppClass_method_debug_getChild(memberFields)); applicationClassRep.addMethod(createAppClass_method_debug_getNodeStartText()); applicationClassRep.addMethod(createAppClass_method_debug_getNodeEndText()); applicationClassRep.addMethod(createAppClass_method_debug_getChildPrefixText(memberFields)); } /** * Create the constructor for the strict application node class. * @param mf * @param functionField * @param functionArgumentMemberFields * @return the java constructor * @throws CodeGenerationException */ private JavaConstructor createStrictAppClass_constructor( MachineFunction mf, JavaField functionField, JavaField[] functionArgumentMemberFields) throws CodeGenerationException { String innerClassName = CALToJavaNames.createStrictInnerClassNameFromSC(mf.getQualifiedName(), module); String constructorName = innerClassName.substring(innerClassName.lastIndexOf("$") + 1); // Get arg types JavaTypeName[] argTypes = new JavaTypeName[mf.getArity() + 1]; Arrays.fill(argTypes, JavaTypeNames.RTVALUE); argTypes[0] = className; // Figure out arg names. String[] argNames = new String[mf.getArity() + 1]; MethodVariable[] argVars = new MethodVariable[argNames.length]; argNames[0] = "$function"; argVars[0] = new MethodVariable(argNames[0]); for (int i = 0; i < mf.getArity(); i++) { argNames[i+1] = "$" + CALToJavaNames.fixupVarName(mf.getParameterNames()[i]); argVars[i+1] = new MethodVariable(argNames[i+1]); if (mf.getParameterStrictness()[i] && SCJavaDefn.canTypeBeUnboxed(mf.getParameterTypes()[i])) { argTypes[i+1] = SCJavaDefn.typeExprToTypeName(mf.getParameterTypes()[i]); } } // Add the constructor to the class. JavaConstructor javaConstructor = new JavaConstructor(Modifier.PUBLIC, argNames, argTypes, constructorName); // Add the body of the constructor // Check for null argument values via assert. JavaExpression argCheck = new OperatorExpression.Binary (JavaOperator.NOT_EQUALS_OBJECT, argVars[0], LiteralWrapper.NULL); for (int i = 1; i < argNames.length; ++i) { // We only do the null pointer check on arguments of type RTValue. // It is valid to have an external object (ex. String) which is null. if (argTypes[i].equals(JavaTypeNames.RTVALUE)) { JavaExpression compareArg = new OperatorExpression.Binary(JavaOperator.NOT_EQUALS_OBJECT, new MethodVariable(argNames[i]), LiteralWrapper.NULL); argCheck = new JavaExpression.OperatorExpression.Binary(JavaOperator.CONDITIONAL_AND, argCheck, compareArg); } } javaConstructor.addStatement( new AssertStatement( argCheck, new MethodInvocation.Instance(null, "badConsArgMsg", JavaTypeName.STRING, MethodInvocation.InvocationType.VIRTUAL), JavaTypeName.STRING)); // Assign the argument values to the class fields. javaConstructor.addStatement(new ExpressionStatement(new Assignment(functionField, new MethodVariable(argNames[0])))); for (int i = 1; i < argNames.length; i++) { JavaField field = functionArgumentMemberFields[i-1]; Assignment memberAssignment = new Assignment(field, new MethodVariable(argNames[i])); javaConstructor.addStatement(new ExpressionStatement(memberAssignment)); } return javaConstructor; } /** * Create an inner class which represents a fully saturated application of the SC. * Add this to the existing class rep. * @param mf */ private void createLazyAppClass (MachineFunction mf) { javaClassRep.addInnerClass(getLazyAppClass(mf)); } /** * Get a new inner class representation of a fully saturated application of the SC to evaluated/primitive values. * @param mf * @return the JavaClassRep for the lazy application node class */ private JavaClassRep getLazyAppClass (MachineFunction mf) { // Get the fully-qualified superclass and class names; JavaTypeName superClassTypeName = JavaTypeNames.RTFULLAPP; // Determine whether the sc is public or package protected. // Construct the class access flags. int classModifiers = Modifier.FINAL | Modifier.PUBLIC | Modifier.STATIC; // No interfaces are implemented JavaTypeName[] interfaces = JavaDefinitionBuilder.EMPTY_TYPE_NAME_ARRAY; JavaTypeName lazyAppTypeName = CALToJavaNames.createLazyInnerTypeNameFromSC(mf.getQualifiedName(), module); // Now instantiate the java class representation. JavaClassRep lazyAppClassRep = new JavaClassRep(lazyAppTypeName, superClassTypeName, classModifiers, interfaces); //add the function field, which is a reference to the function singleton. JavaFieldDeclaration functionFieldDec = new JavaFieldDeclaration (Modifier.PRIVATE | Modifier.FINAL, className, "function", null); lazyAppClassRep.addFieldDeclaration(functionFieldDec); JavaField functionField = new JavaField.Instance (null, "function", className); // This class has a member field for each argument to the SC. JavaField[] functionArgumentMemberFields = new JavaField [mf.getArity()]; for (int i = 0; i < mf.getArity(); ++i) { JavaFieldDeclaration fieldDec; JavaField field; String fieldName = CALToJavaNames.fixupVarName(mf.getParameterNames()[i]); fieldDec = new JavaFieldDeclaration (Modifier.PRIVATE, JavaTypeNames.RTVALUE, fieldName, null); field = new JavaField.Instance (null, fieldName, JavaTypeNames.RTVALUE); lazyAppClassRep.addFieldDeclaration(fieldDec); functionArgumentMemberFields[i] = field; } // Add the constructor. lazyAppClassRep.addConstructor(createLazyAppClass_constructor(mf, functionField, functionArgumentMemberFields)); // Add the reduce method. lazyAppClassRep.addMethod(createLazyAppClass_method_reduce(mf, functionField, lazyAppTypeName, functionArgumentMemberFields)); // Add the clearMembers() method. JavaMethod clearMembers = new JavaMethod (Modifier.PUBLIC | Modifier.FINAL, JavaTypeName.VOID, "clearMembers"); //the line "function = null;" should not be added to clearMembers. function is a reference to a singleton //so that clearing it doesn't actually free any memory. However, not clearing it means that the the method //debug_getNodeStartText works as desired even in the case when result == null and clearMembers() has been called. //clearMembers.addStatement(new ExpressionStatement(new Assignment(functionField, LiteralWrapper.NULL))); for (int i = 0; i < functionArgumentMemberFields.length; ++i) { Assignment a = new Assignment(functionArgumentMemberFields[i], LiteralWrapper.NULL); clearMembers.addStatement(new ExpressionStatement(a)); } lazyAppClassRep.addMethod (clearMembers); createAppClass_debugMethods(lazyAppClassRep, functionArgumentMemberFields); // We need to mark this class as containing assertions. // This is done manually because we don't always // generate the inner classes at the same time // as the containing class. lazyAppClassRep.setContainsAssertions(); return lazyAppClassRep; } /** * Create the reduce method for the lazy application class. * @param mf * @param functionField * @param lazyAppTypeName * @param functionArgumentMemberFields * @return the java method */ private JavaMethod createLazyAppClass_method_reduce(MachineFunction mf, JavaField functionField, JavaTypeName lazyAppTypeName, JavaField[] functionArgumentMemberFields) { // Add the reduce method. JavaMethod reduce = new JavaMethod (Modifier.PROTECTED | Modifier.FINAL, JavaTypeNames.RTVALUE, SCJavaDefn.EXECUTION_CONTEXT_NAME, JavaTypeNames.RTEXECUTION_CONTEXT, false, "reduce"); // Add the throws declaration reduce.addThrows(JavaTypeName.CAL_EXECUTOR_EXCEPTION); // Add the body. // if (result == null) { // setResult ( // function.f4L(RTValue.lastRef(arg1, arg1 = null), ...)); // } // return result; final JavaField resultField = new JavaField.Instance(null, "result", JavaTypeNames.RTVALUE); OperatorExpression condition = new OperatorExpression.Binary (JavaOperator.EQUALS_OBJECT, resultField, LiteralWrapper.NULL); Block then = new Block(); JavaExpression args[] = new JavaExpression[mf.getArity() + 1]; JavaTypeName[] argTypes = new JavaTypeName[mf.getArity()+1]; for (int i = 0; i < mf.getArity(); ++i) { args[i] = callLastRef(functionArgumentMemberFields[i], (JavaField)functionArgumentMemberFields[i]); argTypes[i] = JavaTypeNames.RTVALUE; } args[args.length-1] = SCJavaDefn.EXECUTION_CONTEXT_VAR; argTypes[argTypes.length-1] = JavaTypeNames.RTEXECUTION_CONTEXT; String fMethodName = functions.getFnNamePrefix(mf.getName()) + "f" + mf.getArity() + "L"; MethodInvocation fn = new MethodInvocation.Instance (functionField, fMethodName, args, argTypes, JavaTypeNames.RTVALUE, MethodInvocation.InvocationType.VIRTUAL); MethodInvocation setResult = new MethodInvocation.Instance (null, "setResult", fn, JavaTypeNames.RTVALUE, JavaTypeName.VOID, MethodInvocation.InvocationType.VIRTUAL); then.addStatement(new ExpressionStatement(setResult)); reduce.addStatement(new IfThenElseStatement(condition, then)); reduce.addStatement(new ReturnStatement(resultField)); return reduce; } /** * Create the constructor for the lazy application node class. * @param mf * @param functionField * @param functionArgumentMemberFields * @return the constructor */ private JavaConstructor createLazyAppClass_constructor (MachineFunction mf, JavaField functionField, JavaField[] functionArgumentMemberFields) { // Add the constructor String innerClassName = CALToJavaNames.createLazyInnerClassNameFromSC(mf.getQualifiedName(), module); String constructorName = innerClassName.substring(innerClassName.lastIndexOf("$") + 1); // Get arg types - all RTValues. JavaTypeName[] argTypes = new JavaTypeName[mf.getArity() + 1]; Arrays.fill(argTypes, JavaTypeNames.RTVALUE); argTypes[0] = className; // Figure out arg names and method variables. String[] argNames = new String[mf.getArity() + 1]; MethodVariable argVars[] = new MethodVariable[mf.getArity() + 1]; argNames[0] = "$function"; argVars[0] = new MethodVariable(argNames[0]); for (int i = 0; i < mf.getArity(); i++) { argNames[i+1] = "$" + CALToJavaNames.fixupVarName(mf.getParameterNames()[i]); argVars[i+1] = new MethodVariable(argNames[i+1]); } // Create the constructor. JavaConstructor javaConstructor = new JavaConstructor(Modifier.PUBLIC, argNames, argTypes, constructorName); // Add the body of the constructor // We check for null argument values via assert. JavaExpression argCheck = new OperatorExpression.Binary (JavaOperator.NOT_EQUALS_OBJECT, argVars[0], LiteralWrapper.NULL); for (int i = 1; i < argNames.length; ++i) { JavaExpression compareArg = new OperatorExpression.Binary(JavaOperator.NOT_EQUALS_OBJECT, argVars[i], LiteralWrapper.NULL); argCheck = new JavaExpression.OperatorExpression.Binary(JavaOperator.CONDITIONAL_AND, argCheck, compareArg); } javaConstructor.addStatement( new AssertStatement(argCheck, new MethodInvocation.Instance(null, "badConsArgMsg", JavaTypeName.STRING, MethodInvocation.InvocationType.VIRTUAL), JavaTypeName.STRING)); // Assign the constructor arguments to the class fields. javaConstructor.addStatement(new ExpressionStatement(new Assignment(functionField, argVars[0]))); for (int i = 1; i < argNames.length; i++) { JavaField field = functionArgumentMemberFields[i-1]; Assignment memberAssignment = new Assignment(field, argVars[i]); javaConstructor.addStatement(new ExpressionStatement(memberAssignment)); } return javaConstructor; } /** * Defines the abstract method CalValue.debug_getNChildren within RTAppL and RTAppS (the method is the * same in the strict and lazy generated app classes). * @param memberFields * @return the java method */ private JavaMethod createAppClass_method_debug_getNChildren(JavaField[] memberFields) { //todoBI the method is the same for Lazy and Strict App classes. This is a possible refactoring opportunity. //for example, for Prelude.upFromByUpToInt: //public final int debug_getNChildren() { // if (result != null) { // return super.debug_getNChildren(); // } else { // return 3; // } //} JavaMethod method = new JavaMethod (Modifier.PUBLIC | Modifier.FINAL, JavaTypeName.INT, "debug_getNChildren"); JavaField resultField = new JavaField.Instance(null, "result", JavaTypeNames.RTVALUE); JavaExpression conditionExpr = new JavaExpression.OperatorExpression.Binary( JavaOperator.NOT_EQUALS_OBJECT, resultField, LiteralWrapper.NULL) ; JavaStatement thenStatement = new ReturnStatement( new MethodInvocation.Instance( null, "debug_getNChildren", JavaTypeNames.RTFULLAPP, JavaExpression.EMPTY_JAVA_EXPRESSION_ARRAY, JavaExpression.EMPTY_TYPE_NAME_ARRAY, JavaTypeName.INT, MethodInvocation.InvocationType.SPECIAL)); JavaStatement elseStatement = new ReturnStatement(LiteralWrapper.make(Integer.valueOf(memberFields.length))); JavaStatement.IfThenElseStatement ifThenElseStatement = new JavaStatement.IfThenElseStatement (conditionExpr, thenStatement, elseStatement); method.addStatement(ifThenElseStatement); return method; } private JavaMethod createAppClass_method_debug_getChild(JavaField[] memberFields) { //for example, for Prelude.upFromByDownToInt this is: //public final CalValue debug_getChild(int childN) { // if (result != null) { // return super.debug_getChild(childN); // } // // switch (childN) { // case 0: // return CAL_Int.make(upFromByDownToInt$start$1); // case 1: // return CAL_Int.make(upFromByDownToInt$step$2); // case 2: // return CAL_Int.make(upFromByDownToInt$end$3); // default: // throw new IndexOutOfBoundsException(); // } //} JavaMethod method = new JavaMethod (Modifier.PUBLIC | Modifier.FINAL, JavaTypeName.CAL_VALUE, "childN", JavaTypeName.INT, false, "debug_getChild"); MethodVariable childNVar = new JavaExpression.MethodVariable("childN"); { // if (result != null) { // return super.debug_getChild(childN); // } JavaField resultField = new JavaField.Instance(null, "result", JavaTypeNames.RTVALUE); JavaExpression conditionExpr = new OperatorExpression.Binary( JavaOperator.NOT_EQUALS_OBJECT, resultField, LiteralWrapper.NULL) ; JavaStatement thenStatement = new ReturnStatement( new MethodInvocation.Instance( null, "debug_getChild", JavaTypeNames.RTFULLAPP, new JavaExpression[]{childNVar}, new JavaTypeName[] {JavaTypeName.INT}, JavaTypeName.CAL_VALUE, MethodInvocation.InvocationType.SPECIAL)); JavaStatement.IfThenElseStatement ifThenStatement = new JavaStatement.IfThenElseStatement (conditionExpr, thenStatement); method.addStatement(ifThenStatement); } SwitchStatement switchStatement = new SwitchStatement(childNVar); for (int i = 0, nFields = memberFields.length; i < nFields; ++i) { JavaField javaField = memberFields[i]; String javaFieldName = javaField.getFieldName(); JavaTypeName javaFieldType = javaField.getFieldType(); JavaExpression javaFieldExpr = new JavaExpression.JavaField.Instance(null, javaFieldName, javaFieldType); if (!javaFieldType.equals(JavaTypeNames.RTVALUE)) { javaFieldExpr = SCJavaDefn.boxExpression(javaFieldType, javaFieldExpr); } switchStatement.addCase( new SwitchStatement.IntCaseGroup(i, new ReturnStatement(javaFieldExpr))); } switchStatement.addCase( new SwitchStatement.DefaultCase( new JavaStatement.ThrowStatement( new JavaExpression.ClassInstanceCreationExpression(JavaTypeName.INDEX_OUT_OF_BOUNDS_EXCEPTION)))); method.addStatement(switchStatement); return method; } private JavaMethod createAppClass_method_debug_getNodeStartText() { //public final String debug_getNodeStartText() { // if (result != null) { // return super.debug_getNodeStartText(); // } else { // return "(" + function.getQualifiedName(); // } //} JavaMethod method = new JavaMethod (Modifier.PUBLIC | Modifier.FINAL, JavaTypeName.STRING, "debug_getNodeStartText"); JavaField resultField = new JavaField.Instance(null, "result", JavaTypeNames.RTVALUE); JavaExpression conditionExpr = new JavaExpression.OperatorExpression.Binary( JavaOperator.NOT_EQUALS_OBJECT, resultField, LiteralWrapper.NULL) ; JavaStatement thenStatement = new ReturnStatement( new MethodInvocation.Instance( null, "debug_getNodeStartText", JavaTypeNames.RTFULLAPP, JavaExpression.EMPTY_JAVA_EXPRESSION_ARRAY, JavaExpression.EMPTY_TYPE_NAME_ARRAY, JavaTypeName.STRING, MethodInvocation.InvocationType.SPECIAL)); JavaStatement elseStatement = new ReturnStatement( new JavaExpression.OperatorExpression.Binary(JavaOperator.STRING_CONCATENATION, LiteralWrapper.make("("), new MethodInvocation.Instance( new JavaExpression.JavaField.Instance(null, "function", className), "getQualifiedName", JavaTypeName.STRING, MethodInvocation.InvocationType.VIRTUAL))); JavaStatement.IfThenElseStatement ifThenElseStatement = new JavaStatement.IfThenElseStatement (conditionExpr, thenStatement, elseStatement); method.addStatement(ifThenElseStatement); return method; } private JavaMethod createAppClass_method_debug_getNodeEndText() { //public final String debug_getNodeEndText() { // if (result != null) { // return super.debug_getNodeEndText(); // } else { // return ")"; // } //} JavaMethod method = new JavaMethod (Modifier.PUBLIC | Modifier.FINAL, JavaTypeName.STRING, "debug_getNodeEndText"); JavaField resultField = new JavaField.Instance(null, "result", JavaTypeNames.RTVALUE); JavaExpression conditionExpr = new JavaExpression.OperatorExpression.Binary( JavaOperator.NOT_EQUALS_OBJECT, resultField, LiteralWrapper.NULL) ; JavaStatement thenStatement = new ReturnStatement( new MethodInvocation.Instance( null, "debug_getNodeEndText", JavaTypeNames.RTFULLAPP, JavaExpression.EMPTY_JAVA_EXPRESSION_ARRAY, JavaExpression.EMPTY_TYPE_NAME_ARRAY, JavaTypeName.STRING, MethodInvocation.InvocationType.SPECIAL)); JavaStatement elseStatement = new ReturnStatement(LiteralWrapper.make(")")); JavaStatement.IfThenElseStatement ifThenElseStatement = new JavaStatement.IfThenElseStatement (conditionExpr, thenStatement, elseStatement); method.addStatement(ifThenElseStatement); return method; } private JavaMethod createAppClass_method_debug_getChildPrefixText(JavaField[] memberFields) { //for example, if there are 2 fields: //public final String debug_getChildPrefixText(int childN) { // if (result != null) { // return super.debug_getChildPrefixText(childN); // } // // if (childN >= 0 && childN < 2) { // return " "; // } // // throw new IndexOutOfBoundsException(); //} JavaMethod method = new JavaMethod (Modifier.PUBLIC | Modifier.FINAL, JavaTypeName.STRING, "childN", JavaTypeName.INT, false, "debug_getChildPrefixText"); MethodVariable childNVar = new JavaExpression.MethodVariable("childN"); { // if (result != null) { // return super.debug_getChildPrefixText(childN); // } JavaField resultField = new JavaField.Instance(null, "result", JavaTypeNames.RTVALUE); JavaExpression conditionExpr = new OperatorExpression.Binary( JavaOperator.NOT_EQUALS_OBJECT, resultField, LiteralWrapper.NULL) ; JavaStatement thenStatement = new ReturnStatement( new MethodInvocation.Instance( null, "debug_getChildPrefixText", JavaTypeNames.RTFULLAPP, new JavaExpression[]{childNVar}, new JavaTypeName[]{JavaTypeName.INT}, JavaTypeName.STRING, MethodInvocation.InvocationType.SPECIAL)); JavaStatement.IfThenElseStatement ifThenStatement = new JavaStatement.IfThenElseStatement (conditionExpr, thenStatement); method.addStatement(ifThenStatement); } { // if (childN >= 0 && childN < 2) { // return " "; // } JavaExpression conditionExpr = new OperatorExpression.Binary( JavaOperator.CONDITIONAL_AND, new OperatorExpression.Binary( JavaOperator.GREATER_THAN_EQUALS_INT, childNVar, LiteralWrapper.make(Integer.valueOf(0))), new OperatorExpression.Binary( JavaOperator.LESS_THAN_INT, childNVar, LiteralWrapper.make(Integer.valueOf(memberFields.length)))); JavaStatement thenStatement = new ReturnStatement(LiteralWrapper.make(" ")); JavaStatement.IfThenElseStatement ifThenStatement = new JavaStatement.IfThenElseStatement (conditionExpr, thenStatement); method.addStatement(ifThenStatement); } method.addStatement( new JavaStatement.ThrowStatement( new JavaExpression.ClassInstanceCreationExpression(JavaTypeName.INDEX_OUT_OF_BOUNDS_EXCEPTION))); return method; } /** * An extension of MachineFunction used to describe lifted let variable * definition functions in the LECC machine. * @author rcypher */ static final class LECCLiftedLetVarMachineFunction implements MachineFunction { /** Qualified name of the function. */ private final QualifiedName functionName; /** arity of the function. */ private final int arity; /** Parameter names of the function.*/ private final String[] parameterNames; /** Parameter types of the function. */ private final TypeExpr[] parameterTypes; /** Parameter strictness for the function. */ private final boolean[] parameterStrictness; /** The body of the function. */ private Expression expression; /** Is the function part of an Adjunct. */ private final boolean isForAdjunct; /** The result type of the funciton. */ private final TypeExpr resultType; /** Set of names (i.e. String) comprising the names of functions that are strongly connected to this function.*/ private Set<String> connectedComponents = new HashSet<String>(); /** Flag indicating that machine specific code has been generated. */ private boolean codeGenerated = false; /** Can laziness be ignored when generating code for this function. */ private boolean ignoreLaziness = false; /** * If this function can be considered an alias of another function then aliasOf * will hold the name of another function. * * One function is an alias of another if all references to the alias can be * safely replaced by references to the aliased function. This is true when: * * 1. the body of the alias function consists solely of a call to the aliased * function. * 2. All the arguments to the alias are passed to the aliased function in the * same order as the alias's argument list * 3. the aliased function and the alias have the same arity * 4. the aliased function and the alias have compatible strictness (see below) * 5. the aliased function and the alias aren't defined in terms of each other * (ie, there is no cycle between the two) * 6. the aliased function is not a 0-arity foreign function * * Ex, in the following: * * foo x y = bar x y; * * bar a b = baz a b; * * baz i j k = quux i j k; * * quux m n o = quux2 m n o; * * quux2 p q r = baz p q r + baz r q p; * * foo is an alias of bar. * bar is _not_ an alias of baz, because bar and baz have different arities. * baz is _not_ an alias of quux, because quux is defined in terms of quux2, * which is defined in terms of baz. * * The strictness of an alias is compatible with that of an aliased function * when the same arguments are plinged, up to the last plinged argument of the * alias. In other words, it's okay for an aliased function to have extra * plinged arguments to the right of the last pling on the alias, but otherwise * they must match exactly. Ex: * * alpha x y z = delta x y z; * * beta x !y z = delta x y z; * * gamma !x y z = delta x y z; * * delta x !y !z = ...; * * * epsilon x y !z = zeta x y z; * * zeta !x y !z = ...; * * In the above code, alpha has compatible strictness with delta, because * alpha doesn't have any plinged arguments. * * beta has compatible strictness with delta, because the first and second * arguments have the same plings; the third argument doesn't need to have * the same strictness because the second argument is beta's last plinged * argument. * * gamma and delta do _not_ have compatible strictness, because gamma's * first argument is plinged and delta's is not. * * epsilon and zeta also don't have compatible strictness, because the * first argument's strictness does not match (which is important because * the third argument is epsilon's rightmost strict argument). * */ private QualifiedName aliasOf; /** * Flag indicating transformation optimizations have been applied. * Lifted let variable machine functions are created as part of * final code generation. As such they can be considered as * already optimized. */ private boolean optimized = true; /** * Flag indication that the expression used to have a call to unsafeCoerce */ private boolean hadUnsafeCoerce = false; LECCLiftedLetVarMachineFunction ( QualifiedName functionName, int arity, String[] parameterNames, TypeExpr[] parameterTypes, boolean[] parameterStrictness, TypeExpr resultType, Expression expression, boolean isForAdjunct) { this.functionName = functionName; this.arity = arity; this.parameterNames = parameterNames; this.parameterTypes = parameterTypes; this.parameterStrictness = parameterStrictness; this.expression = expression; this.isForAdjunct = isForAdjunct; this.resultType = resultType; } /** * @return Returns the optimized. */ public boolean isOptimized() { return optimized; } /** * Mark this function as being optimized. */ public void setOptimized() { this.optimized = true; } /** * @return True if the function contained a call to unsafeCoerce. */ public boolean getHadUnsafeCoerce(){ return hadUnsafeCoerce; } /** * Mark the funtion as having contained a call to unsafeCoerce. */ public void setHadUnsafeCoerce(){ this.hadUnsafeCoerce = true; } /** * Get the number of formal arguments for this supercombinator. * Creation date: (3/9/00 3:30:22 PM) * @return int */ public int getNFormalParameters() { return parameterNames.length; } /** * Get the type signature for the function. This is arguments and return type. * @return The function type signature. */ public TypeExpr[] getType(){ TypeExpr[] argAndResultTypes = new TypeExpr[parameterTypes.length + 1]; for(int i = 0; i < parameterTypes.length; ++i){ argAndResultTypes[i] = parameterTypes[i]; } argAndResultTypes[parameterTypes.length] = resultType; argAndResultTypes = TypeExpr.copyTypeExprs(argAndResultTypes); return argAndResultTypes; } /** * @return Returns the codeGenerated. */ public boolean isCodeGenerated() { return codeGenerated; } /** * @param codeGenerated The codeGenerated to set. */ public void setCodeGenerated(boolean codeGenerated) { this.codeGenerated = codeGenerated; } /** * @param isTailRecursive The isTailRecursive to set. */ public void setIsTailRecursive(boolean isTailRecursive) { // Lifted let variable definitions can never be // tail recursive. if (isTailRecursive) { throw new NullPointerException("Attempt to mark lifted let variable definition functions as tail recursive."); } } /** {@inheritDoc} */ public int compareTo (MachineFunction o) { return getName().compareTo(o.getName()); } @Override public boolean equals (Object o) { if (o == null || !(o instanceof MachineFunction)) { return false; } else { return getName().equals(((MachineFunction)o).getName()); } } @Override public int hashCode () { return getName().hashCode(); } /** * @return Returns the isTailRecursive flag. */ public boolean isTailRecursive() { return false; } /** Return the qualified name for this label. * @return QualifiedName */ public QualifiedName getQualifiedName () { return functionName; } /** * Return the name for this label * @return String */ public String getName() { return getQualifiedName().getUnqualifiedName(); } /** * @return true if this CAL function is marked as being valid for * eager evaluation. */ public boolean canFunctionBeEagerlyEvaluated () { return false; } /** * Returns the arity of the code associated with this label. * @return in The arity of the code associated with this label. */ public int getArity () { return arity; } /** * @return Returns the connectedComponent. */ public Set<String> getStronglyConnectedComponents() { return this.connectedComponents; } /** * @param connectedComponents (String set) The connectedComponents to set. Cannot be null. */ public void setStronglyConnectedComponents(Set<String> connectedComponents) { this.connectedComponents = connectedComponents; } /** * @return Returns the aliasOf. */ public QualifiedName getAliasOf() { return aliasOf; } public void setAliasOf(QualifiedName name) { this.aliasOf = name; } /** * @return true if this is a primitive function, false otherwise. */ public boolean isPrimitiveFunction() { return false; } /** * @return true if this is a foreign function, false otherwise. */ public boolean isForeignFunction () { return false; } /** * @return true if this is a CAL function (i.e. not primitive and not foreign), false otherwise. */ public boolean isCALFunction () { return true; } /** * @return true if this is a CAL Data Constructor. */ public boolean isDataConstructor () { return false; } /** * A CAF is a constant applicative form. * Currently zero arity CAL functions are CAFs but foreign functions are not. * This is because a zero arity foreign function can return a different value * each time it is evaluated (ex. a construtor). * @return true if this is a constant applicative form. */ public boolean isCAF () { return false; } /** * Return the timestamp associated with this entity. * @return long */ public long getTimeStamp() { throw new UnsupportedOperationException("LECCLiftedLetVarMachineFunction does not support getTimeStamp()"); } /** * @return strictness info for the arguments. */ public boolean[] getParameterStrictness() { return parameterStrictness; } /** * @return types of the arguments. */ public TypeExpr[] getParameterTypes() { return parameterTypes; } /** * @return Returns the expressionForm. */ public Expression getExpressionForm() { return expression; } /** * Set the expression form of this function. * @param e */ public void setExpression(Expression e) { this.expression = e; } /** * @return Returns the parameterNames. */ public String[] getParameterNames() { return parameterNames; } @Override public String toString () { StringBuilder sb = new StringBuilder(); sb.append (getQualifiedName()); sb.append (": arity = "); sb.append (getArity()); return sb.toString(); } /** * @return The result type of this function. */ public TypeExpr getResultType () { return resultType; } /** * @param function * @return true if this is strongly connected to function. */ public boolean isStronglyConnectedTo(String function) { return false; } /** * Derived classes can override this to generate appopriate disassembly. * @return the disassembled form of the function. */ public String getDisassembly () { return "Unable to disassemble function " + getQualifiedName() + "."; } /** * @return Returns the coreFunction. */ public CoreFunction getCoreFunction() { throw new UnsupportedOperationException("LECCLiftedLetVarMachineFunction does not support getCoreFunction()."); } /** * Write this MachineFunction instance out to the RecordOutputStream. * @param s */ public void write (RecordOutputStream s) { throw new UnsupportedOperationException("LECCLiftedLetVarMachineFunction does not support write(RecordOutputStream)."); } /** * Reads this MachineFunction instance. * Read position in the stream is before record header. * @param s * @param mti * @param msgLogger the logger to which to log deserialization messages. */ protected void read (RecordInputStream s, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) { throw new UnsupportedOperationException("LECCLiftedLetVarMachineFunction does not support read(RecordInputStream, ModuleTypeInfo, CompilerMessageLogger)."); } /** * Reads content of this MachineFunction instance. * Read position in the stream is after record header. * @param s * @param schema * @param mti * @param msgLogger the logger to which to log deserialization messages. */ protected void readContent (RecordInputStream s, int schema, ModuleTypeInfo mti, CompilerMessageLogger msgLogger) { throw new UnsupportedOperationException("LECCLiftedLetVarMachineFunction does not support read(RecordInputStream, int, ModuleTypeInfo, CompilerMessageLogger)."); } /** * @return literal value if this supercombinator is defined as a literal value, null otherwise */ public Object getLiteralValue() { return null; } /** * @return true if this MachineFunction corresponds to an adjunct function. */ public boolean isForAdjunct () { return isForAdjunct; } boolean canIgnoreLaziness() { return ignoreLaziness; } void setIgnoreLaziness(boolean ignoreLaziness) { this.ignoreLaziness = ignoreLaziness; } } } /** * A DataTypeDefinitionBuilder builds an internal object representation of a Java class file for a CAL data type. * @author Edward Lam */ static class DataTypeDefinitionBuilder { /** The type name of the Java class for the data type. */ private final JavaTypeName className; /** The Java class representation for the data type. */ private JavaClassRep javaClassRep; /** * The LECCModule instance corresponding to either the module defining * the entity. This is used for obtaining the appropriate * {@link LECCModule.ClassNameMapper} for use in mapping names. */ private final LECCModule module; /** (List of DataConstructor) the list of data constructors for this type. */ private final List<DataConstructor> dataConsList; /** (Set of FieldName) The field names calculated to be common to all data constructors. * i.e. same name, same type, same strictness */ private Set<FieldName> commonFieldNames = null; /** The type for which this builder is responsible. */ private final TypeConstructor typeConstructor; /** Object used to collect code generation info. May be null. */ private final CodeGenerationStats codeGenerationStats; /** * Constructor for a DataTypeDefinitionBuilder. * @param typeCons the type constructor. * @param module the LECCModule instance corresponding to the module defining the entity. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @param codeGenerationStats - object used to collect code generation information. May be null. */ private DataTypeDefinitionBuilder(TypeConstructor typeCons, LECCModule module, CodeGenerationStats codeGenerationStats) { if (typeCons == null) { throw new IllegalArgumentException ("Unable to create DataTypeDefinitionBuilder: null argument."); } // eg. "org.openquark.cal.internal.runtime.lecc.cal_Prelude.TYPE_Maybe" this.className = CALToJavaNames.createTypeNameFromType(typeCons, module); this.module = module; this.dataConsList = new ArrayList<DataConstructor>(typeCons.getNDataConstructors()); for (int i = 0; i < typeCons.getNDataConstructors(); ++i) { this.dataConsList.add(typeCons.getNthDataConstructor(i)); } this.typeConstructor = typeCons; this.codeGenerationStats = codeGenerationStats; } /** * Get the object representation for a Java class representing a data or type constructor by name. * The returned class will not have any inner class info set. * * @param typeConstructor the relevant type constructor * @param unqualifiedClassName the unqualified class name (ie. without the package name) * @param module * @param codeGenerationStats * @return the corresponding class representation, or null if the name is not a class built by this builder. * @throws CodeGenerationException */ static JavaClassRep getClassRep(TypeConstructor typeConstructor, String unqualifiedClassName, LECCModule module, CodeGenerationStats codeGenerationStats) throws CodeGenerationException { DataTypeDefinitionBuilder builder = new DataTypeDefinitionBuilder(typeConstructor, module, codeGenerationStats); if (unqualifiedClassName.equals(CALToJavaNames.createClassNameFromType(typeConstructor, module))) { return builder.getOuterClassRep(); } if (unqualifiedClassName.equals(CALToJavaNames.createUnqualifiedClassNameForTagDCFromType(typeConstructor, module))) { return builder.getTagDCClass(); } for (int i = 0, nDataConstructors = typeConstructor.getNDataConstructors(); i < nDataConstructors; i++) { DataConstructor dc = typeConstructor.getNthDataConstructor(i); if (unqualifiedClassName.equals(CALToJavaNames.createClassName(dc, module))) { return builder.getDCClass(dc); } else if (unqualifiedClassName.equals(CALToJavaNames.createFieldSelectionClassNameFromDC(dc, module))) { return builder.getDCFieldSelectionClass(dc); } } return null; } /** * Get the Java representation for the given data type. * This will include inner class info. * * @param typeCons the type constructor. * @param module the LECCModule instance corresponding to the module defining the entity. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @param codeGenerationStats - object used to collect code generation information. May be null. * @return JavaClassRep * @throws CodeGenerationException */ static JavaClassRep getDataTypeDefinition(TypeConstructor typeCons, LECCModule module, CodeGenerationStats codeGenerationStats) throws CodeGenerationException { DataTypeDefinitionBuilder instance = new DataTypeDefinitionBuilder (typeCons, module, codeGenerationStats); return instance.generateDataTypeDefinition(); } /** * Generate the java class representation of the data type for which this builder is responsible. * This representation will contain the relevant inner classes. * * @return the generated class representation. * @throws CodeGenerationException */ private JavaClassRep generateDataTypeDefinition() throws CodeGenerationException { if (javaClassRep == null) { generateOuterTypeDefinition(); // If there is more than one zero arity data constructor in this data type // we create a derived class called TagDC which holds an int tag field to // indicate which data constructor it corresponds to. We than create a single instance // of this class for each of the zero arity data constructors. This avoids // proliferation of classes. boolean createdTagDC = false; int nZeroArityDCs = 0; for (final DataConstructor dc : dataConsList) { if (dc.getArity() == 0) { nZeroArityDCs++; if (nZeroArityDCs > 1) { createClass_tagDC(); createdTagDC = true; break; } } } // Add inner classes for the data constructors. for (final DataConstructor dc : dataConsList) { if (dc.getArity() > 0 || !createdTagDC) { JavaClassRep dataConsClassRep = (new DataConsDefinitionBuilder(dc, className, commonFieldNames, module)).generateDataConsDefinition(); javaClassRep.addInnerClass(dataConsClassRep); } } } return javaClassRep; } /** * Get the object representation for a Java class representing a data constructor by name. * @param dc the data constructor whose definition to return. * @return the class representation for the data constructor. * @throws CodeGenerationException */ private JavaClassRep getDCClass(DataConstructor dc) throws CodeGenerationException { if (commonFieldNames == null) { setCommonFieldNames(new HashMap<FieldName, JavaTypeName>(), new HashMap<FieldName, Boolean>()); } return (new DataConsDefinitionBuilder(dc, className, commonFieldNames, module)).getDataConsDefinition(); } /** * Retrieve the inner FieldSelection class for the provided DC. * @param dc * @return The FieldSelection inner class. * @throws CodeGenerationException */ private JavaClassRep getDCFieldSelectionClass (DataConstructor dc) throws CodeGenerationException { return (new DataConsDefinitionBuilder(dc, className, commonFieldNames, module)).getDCFieldSelectionClass(); } /** * Get the object representation for a Java class representing the type constructor for this class. * This will not have any inner class info set. * * @return the class representation of the type constructor for this class. * @throws CodeGenerationException */ private JavaClassRep getOuterClassRep() throws CodeGenerationException { if (this.javaClassRep == null) { generateOuterTypeDefinition(); } return javaClassRep; } /** * Generate the java class representation of the data type for which this builder is responsible. * The generated representation will be the outermost class definition only -- no inner classes will have been generated. * @throws CodeGenerationException */ private void generateOuterTypeDefinition() throws CodeGenerationException { if (codeGenerationStats != null) { codeGenerationStats.incrementDataType(dataConsList.size()); for (final DataConstructor dc : dataConsList) { codeGenerationStats.incrementDCArity(dc.getArity()); } } int classModifiers = Modifier.ABSTRACT | Modifier.PUBLIC; JavaTypeName superClassTypeName = JavaTypeNames.RTCONS; // No interfaces are implemented JavaTypeName[] interfaces = JavaDefinitionBuilder.EMPTY_TYPE_NAME_ARRAY; this.javaClassRep = new JavaClassRep(className, superClassTypeName, classModifiers, interfaces); // If there is more than one zero arity data constructor in this data type // we create a derived class called TagDC which holds an int tag field to // indicate which data constructor it corresponds to. We than create a single instance // of this class for each of the zero arity data constructors. This avoids // proliferation of classes. int nTagDCs = 0; for (final DataConstructor dci : dataConsList) { if (dci.getArity() == 0) { nTagDCs++; if (nTagDCs > 1) { createFields_tagDCs(); createMethod_getTagDC(); break; } } } if (nTagDCs < dataConsList.size()) { // We need to mark this class as having // inner classes containing assertions. // This is done manually because we don't always // generate the inner classes at the same time // as the containing class. javaClassRep.setInnerClassContainsAssertions(); } // These fields will be lifted into the class generated for the type. Map<FieldName, JavaTypeName> fieldNameToTypeMap = new HashMap<FieldName, JavaTypeName> (); Map<FieldName, Boolean> fieldNameToStrictnessMap = new HashMap<FieldName, Boolean> (); setCommonFieldNames(fieldNameToTypeMap, fieldNameToStrictnessMap); // Generate fields in this class for any fields which are common to all data constructors. create_Fields(commonFieldNames, fieldNameToTypeMap, fieldNameToStrictnessMap); javaClassRep.addConstructor(createDefaultConstructor(commonFieldNames, fieldNameToTypeMap, fieldNameToStrictnessMap)); if (commonFieldNames.size() > 0) { javaClassRep.addConstructor(createConstructor_allArgs(commonFieldNames, fieldNameToTypeMap, fieldNameToStrictnessMap)); } Map<String, Map<String, FieldTypeAndStrictness>> fieldNameToInfo = new LinkedHashMap<String, Map<String, FieldTypeAndStrictness>>(); for (final DataConstructor dc : dataConsList) { TypeExpr[] fieldTypes = SCJavaDefn.getFieldTypesForDC (dc); for (int i = 0; i < dc.getArity(); ++i) { TypeExpr fieldTypeExpr = fieldTypes[i]; if (SCJavaDefn.canTypeBeUnboxed(fieldTypeExpr)) { String fieldName = SCJavaDefn.getJavaFieldNameFromDC(dc, i); Map<String, FieldTypeAndStrictness> infoMap = fieldNameToInfo.get(fieldName); if (infoMap == null) { infoMap = new LinkedHashMap<String, FieldTypeAndStrictness>(); fieldNameToInfo.put (fieldName, infoMap); } // 0 - means all fields with the name/type are lazy // 1 - means all fields with the name/type are strict // -1 - means some are strict some are lazy String fieldTypeName = SCJavaDefn.getNameForPrimitive(fieldTypeExpr); FieldTypeAndStrictness tas = infoMap.get(fieldTypeName); if (tas == null) { tas = new FieldTypeAndStrictness (dc.getNthFieldName(i), fieldName, fieldTypeExpr, dc.isArgStrict(i) ? 1 : 0); infoMap.put (fieldTypeName, tas); } else { if ((dc.isArgStrict(i) && tas.strictness != 1) || (!dc.isArgStrict(i) && tas.strictness != 0)) { tas.strictness = -1; } } } } } // Create a get_FieldName method for each unique field name in the set of // data constructors. This is the accessor that returns an RTValue. // If the field is common to all DCs it will be implemented at this // level. Otherwise the implementation at this level throws an error // and the DC classes are expected to override it. Set<String> doneFieldNames = new HashSet<String>(); for (final DataConstructor dc : dataConsList) { TypeExpr[] fieldTypes = SCJavaDefn.getFieldTypesForDC(dc); for (int i = 0; i < dc.getArity(); ++i) { String fieldName = SCJavaDefn.getJavaFieldNameFromDC(dc, i); if (!doneFieldNames.contains(fieldName)) { createMethod_getBoxedField (fieldName, dc.isArgStrict(i), dc.isArgStrict(i) ? fieldTypes[i] : null, commonFieldNames.contains(dc.getNthFieldName(i))); doneFieldNames.add(fieldName); } } } // Create a get_FieldName method for each unique field name/primitive type // combination in the data constructors. This returns an unboxed value. // These will be overridden by the data constructor classes for fields // which are not common. for (final Map.Entry<String, Map<String, FieldTypeAndStrictness>> entry : fieldNameToInfo.entrySet()) { String fieldName = entry.getKey(); Map<String, FieldTypeAndStrictness> infoMap = entry.getValue(); for (final Map.Entry<String, FieldTypeAndStrictness> typeEntry : infoMap.entrySet()) { FieldTypeAndStrictness tas = typeEntry.getValue(); // If all instances of this field/type are strict we create an unboxed // accessor. createMethod_getUnBoxedField(fieldName, tas.type, tas.strictness == 1, commonFieldNames.contains(tas.fieldName)); } } createMethod_getDCNameByOrdinal(); } /** * Calculate which fields are common to all data constructors - i.e. same name, same type, same strictness * Set this into commonFieldNames. * * @param fieldNameToType (FieldName->JavaTypeName) this map will be populated with mappings from field name to type * @param fieldNameToStrictness (FieldName->Boolean) this map will be populated with mappings from field name to strictness * @throws CodeGenerationException */ private void setCommonFieldNames(Map<FieldName, JavaTypeName> fieldNameToType, Map<FieldName, Boolean> fieldNameToStrictness) throws CodeGenerationException { this.commonFieldNames = new LinkedHashSet<FieldName>(); DataConstructor firstDC = dataConsList.get(0); boolean[] fieldStrictness = firstDC.getArgStrictness(); for (int i = 0; i < firstDC.getArity(); ++i) { TypeExpr[] fieldTypes = SCJavaDefn.getFieldTypesForDC(firstDC); FieldName fn = firstDC.getNthFieldName(i); commonFieldNames.add(fn); int index = firstDC.getFieldIndex(fn); fieldNameToType.put (fn, SCJavaDefn.typeExprToTypeName(fieldTypes[index])); fieldNameToStrictness.put (fn, Boolean.valueOf (fieldStrictness[i])); } for (int i = 1; i < dataConsList.size(); ++i) { DataConstructor dc = dataConsList.get(i); TypeExpr[] fieldTypes = SCJavaDefn.getFieldTypesForDC(dc); fieldStrictness = dc.getArgStrictness(); Set<FieldName> commonFieldNamesClone = new HashSet<FieldName>(commonFieldNames); for (int j = 0, dcArity = dc.getArity(); j < dcArity; ++j) { commonFieldNamesClone.remove(dc.getNthFieldName(j)); } for (final FieldName fieldName : commonFieldNamesClone) { commonFieldNames.remove(fieldName); } HashSet<FieldName> set = new LinkedHashSet<FieldName>(); for (final FieldName fn : commonFieldNames) { int index = dc.getFieldIndex(fn); JavaTypeName jt = fieldNameToType.get(fn); boolean fs = fieldNameToStrictness.get(fn).booleanValue(); if (fs == fieldStrictness[index] && SCJavaDefn.typeExprToTypeName(fieldTypes[index]).equals(jt)) { set.add(fn); } } this.commonFieldNames = set; } } /** * Create a static final field for each zero arity DC. */ private void createFields_tagDCs () { JavaTypeName tagDCTypeName = CALToJavaNames.createTypeNameForTagDCFromType(typeConstructor, module); int fieldModifiers = Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL; for (final DataConstructor dc : dataConsList) { if (dc.getArity() == 0) { JavaExpression initializer = new ClassInstanceCreationExpression(tagDCTypeName, LiteralWrapper.make(Integer.valueOf(dc.getOrdinal())), JavaTypeName.INT); String fieldName = CALToJavaNames.fixupVarName(dc.getName().getUnqualifiedName()); JavaFieldDeclaration fieldDec = new JavaFieldDeclaration(fieldModifiers, tagDCTypeName, fieldName, initializer); javaClassRep.addFieldDeclaration(fieldDec); } } } /** * Create the default constructor for the data type class. * @param fieldNames * @param fieldNameToType * @param fieldNameToStrictness * @return the constructor. */ private JavaConstructor createDefaultConstructor (Set<FieldName> fieldNames, Map<FieldName, JavaTypeName> fieldNameToType, Map<FieldName, Boolean> fieldNameToStrictness) { JavaConstructor constructor = new JavaConstructor (Modifier.PROTECTED, className.getUnqualifiedJavaSourceName()); // The final fields need to be initialized to a default value. for (final FieldName fn : fieldNames) { boolean strict = fieldNameToStrictness.get(fn).booleanValue(); if (!strict) { continue; } JavaTypeName type = fieldNameToType.get(fn); String fieldName = SCJavaDefn.getJavaFieldNameFromFieldName(fn); JavaExpression assign = new JavaExpression.Assignment(new JavaField.Instance(null, fieldName, type), getDefaultValueForType(type)); constructor.addStatement(new ExpressionStatement(assign)); } return constructor; } /** * Create a constructor for the data type class. * @param fieldNames - set of FieldName representing fields lifted into the data type class * @param fieldNameToType - map of FieldName -> JavaTypeName * @param fieldNameToStrictness - map of FieldName -> boolean * @return the constructor for the data type class. */ private JavaConstructor createConstructor_allArgs (Set<FieldName> fieldNames, Map<FieldName, JavaTypeName> fieldNameToType, Map<FieldName, Boolean> fieldNameToStrictness) { String[] argNames = new String [fieldNames.size()]; JavaTypeName[] argTypes = new JavaTypeName [fieldNames.size()]; Block constructorBody = new Block(); int i = 0; for (final FieldName fn : fieldNames) { boolean strict = fieldNameToStrictness.get(fn).booleanValue(); JavaTypeName type = JavaTypeNames.RTVALUE; if (strict) { type = fieldNameToType.get(fn); } String fieldName = SCJavaDefn.getJavaFieldNameFromFieldName(fn); String argName = fieldName+"$"; argNames[i] = argName; argTypes[i] = type; MethodVariable mv = new MethodVariable (argName); JavaExpression.JavaField.Instance field = new JavaExpression.JavaField.Instance(null, fieldName, type); JavaExpression assign = new Assignment (field, mv); constructorBody.addStatement(new ExpressionStatement(assign)); i++; } JavaConstructor constructor = new JavaConstructor (Modifier.PROTECTED, argNames, argTypes, className.getUnqualifiedJavaSourceName()); constructor.addStatement(constructorBody); return constructor; } /** * Create fields for this class. * @param fieldNames - Set of FieldName * @param fieldNameToType - Map of FieldName -> TypeExpr * @param fieldNameToStrictness - Map of FieldName -> Boolean */ private void create_Fields (Set<FieldName> fieldNames, Map<FieldName, JavaTypeName> fieldNameToType, Map<FieldName, Boolean> fieldNameToStrictness) { for (final FieldName fieldName : fieldNames) { boolean strict = fieldNameToStrictness.get(fieldName).booleanValue(); JavaTypeName type = JavaTypeNames.RTVALUE; int modifiers = 0; if (strict) { modifiers = Modifier.FINAL; type = fieldNameToType.get(fieldName); } String javaFieldName = SCJavaDefn.getJavaFieldNameFromFieldName(fieldName); JavaFieldDeclaration fieldDec = new JavaFieldDeclaration (modifiers, type, javaFieldName, null); javaClassRep.addFieldDeclaration(fieldDec); } } /** * Create the method: * protected final String getDCNameByOrdinal(int dcOrdinal) * This overrides the implementation in the RTCons base class. */ private void createMethod_getDCNameByOrdinal() { JavaMethod javaMethod = new JavaMethod(Modifier.PROTECTED | Modifier.FINAL, JavaTypeName.STRING, "dcOrdinal", JavaTypeName.INT, false, "getDCNameByOrdinal"); javaClassRep.addMethod(javaMethod); SwitchStatement sw = new SwitchStatement(new MethodVariable("dcOrdinal")); for (int i = 0, n = dataConsList.size(); i < n; ++i) { DataConstructor dc = dataConsList.get(i); SwitchStatement.IntCaseGroup icg = new SwitchStatement.IntCaseGroup( dc.getOrdinal(), new ReturnStatement(LiteralWrapper.make(dc.getName().getUnqualifiedName()))); sw.addCase(icg); } javaMethod.addStatement(sw); // If the argument doesn't match the ordinal for any DC we // throw an error. MethodInvocation badValue = new MethodInvocation.Static( JavaTypeNames.RTVALUE, "badValue_Object", new JavaExpression[]{LiteralWrapper.NULL, LiteralWrapper.make("Invalid DC ordinal in getDCNameByOrdinal() for " + className.toString())}, new JavaTypeName[]{JavaTypeName.ERRORINFO, JavaTypeName.STRING}, JavaTypeName.OBJECT); javaMethod.addStatement (new ReturnStatement(new CastExpression(JavaTypeName.STRING, badValue))); } /** * Create the getTagDC(int tag) method. This method * returns the TagDC instance corresponding to the given ordinal. */ private void createMethod_getTagDC() { int modifiers = Modifier.PUBLIC | Modifier.FINAL | Modifier.STATIC; // Add the method to the class. JavaTypeName tagDCTypeName = CALToJavaNames.createTypeNameForTagDCFromType(typeConstructor, module); JavaMethod javaMethod = new JavaMethod(modifiers, tagDCTypeName, "ordinal", JavaTypeName.INT, false, "getTagDC"); javaClassRep.addMethod(javaMethod); // Add the body.. // switch (ordinal) { // case 1: ...; // } SwitchStatement ordSwitch = new SwitchStatement(METHODVAR_ORDINAL); for (final DataConstructor dc : dataConsList) { if (dc.getArity() == 0) { // Return the static TagDC instance for this ordinal. String fieldName = CALToJavaNames.fixupVarName(dc.getName().getUnqualifiedName()); JavaField field = new JavaField.Static(className, fieldName, tagDCTypeName); SwitchStatement.SwitchCase sc = new SwitchStatement.IntCaseGroup(dc.getOrdinal(), new ReturnStatement(field)); ordSwitch.addCase(sc); } else { // This is a valid ordinal for the data type but does not correspond to a zero arity DC. LiteralWrapper badValueMessageWrapper = LiteralWrapper.make ("Attempt to treat " + dc.getName() + " as a zero arity data constructor."); Block block = new Block(); block.addStatement(new JavaStatement.LineComment(dc.getName().getQualifiedName())); JavaExpression castExpression = new CastExpression(tagDCTypeName, new MethodInvocation.Static(JavaTypeNames.RTVALUE, "badValue", badValueMessageWrapper, JavaTypeName.STRING, JavaTypeNames.RTVALUE)); block.addStatement(new ReturnStatement(castExpression)); ordSwitch.addCase(new SwitchStatement.IntCaseGroup (dc.getOrdinal(), block)); } } // Add a default case in the switch to throw an error if an invalid ordinal value is used. Block defaultBlock = new Block(); LocalVariable bf = new LocalVariable("bf", JavaTypeName.STRING_BUILDER); defaultBlock.addStatement(new LocalVariableDeclaration (bf, new ClassInstanceCreationExpression(JavaTypeName.STRING_BUILDER))); LiteralWrapper badValueMessageWrapper1 = LiteralWrapper.make("Invalid ordinal value of "); JavaExpression message = new MethodInvocation.Instance(bf, "append", badValueMessageWrapper1, JavaTypeName.STRING, JavaTypeName.STRING_BUILDER, MethodInvocation.InvocationType.VIRTUAL); message = new MethodInvocation.Instance(message, "append", METHODVAR_ORDINAL, JavaTypeName.INT, JavaTypeName.STRING_BUILDER, MethodInvocation.InvocationType.VIRTUAL); LiteralWrapper badValueMessageWrapper2 = LiteralWrapper.make(" in " + className.toString() + ".getTagDC()."); message = new MethodInvocation.Instance(message, "append", badValueMessageWrapper2, JavaTypeName.STRING, JavaTypeName.STRING_BUILDER, MethodInvocation.InvocationType.VIRTUAL); defaultBlock.addStatement (new ExpressionStatement(message)); message = new MethodInvocation.Instance(bf, "toString", JavaTypeName.STRING, MethodInvocation.InvocationType.VIRTUAL); defaultBlock.addStatement (new ReturnStatement(new CastExpression(tagDCTypeName, new MethodInvocation.Static(JavaTypeNames.RTVALUE, "badValue", message, JavaTypeName.STRING, JavaTypeNames.RTVALUE)))); ordSwitch.addCase(new SwitchStatement.DefaultCase (defaultBlock)); // Add the switch statement to the method. javaMethod.addStatement(ordSwitch); } /** * Generate an accessor function of the form RTValue get_FieldName() {} * @param fieldName * @param fieldIsStrict * @param fieldTypeExpr * @param implementAtThisLevel * @throws CodeGenerationException */ private void createMethod_getBoxedField (String fieldName, boolean fieldIsStrict, TypeExpr fieldTypeExpr, boolean implementAtThisLevel) throws CodeGenerationException { int modifiers = Modifier.PUBLIC; JavaTypeName fieldType = SCJavaDefn.typeExprToTypeName(fieldTypeExpr); boolean primitiveType = !fieldType.equals(JavaTypeNames.RTVALUE); // Add the method to the class. String methodName = "get" + fieldName; JavaMethod javaMethod = new JavaMethod(modifiers, JavaTypeNames.RTVALUE, methodName); javaClassRep.addMethod(javaMethod); if (implementAtThisLevel) { JavaField field = new JavaField.Instance (null, fieldName, fieldType); if (primitiveType) { javaMethod.addStatement(new ReturnStatement(SCJavaDefn.boxExpression(fieldTypeExpr, field))); } else { if (!fieldIsStrict) { // We have a non-strict field of type RTValue. In order to reduce space usage // we want to update the field value to be the result of the original suspension. // If the field value is an RTResultFunction we want to re-assign the field with // a call to 'getValue()'. // For example: //public final RTValue get_head() { // RTValue field; // if ((field = _head) instanceof RTResultFunction) { // return (_head = field.getValue()); // } // return field; //} String localName = fieldName+"$"; LocalVariable localVariable = new LocalVariable(localName, fieldType); LocalVariableDeclaration localDeclaration = new LocalVariableDeclaration(localVariable); javaMethod.addStatement(localDeclaration); Assignment localAssignment = new Assignment(localVariable, field); InstanceOf checkType = new InstanceOf(localAssignment, JavaTypeNames.RTRESULT_FUNCTION); MethodInvocation getValue = new MethodInvocation.Instance(localVariable, "getValue", JavaTypeNames.RTVALUE, MethodInvocation.InvocationType.VIRTUAL); Assignment fieldAssignment = new Assignment(field, getValue); ReturnStatement returnNewVal = new ReturnStatement(fieldAssignment); IfThenElseStatement ifThen = new IfThenElseStatement(checkType, returnNewVal); javaMethod.addStatement(ifThen); javaMethod.addStatement(new ReturnStatement (localVariable)); } else { javaMethod.addStatement(new ReturnStatement (field)); } } } else { // This class should throw an error for any access. The methods will // be overridden by derived classes for each data constructor. // We can simply call the method badFieldAccessor() implemented in // RTCons. MethodInvocation mi = new MethodInvocation.Instance( null, "badFieldAccessor", LiteralWrapper.make(fieldName), JavaTypeName.STRING, JavaTypeNames.RTVALUE, MethodInvocation.InvocationType.VIRTUAL); javaMethod.addStatement (new ReturnStatement (mi)); } } /** * Generate an accessor function of the form Type get_FieldName_As_Type(RTExecutionContext $ec) {} * @param fieldName * @param fieldTypeExpr * @param isStrict - indicates that all instances of this fieldname/type are strict * @param implementAtThisLevel * @throws CodeGenerationException */ private void createMethod_getUnBoxedField (String fieldName, TypeExpr fieldTypeExpr, boolean isStrict, boolean implementAtThisLevel) throws CodeGenerationException { int modifiers = Modifier.PUBLIC; JavaTypeName fieldType = SCJavaDefn.typeExprToTypeName(fieldTypeExpr); // Add the method to the class. String methodName = "get" + fieldName + "_As_" + SCJavaDefn.getNameForPrimitive(fieldType); JavaMethod javaMethod; // Don't need to pass in an execution context as this will always be evaluted. javaMethod = new JavaMethod(modifiers, fieldType, methodName); // Add the throws declaration javaMethod.addThrows(JavaTypeName.CAL_EXECUTOR_EXCEPTION); javaClassRep.addMethod(javaMethod); if (implementAtThisLevel && isStrict) { JavaField field = new JavaField.Instance (null, fieldName, fieldType); javaMethod.addStatement(new ReturnStatement (field)); } else { // This class should throw an error for any access. The methods will // be overridden by derived classes for each data constructor. // We want to call the RTCons method badFieldAccessor_... // that matches the type of the field. String fieldTypeString = SCJavaDefn.getNameForPrimitive(fieldType); String castTypeString = null; if (!(fieldType instanceof JavaTypeName.Primitive)) { castTypeString = fieldTypeString; fieldTypeString = "Object"; } MethodInvocation mi = new MethodInvocation.Instance( null, "badFieldAccessor_" + fieldTypeString, LiteralWrapper.make(fieldName), JavaTypeName.STRING, fieldType, MethodInvocation.InvocationType.VIRTUAL); if (castTypeString != null) { javaMethod.addStatement(new ReturnStatement(new JavaExpression.CastExpression(fieldType, mi))); } else { javaMethod.addStatement (new ReturnStatement (mi)); } } } /** * Create an inner class 'TagDC' which is used to represent the various zero arity data constructors. * Add this to the existing class rep. */ private void createClass_tagDC() { javaClassRep.addInnerClass(getTagDCClass()); } /** * Get a new inner class 'TagDC' representation used to represent the various zero arity data constructors. * @return the JavaClassRep for the tag DC class */ private JavaClassRep getTagDCClass() { // Determine access.. // Determine access.. int classModifiers = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; // No interfaces are implemented JavaTypeName[] interfaces = JavaDefinitionBuilder.EMPTY_TYPE_NAME_ARRAY; // Now instantiate the java class representation. JavaTypeName tagDCTypeName = CALToJavaNames.createTypeNameForTagDCFromType(typeConstructor, module); JavaClassRep tagDCClassRep = new JavaClassRep(tagDCTypeName, className, classModifiers, interfaces); // Add the body of the class. // private final int tag; JavaFieldDeclaration tagField = new JavaFieldDeclaration (Modifier.PRIVATE | Modifier.FINAL, JavaTypeName.INT, "tag", null); tagDCClassRep.addFieldDeclaration(tagField); // public TagDC(int tagVal) {this.tag = tagVal;} JavaConstructor javaConstructor = new JavaConstructor(Modifier.PUBLIC, new String[]{"tagVal"}, new JavaTypeName[]{JavaTypeName.INT}, "TagDC"); tagDCClassRep.addConstructor(javaConstructor); Assignment tagAssign = new Assignment (new JavaField.Instance(null, "tag", JavaTypeName.INT), METHODVAR_TAGVAL); javaConstructor.addStatement(new ExpressionStatement(tagAssign)); // public final int getArity() {return 0;} int modifiers = Modifier.PUBLIC | Modifier.FINAL; JavaTypeName returnType = JavaTypeName.INT; JavaMethod javaMethod = new JavaMethod(modifiers, returnType, "getArity"); tagDCClassRep.addMethod(javaMethod); javaMethod.addStatement(new ReturnStatement(LiteralWrapper.make(Integer.valueOf(0)))); // public final int getOrdinalValue(){return tag;} modifiers = Modifier.PUBLIC | Modifier.FINAL; returnType = JavaTypeName.INT; javaMethod = new JavaMethod(modifiers, returnType, "getOrdinalValue"); tagDCClassRep.addMethod(javaMethod); javaMethod.addStatement(new ReturnStatement(new JavaField.Instance(null, "tag", JavaTypeName.INT))); // public final String getModuleName() ... modifiers = Modifier.PUBLIC | Modifier.FINAL; returnType = JavaTypeName.STRING; javaMethod = new JavaMethod(modifiers, returnType, "getModuleName"); tagDCClassRep.addMethod(javaMethod); javaMethod.addStatement(new ReturnStatement(LiteralWrapper.make (typeConstructor.getName().getModuleName().toSourceText()))); // public final String getUnqualifiedName() ... modifiers = Modifier.PUBLIC | Modifier.FINAL; returnType = JavaTypeName.STRING; javaMethod = new JavaMethod(modifiers, returnType, "getUnqualifiedName"); tagDCClassRep.addMethod(javaMethod); SwitchStatement sw = new SwitchStatement (new JavaField.Instance(null, "tag", JavaTypeName.INT)); for (int i = 0, nDCs = dataConsList.size(); i < nDCs; ++i) { DataConstructor dc = dataConsList.get (i); sw.addCase(new SwitchStatement.IntCaseGroup(dc.getOrdinal(), new ReturnStatement (LiteralWrapper.make(dc.getName().getUnqualifiedName())))); } javaMethod.addStatement (sw); javaMethod.addStatement(new ReturnStatement(LiteralWrapper.make ("Unknown data constructor"))); // public final String getQualfiedName() ... modifiers = Modifier.PUBLIC | Modifier.FINAL; returnType = JavaTypeName.STRING; javaMethod = new JavaMethod(modifiers, returnType, "getQualifiedName"); tagDCClassRep.addMethod(javaMethod); sw = new SwitchStatement (new JavaField.Instance(null, "tag", JavaTypeName.INT)); for (int i = 0, nDCs = dataConsList.size(); i < nDCs; ++i) { DataConstructor dc = dataConsList.get (i); sw.addCase(new SwitchStatement.IntCaseGroup(dc.getOrdinal(), new ReturnStatement (LiteralWrapper.make(dc.getName().getQualifiedName())))); } javaMethod.addStatement (sw); javaMethod.addStatement(new ReturnStatement(LiteralWrapper.make ("Unknown data constructor"))); return tagDCClassRep; } } /** * A DataConsDefinitionBuilder builds an internal object representation of a Java class file for a CAL data constructor. * @author Edward Lam */ static class DataConsDefinitionBuilder { /** The type name of the Java class for the data constructor. */ private final JavaTypeName className; /** The type name of the Java class for the type to which the data constructor belongs. * This is also the data constructor's superclass. */ private final JavaTypeName dataTypeClassName; /** The data constructor associated with this definition. */ private final DataConstructor dc; /** The Java class representation for the data constructor. */ private JavaClassRep javaClassRep; /** * The LECCModule instance corresponding to either the module defining * the entity. This is used for obtaining the appropriate * {@link LECCModule.ClassNameMapper} for use in mapping names. */ private final LECCModule module; /** The name to use for the singleton instance field. */ private final String instanceName; /** Flag indicating whether this data constructor has any strict fields. */ private final boolean[] fieldStrictness; /** true if at least one of the fields has an unboxed form (as a Java primitive type or object type rather than an RTValue).*/ private boolean dcHasPrimitives; private final TypeExpr[] fieldTypes; /** same cardinality as fieldTypes. The names of the fields of the data constructor as appearing in its Java class representation. */ private final String[] javaFieldNames; /** (FieldName) fields that are common to all data constructors for this type. They must be indentically plinged. */ private final Set<FieldName> commonFields; /** * Constructor for a DataConsDefinitionBuilder. * @param dc data constructor for which to build a definition. * @param dataTypeClassName the type name of the data type. * @param commonFields - FieldName -> fields that are common to all data constructors for this type * @param module the LECCModule instance corresponding to the module defining the entity. * This is used for obtaining the appropriate {@link LECCModule.ClassNameMapper} for use in mapping names. * @throws CodeGenerationException */ DataConsDefinitionBuilder(DataConstructor dc, JavaTypeName dataTypeClassName, Set<FieldName> commonFields, LECCModule module) throws CodeGenerationException { if (dc == null || dataTypeClassName == null) { throw new IllegalArgumentException ("Unable to create DataConsDefinitionBuilder: null argument."); } this.dc = dc; this.dataTypeClassName = dataTypeClassName; this.className = CALToJavaNames.createTypeNameFromDC(dc, module); this.module = module; this.instanceName = "$instance"; this.fieldStrictness = new boolean [dc.getArity()]; if (LECCMachineConfiguration.IGNORE_STRICTNESS_ANNOTATIONS) { Arrays.fill (this.fieldStrictness, false); } else { for (int i = 0; i < dc.getArity(); ++i) { this.fieldStrictness[i] = dc.isArgStrict(i); } } this.fieldTypes = SCJavaDefn.getFieldTypesForDC(dc); for (int i = 0; i < this.fieldTypes.length; ++i) { if (SCJavaDefn.canTypeBeUnboxed(this.fieldTypes[i])) { this.dcHasPrimitives = true; } } this.javaFieldNames = new String[dc.getArity()]; for (int i = 0; i < javaFieldNames.length; ++i) { javaFieldNames[i] = SCJavaDefn.getJavaFieldNameFromDC(dc, i); } this.commonFields = commonFields; } /** * Adds debug processing: * Tracing that prints (when tracing, and all tracing options are enabled): * -the name of the executing thread * -the name of the function and the argument values, in the applicative style of CAL textual syntax. * Halting on breakpoints. * etc. * * Note that the processing takes place after the arguments that are plinged are evaluated to WHNF. Thus the tracing * does not occur at the immediate entry of the generated f function, but a bit later. This is conceptually closer * to what the meaning of plinged arguments in CAL source is, in that they are evaluated to WHNF prior to evaluating * the body of the function to WHNF. It also makes it much easier to do proper tracing in tail recursive functions for * each recursive call. * * @param argNames String[] names of the arguments to be traced, in argument order. Only the first method.getArity() names * are used. Also, argNames may actually be Java local variable or method variable names: we generate code in the * implementation below using LocalName to handle both cases. * @param argTypes JavaTypeName[] types of the traced arguments. * @return JavaStatement a java statement that will perform the debug processing */ private JavaStatement generateDebugCode (String[] argNames, JavaTypeName[] argTypes) { if (!LECCMachineConfiguration.generateDebugCode()) { throw new IllegalStateException(); } if (!LECCMachineConfiguration.generateDebugCode()) { throw new IllegalStateException(); } // Add debug processing. This includes things such as function tracing // and halting on breakpoints. //if ($ec.isDebugProcessingNeeded("Prelude.take")) { // // $ec.debugProcessing("Prelude.take", // new RTValue[]{CAL_Int.make(take$nElements$1), take$list$2}); // //} //notice that for non-RTValue fields, we need to box //$ec.isBreakpointEnabled($functionNameField) JavaExpression isDebuggingNeededCheck = new MethodInvocation.Instance(SCJavaDefn.EXECUTION_CONTEXT_VAR, "isDebugProcessingNeeded", LiteralWrapper.make(dc.getName().getQualifiedName()), JavaTypeName.STRING, JavaTypeName.BOOLEAN, MethodInvocation.InvocationType.VIRTUAL); JavaStatement.Block debuggingNeededThenBlock = new Block(); JavaStatement isDebuggingNeededIfStatement = new JavaStatement.IfThenElseStatement(isDebuggingNeededCheck, debuggingNeededThenBlock); final int arity = dc.getArity(); //new RTValue[]{CAL_Int.make(take$nElements$1), take$list$2} JavaExpression[] argValues = new JavaExpression[arity]; for (int i = 0; i < arity; ++i) { String javaArgName = argNames[i]; JavaTypeName javaArgType = argTypes[i]; JavaExpression javaArgValue = new LocalName(javaArgName, javaArgType); if (!javaArgType.equals(JavaTypeNames.RTVALUE)) { javaArgValue = SCJavaDefn.boxExpression(javaArgType, javaArgValue); } argValues[i] = javaArgValue; } JavaExpression argValueArrayCreation = new JavaExpression.ArrayCreationExpression(JavaTypeNames.RTVALUE, argValues); //$ec.debugProcessing("Prelude.take", // new RTValue[]{CAL_Int.make(take$nElements$1), take$list$2})); JavaExpression suspend = new MethodInvocation.Instance( SCJavaDefn.EXECUTION_CONTEXT_VAR, "debugProcessing", new JavaExpression[] { LiteralWrapper.make(dc.getName().getQualifiedName()), argValueArrayCreation }, new JavaTypeName[] { JavaTypeName.STRING, JavaTypeName.CAL_VALUE_ARRAY }, JavaTypeName.VOID, MethodInvocation.InvocationType.VIRTUAL); debuggingNeededThenBlock.addStatement(new ExpressionStatement(suspend)); return isDebuggingNeededIfStatement; } /** * Get the Java representation for the data constructor. * The returned class will include any inner classes. * @return JavaClassRep * @throws CodeGenerationException */ JavaClassRep generateDataConsDefinition() throws CodeGenerationException { JavaClassRep dcClass = getDataConsDefinition(); dcClass.addInnerClass(getDCFieldSelectionClass()); return dcClass; } /** * Get the Java representation for the data constructor. * The returned class will not include any inner classes. * @return JavaClassRep * @throws CodeGenerationException */ JavaClassRep getDataConsDefinition() throws CodeGenerationException { if (javaClassRep == null) { // Determine access.. int classModifiers = Modifier.FINAL | Modifier.PUBLIC | Modifier.STATIC; // No interfaces are implemented JavaTypeName[] interfaces = JavaDefinitionBuilder.EMPTY_TYPE_NAME_ARRAY; // Now instantiate the java class representation. this.javaClassRep = new JavaClassRep(className, dataTypeClassName, classModifiers, interfaces); createFields(); // There are two situations where we construct a 'data type' object: 1) fully saturated // 2) where it is the beginning of an application chain. For case 1 we need a constructor // that takes all arguments. For case 2 we need a zero argument constructor. int arity = dc.getArity(); createConstructor_noArgs(); if (arity > 0) { // We need to mark this class as containing assertions. // This is done manually because we don't always // generate the inner classes at the same time // as the containing class. javaClassRep.setContainsAssertions(); if (dcHasPrimitives) { createConstructor_allArgsUnboxed(); } else { createConstructor_allArgs(); } createMethod_f(); createMethod_fSaturatedLazy(); } createMethod_getArity(); createMethod_getOrdinalValue(); createMethod_make(); // Special case for boolean false if (dc.getName().equals(CAL_Prelude.DataConstructors.False)) { createMethod_isLogicalTrueOverrideFalse(); } for (int i = 0; i < arity; ++i) { createMethod_fieldGetter(i); } createMethod_buildDeepSeq(); // We want to create a version of get field by index which returns // a boxed version (i.e. RTValue) of each field. createMethod_getFieldByIndex(null, ""); // Now for each primitive type, for which there is a strict field, // generate a version getFieldByIndex_As_... that returns an unboxed // value. createMethod_getFieldByIndex(JavaTypeName.BOOLEAN, SCJavaDefn.getNameForPrimitive(JavaTypeName.BOOLEAN)); createMethod_getFieldByIndex(JavaTypeName.BYTE, SCJavaDefn.getNameForPrimitive(JavaTypeName.BYTE)); createMethod_getFieldByIndex(JavaTypeName.CHAR, SCJavaDefn.getNameForPrimitive(JavaTypeName.CHAR)); createMethod_getFieldByIndex(JavaTypeName.DOUBLE, SCJavaDefn.getNameForPrimitive(JavaTypeName.DOUBLE)); createMethod_getFieldByIndex(JavaTypeName.FLOAT, SCJavaDefn.getNameForPrimitive(JavaTypeName.FLOAT)); createMethod_getFieldByIndex(JavaTypeName.INT, SCJavaDefn.getNameForPrimitive(JavaTypeName.INT)); createMethod_getFieldByIndex(JavaTypeName.LONG, SCJavaDefn.getNameForPrimitive(JavaTypeName.LONG)); createMethod_getFieldByIndex(JavaTypeName.SHORT, SCJavaDefn.getNameForPrimitive(JavaTypeName.SHORT)); createMethod_getFieldByIndex(JavaTypeName.STRING, SCJavaDefn.getNameForPrimitive(JavaTypeName.STRING)); createMethod_getFieldByIndex(JavaTypeName.OBJECT, "Object"); createMethod_getModuleName(); createMethod_getUnqualifiedName(); createMethod_getQualifiedName(); createMethod_isFunctionSingleton(); createMethod_debug_getChild(); } return javaClassRep; } /** * Generate the inner class FieldSelection. This is used to represent a * field selection construct on an instance of the DC. * @return the FieldSelection class. */ private JavaClassRep getDCFieldSelectionClass () { int classModifiers = Modifier.FINAL | Modifier.PUBLIC | Modifier.STATIC; // No interfaces are implemented JavaTypeName[] interfaces = JavaDefinitionBuilder.EMPTY_TYPE_NAME_ARRAY; JavaTypeName fieldSelectionTypeName = CALToJavaNames.createFieldSelectionClassTypeNameFromDC(dc, module); // Now instantiate the java class representation. JavaClassRep fieldSelectionClass = new JavaClassRep(fieldSelectionTypeName, JavaTypeNames.RTDATACONS_FIELD_SELECTION, classModifiers, interfaces); // Add the constructor: FieldSelection (RTValue $dataConsExpr, int $fieldOrdinal, ErrorInfo $errorInfo); fieldSelectionClass.addConstructor(createDCFieldSelectionClass_constructor ()); // Add the method: private final String getFieldNameByOrdinal (int ordinal); fieldSelectionClass.addMethod(createDCFieldSelectionClass_method_getFieldNameByOrdinal()); fieldSelectionClass.addMethod (createDCFieldSelectionClass_method_getDCName()); return fieldSelectionClass; } /** * Create a constructor for the static inner FieldSelection class. * public FieldSelection (RTValue $dataConsExpr, int $fieldOrdinal, ErrorInfo $errorInfo) * @return the constructor for the FieldSelection class. */ private JavaConstructor createDCFieldSelectionClass_constructor () { JavaTypeName argTypes[] = new JavaTypeName[]{ JavaTypeNames.RTVALUE, JavaTypeName.INT, JavaTypeName.INT, JavaTypeName.ERRORINFO}; String argNames[] = new String[]{ "$dataConsExpr", "$dcOrdinal", "$fieldOrdinal", "$errorInfo"}; MethodVariable $dataConsExprVariable = new MethodVariable(argNames[0]); MethodVariable $dcOrdinalVariable = new MethodVariable(argNames[1]); MethodVariable $fieldOrdinalVariable = new MethodVariable(argNames[2]); MethodVariable $errorInfoVariable = new MethodVariable(argNames[3]); // Create the constructor. // In this case we simply pass the parameters to the superclass constructor. JavaConstructor constructor = new JavaConstructor (Modifier.PUBLIC, argNames, argTypes, "FieldSelection", new JavaExpression[]{ $dataConsExprVariable, $dcOrdinalVariable, $fieldOrdinalVariable, $errorInfoVariable}, argTypes); return constructor; } /** * Create the method String getFieldNameByOrdinal (int ordinal). * This is a method in the static inner class FieldSelection. * @return the getFieldNameByOrdinal() method. */ private JavaMethod createDCFieldSelectionClass_method_getFieldNameByOrdinal () { int modifiers = Modifier.PROTECTED | Modifier.FINAL; JavaMethod javaMethod = new JavaMethod (modifiers, JavaTypeName.STRING, "ordinal", JavaTypeName.INT, false, "getFieldNameByOrdinal"); SwitchStatement sw = new SwitchStatement (METHODVAR_ORDINAL); for (int i = 0, nFields = dc.getArity(); i < nFields; ++i) { String fieldName = dc.getNthFieldName(i).toString(); sw.addCase(new SwitchStatement.IntCaseGroup(i, new ReturnStatement(LiteralWrapper.make(fieldName)))); } javaMethod.addStatement(sw); JavaExpression exception = new JavaExpression.ClassInstanceCreationExpression(JavaTypeName.INDEX_OUT_OF_BOUNDS_EXCEPTION); JavaStatement s = new JavaStatement.ThrowStatement (exception); javaMethod.addStatement(s); return javaMethod; } private JavaMethod createDCFieldSelectionClass_method_getDCName () { int modifiers = Modifier.PROTECTED | Modifier.FINAL; JavaMethod javaMethod = new JavaMethod (modifiers, JavaTypeName.STRING, "getDCName"); javaMethod.addStatement(new ReturnStatement (JavaExpression.LiteralWrapper.make (dc.getName().getQualifiedName()))); return javaMethod; } /** * Create the fields. * private static DataConstructor dc; * private static QualifiedName qn = new QualifiedName(dcName); * private RTValue[] members = new RTValue[arity]; // arity > 0 * private static final (typeConsType) self = new ThisClass(); // arity == 0 * @throws CodeGenerationException */ private void createFields() throws CodeGenerationException { int arity = dc.getArity(); if (arity > 0) { // private RTValue field1, field2, etc. for (int i = 0; i < arity; ++i) { FieldName fn = dc.getNthFieldName(i); if (commonFields.contains(fn)) { // Common fields are implemented in base class. continue; } JavaTypeName fieldType = JavaTypeNames.RTVALUE; int modifiers = Modifier.PRIVATE; if (fieldStrictness[i]) { modifiers = modifiers | Modifier.FINAL; if (SCJavaDefn.canTypeBeUnboxed(fieldTypes[i])) { fieldType = SCJavaDefn.typeExprToTypeName(fieldTypes[i]); } } JavaFieldDeclaration fieldDec = new JavaFieldDeclaration (modifiers, fieldType, javaFieldNames[i], null); javaClassRep.addFieldDeclaration(fieldDec); } } // We want a singleton instance of the data constructor class to use as a // supercombinator. // private static final (typeConsType) self = new ThisClass(); int modifiers = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; JavaExpression selfInitializer; if (LECCMachineConfiguration.passExecContextToDataConstructors()) { selfInitializer = new ClassInstanceCreationExpression(className, JavaExpression.LiteralWrapper.NULL, JavaTypeNames.RTEXECUTION_CONTEXT); } else { selfInitializer = new ClassInstanceCreationExpression(className); } JavaFieldDeclaration selfFieldDeclaration = new JavaFieldDeclaration(modifiers, className, instanceName, selfInitializer); javaClassRep.addFieldDeclaration(selfFieldDeclaration); } /** * Create the no-argument constructor. * private ThisClass() { * } * @throws CodeGenerationException */ private void createConstructor_noArgs() throws CodeGenerationException { int modifiers = 0; boolean addEC = LECCMachineConfiguration.passExecContextToDataConstructors(); JavaConstructor javaConstructor; if (addEC) { modifiers |= Modifier.PUBLIC; javaConstructor = new JavaConstructor(modifiers, new String[]{SCJavaDefn.EXECUTION_CONTEXT_NAME}, new JavaTypeName[]{JavaTypeNames.RTEXECUTION_CONTEXT}, CALToJavaNames.createInnerClassNameFromDC(dc, module)); } else { modifiers |= Modifier.PRIVATE; javaConstructor = new JavaConstructor(modifiers, CALToJavaNames.createInnerClassNameFromDC(dc, module)); } // Add the method to the class. javaClassRep.addConstructor(javaConstructor); // Initialize any final fields. for (int i = 0; i < dc.getArity(); ++i) { if (!commonFields.contains(dc.getNthFieldName(i)) && fieldStrictness[i]) { JavaTypeName fieldType = JavaTypeNames.RTVALUE; if (SCJavaDefn.canTypeBeUnboxed(fieldTypes[i])) { fieldType = SCJavaDefn.typeExprToTypeName(fieldTypes[i]); } LiteralWrapper fieldVal = JavaDefinitionBuilder.getDefaultValueForType(fieldType); JavaField jf = new JavaField.Instance(null, javaFieldNames[i], fieldType); JavaExpression assign = new JavaExpression.Assignment(jf, fieldVal); javaConstructor.addStatement(new ExpressionStatement(assign)); } } } /** * Create the all-argument constructor. * private ThisClass(RTValue member0, RTValue member1, ...) { * this.members[0] = member0; * this.members[1] = member1; * ... * }; */ private void createConstructor_allArgs() { int modifiers = Modifier.PUBLIC; int nArgs = dc.getArity(); boolean addEC = LECCMachineConfiguration.passExecContextToDataConstructors(); if (addEC) { nArgs++; } // Get arg types - all RTValues. JavaTypeName[] argTypes = new JavaTypeName[nArgs]; Arrays.fill(argTypes, JavaTypeNames.RTVALUE); // Figure out arg names and create method variables. String[] argNames = new String[nArgs]; MethodVariable[] argVars = new MethodVariable[nArgs]; for (int i = 0, n = argNames.length; i < n; i++) { argNames[i] = "member" + i; argVars[i] = new MethodVariable(argNames[i]); } if (addEC) { argTypes[argTypes.length - 1] = JavaTypeNames.RTEXECUTION_CONTEXT; argNames[argNames.length - 1] = SCJavaDefn.EXECUTION_CONTEXT_NAME; } // Build up the arguments to the superclass constructor. JavaExpression superClassConstructorArgValues[] = new JavaExpression [commonFields.size()]; JavaTypeName superClassConstructorArgTypes[] = new JavaTypeName [commonFields.size()]; int j = 0; for (final FieldName fn : commonFields) { int fieldIndex = dc.getFieldIndex(fn); superClassConstructorArgValues[j] = argVars[fieldIndex]; superClassConstructorArgTypes[j] = argTypes[fieldIndex]; j++; } // Add the method to the class. JavaConstructor javaConstructor = new JavaConstructor(modifiers, argNames, argTypes, CALToJavaNames.createInnerClassNameFromDC(dc, module), superClassConstructorArgValues, superClassConstructorArgTypes); javaClassRep.addConstructor(javaConstructor); if (LECCMachineConfiguration.generateDebugCode()) { javaConstructor.addStatement(generateDebugCode(argNames, argTypes)); } // Assert the arguments are not null. JavaExpression check = new JavaExpression.OperatorExpression.Binary(JavaOperator.NOT_EQUALS_OBJECT, argVars[0], LiteralWrapper.NULL); for (int i = 1, n = dc.getArity(); i < n; ++i) { JavaExpression nextCheck = new JavaExpression.OperatorExpression.Binary(JavaOperator.NOT_EQUALS_OBJECT, argVars[i], LiteralWrapper.NULL); check = new OperatorExpression.Binary(JavaOperator.CONDITIONAL_AND, check, nextCheck); } javaConstructor.addStatement(new AssertStatement(check, LiteralWrapper.make("Invalid constructor argument for " + dc.getName().getQualifiedName()), JavaTypeName.STRING)); // Add the body.. // We want to assign any fields in this class. for (int i = 0; i < dc.getArity(); i++) { if (commonFields.contains(dc.getNthFieldName(i))) { // This field is in the containing class so we don't // assign here. continue; } JavaField field = new JavaField.Instance(null, javaFieldNames[i], JavaTypeNames.RTVALUE); Assignment memberAssignment = new Assignment(field, argVars[i]); javaConstructor.addStatement(new ExpressionStatement(memberAssignment)); } // Add the statistics block. addStatsBlock(javaConstructor); } private void createConstructor_allArgsUnboxed() throws CodeGenerationException { int modifiers = Modifier.PUBLIC; int nArgs = dc.getArity(); boolean addEC = LECCMachineConfiguration.passExecContextToDataConstructors(); if (addEC) { nArgs++; } // Get arg types JavaTypeName[] argTypes = new JavaTypeName[nArgs]; for (int i = 0; i < dc.getArity(); ++i) { if (fieldStrictness[i] && SCJavaDefn.canTypeBeUnboxed(fieldTypes[i])) { argTypes[i] = SCJavaDefn.typeExprToTypeName(fieldTypes[i]); } else { argTypes[i] = JavaTypeNames.RTVALUE; } } // Figure out arg names and create method vars. String[] argNames = new String[nArgs]; MethodVariable[] argVars = new MethodVariable[nArgs]; for (int i = 0; i < argNames.length; i++) { argNames[i] = "member" + i; argVars[i] = new MethodVariable(argNames[i]); } if (addEC) { argTypes[argTypes.length - 1] = JavaTypeNames.RTEXECUTION_CONTEXT; argNames[argNames.length - 1] = SCJavaDefn.EXECUTION_CONTEXT_NAME; } // Build up the arguments to the superclass constructor. JavaExpression superClassConstructorArgValues[] = new JavaExpression [commonFields.size()]; JavaTypeName superClassConstructorArgTypes[] = new JavaTypeName [commonFields.size()]; int j = 0; for (final FieldName fn : commonFields) { int fieldIndex = dc.getFieldIndex(fn); superClassConstructorArgValues[j] = argVars[fieldIndex]; superClassConstructorArgTypes[j] = argTypes[fieldIndex]; j++; } // Add the method to the class. JavaConstructor javaConstructor = new JavaConstructor(modifiers, argNames, argTypes, CALToJavaNames.createInnerClassNameFromDC(dc, module), superClassConstructorArgValues, superClassConstructorArgTypes); javaClassRep.addConstructor(javaConstructor); if (LECCMachineConfiguration.generateDebugCode()) { javaConstructor.addStatement(generateDebugCode(argNames, argTypes)); } // Assert that object arguments are non-null. JavaExpression check = null; for (int i = 0, n = dc.getArity(); i < n; ++i) { // We only check fields of type RTValue. It is valid to have an unboxed object value of null. if (argTypes[i].equals(JavaTypeNames.RTVALUE)) { JavaExpression newCheck = new OperatorExpression.Binary(JavaOperator.NOT_EQUALS_OBJECT, argVars[i], LiteralWrapper.NULL); if (check == null) { check = newCheck; } else { check = new OperatorExpression.Binary(JavaOperator.CONDITIONAL_AND, check, newCheck); } } } if (check != null) { javaConstructor.addStatement(new AssertStatement(check, LiteralWrapper.make("Invalid constructor argument for " + dc.getName().getQualifiedName()), JavaTypeName.STRING)); } // Add the body.. // We want to assign any of the fields in this class. for (int i = 0, n = dc.getArity(); i < n; i++) { if (commonFields.contains(dc.getNthFieldName(i))) { // This field is in the containing class so we don't // assign here. continue; } JavaTypeName fieldType = JavaTypeNames.RTVALUE; if (fieldStrictness[i] && SCJavaDefn.canTypeBeUnboxed(fieldTypes[i])) { fieldType = SCJavaDefn.typeExprToTypeName(fieldTypes[i]); } JavaField field = new JavaField.Instance(null, javaFieldNames[i], fieldType); Assignment memberAssignment = new Assignment(field, argVars[i]); javaConstructor.addStatement(new ExpressionStatement(memberAssignment)); } // Add the statistics block. addStatsBlock(javaConstructor); } /** * Create the getArity() method. * public final int getArity(); */ private void createMethod_getArity() { int modifiers = Modifier.PUBLIC | Modifier.FINAL; JavaTypeName returnType = JavaTypeName.INT; // Add the method to the class. JavaMethod javaMethod = new JavaMethod(modifiers, returnType, "getArity"); javaClassRep.addMethod(javaMethod); // Add the body.. javaMethod.addStatement(new ReturnStatement(LiteralWrapper.make(Integer.valueOf(dc.getArity())))); //LiteralWrapper zeroWrapper = LiteralWrapper.make(JavaPrimitives.makeInteger(0)); // if (arity == 0) { // return 0; // javaMethod.addStatement(new ReturnStatement(zeroWrapper)); // } else { // // return (members[0] == null) ? arity : 0; // JavaField field = new JavaField("field0", JavaTypeNames.RTVALUE); // JavaExpression equalityTest = new OperatorExpression.Binary(JavaOperator.EQUALS_OBJECT, field, LiteralWrapper.NULL); // JavaExpression ternaryExpression = new OperatorExpression.Ternary(equalityTest, LiteralWrapper.make(JavaPrimitives.makeInteger(arity)), zeroWrapper); // javaMethod.addStatement(new ReturnStatement(ternaryExpression)); // } } /** * Create the getOrdinalValue() method. * This method returns the ordinal identifier of this data constructor. * public int getOrdinalValue() {return (ordinal);} */ private void createMethod_getOrdinalValue() { int modifiers = Modifier.PUBLIC; JavaTypeName returnType = JavaTypeName.INT; // Add the method to the class. JavaMethod javaMethod = new JavaMethod(modifiers, returnType, "getOrdinalValue"); javaClassRep.addMethod(javaMethod); // Add the body.. // return (ordinal); javaMethod.addStatement(new ReturnStatement(LiteralWrapper.make(Integer.valueOf(dc.getOrdinal())))); } /** * Create the no-argument make() method * public static final RTFunction make(); */ private void createMethod_make() { int modifiers = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; JavaTypeName returnType = className; // Add the method to the class. JavaMethod javaMethod = new JavaMethod(modifiers, returnType, "make"); javaClassRep.addMethod(javaMethod); // Add the body.. // The no argument version of make should only be called for an instance to // be used as a supercombinator so we can return the singleton instance. JavaField selfField = new JavaField.Static(className, instanceName, className); javaMethod.addStatement(new ReturnStatement(selfField)); } /** * Create the f() method. * public final RTValue f(RTResultFunction $rootNode) throws org.openquark.cal.runtime.CALExecutor.CALExecutorException; * @throws CodeGenerationException */ private void createMethod_f() throws CodeGenerationException { final int modifiers = Modifier.PUBLIC | Modifier.FINAL; final JavaTypeName returnType = JavaTypeNames.RTVALUE; // Add the method to the class. final JavaMethod javaMethod = new JavaMethod( modifiers, returnType, new String [] {ROOT_NODE, SCJavaDefn.EXECUTION_CONTEXT_NAME}, new JavaTypeName[]{JavaTypeNames.RTRESULT_FUNCTION, JavaTypeNames.RTEXECUTION_CONTEXT}, new boolean[] {true, true}, "f"); javaClassRep.addMethod(javaMethod); // Add the throws declaration javaMethod.addThrows(JavaTypeName.CAL_EXECUTOR_EXCEPTION); if (LECCMachineConfiguration.generateCallCounts()) { JavaExpression args[] = new JavaExpression[2]; JavaTypeName argTypes[] = new JavaTypeName[2]; args[0] = LiteralWrapper.make(dc.getName().getModuleName().toSourceText()); args[1] = LiteralWrapper.make(dc.getName().getUnqualifiedName()); argTypes[0] = argTypes[1] = JavaTypeName.STRING; MethodInvocation mi = new MethodInvocation.Instance(SCJavaDefn.EXECUTION_CONTEXT_VAR, "dcFunctionCalled", args, argTypes, JavaTypeName.VOID, MethodInvocation.InvocationType.VIRTUAL); javaMethod.addStatement(new ExpressionStatement(mi)); } // Add the body.. final int arity = dc.getArity(); if (arity > 0) { // // Arguments javaMethod.addStatement(new LineComment("Arguments")); final boolean addEC = LECCMachineConfiguration.passExecContextToDataConstructors(); final int nConstructorArgs = addEC ? (arity + 1) : arity; // Get arg names final JavaExpression[] arguments = new JavaExpression[nConstructorArgs]; for (int i = 0; i < arity; i++) { final String localVarName = "$arg" + i; arguments[i] = new LocalVariable(localVarName, JavaTypeNames.RTVALUE); } // final RTValue $arg_i = initializer; for (int i = arity - 1; i >= 0; i--) { final JavaExpression initializer; if (i == arity - 1) { //$rootNode.getArgValue(); initializer = SCJavaDefn.createInvocation(METHODVAR_ROOT_NODE, SCJavaDefn.GETARGVALUE); } else if (i == arity - 2) { if (arity > 2) { //RTValue $currentRootNode; javaMethod.addStatement(new LocalVariableDeclaration(LOCALVAR_CURRENT_ROOT_NODE)); //($currentRootNode = $rootNode.prevArg()).getArgValue(); initializer = SCJavaDefn.createInvocation( new Assignment(LOCALVAR_CURRENT_ROOT_NODE, SCJavaDefn.createInvocation(METHODVAR_ROOT_NODE, SCJavaDefn.PREVARG)), SCJavaDefn.GETARGVALUE); } else { //$rootNode.prevArg().getArgValue(); initializer = SCJavaDefn.createInvocation( SCJavaDefn.createInvocation(METHODVAR_ROOT_NODE, SCJavaDefn.PREVARG), SCJavaDefn.GETARGVALUE); } } else if (i == 0) { //$currentRootNode.prevArg().getArgValue(); initializer = SCJavaDefn.createInvocation( SCJavaDefn.createInvocation(LOCALVAR_CURRENT_ROOT_NODE, SCJavaDefn.PREVARG), SCJavaDefn.GETARGVALUE); } else { //($currentRootNode = $currentRootNode.prevArg()).getArgValue(); initializer = SCJavaDefn.createInvocation( new Assignment(LOCALVAR_CURRENT_ROOT_NODE, SCJavaDefn.createInvocation(LOCALVAR_CURRENT_ROOT_NODE, SCJavaDefn.PREVARG)), SCJavaDefn.GETARGVALUE); } final LocalVariableDeclaration argDeclaration = new LocalVariableDeclaration((LocalVariable)arguments[i], initializer, !fieldStrictness[i]); javaMethod.addStatement(argDeclaration); } // Get arg types - all RTValues. final JavaTypeName[] argTypes = new JavaTypeName[nConstructorArgs]; Arrays.fill(argTypes, JavaTypeNames.RTVALUE); if (addEC) { arguments[arguments.length - 1] = SCJavaDefn.EXECUTION_CONTEXT_VAR; argTypes[argTypes.length - 1] = JavaTypeNames.RTEXECUTION_CONTEXT; } for (int i = 0; i < dc.getArity(); ++i) { if (fieldStrictness[i]) { arguments[i]= new MethodInvocation.Instance (arguments[i], "evaluate", SCJavaDefn.EXECUTION_CONTEXT_VAR, JavaTypeNames.RTEXECUTION_CONTEXT, JavaTypeNames.RTVALUE, MethodInvocation.InvocationType.VIRTUAL); if (SCJavaDefn.canTypeBeUnboxed(fieldTypes[i])) { argTypes[i] = SCJavaDefn.typeExprToTypeName(fieldTypes[i]); arguments[i] = SCJavaDefn.unboxValue(SCJavaDefn.typeExprToTypeName(fieldTypes[i]), arguments[i]); } } } // return new ThisClass($arg0, $arg1, ...); final JavaExpression cice = new ClassInstanceCreationExpression(className, arguments, argTypes); javaMethod.addStatement(new ReturnStatement(cice)); } else { // return new ThisClass(); final JavaExpression cice = new ClassInstanceCreationExpression(className, JavaExpression.LiteralWrapper.NULL, JavaTypeNames.RTEXECUTION_CONTEXT); javaMethod.addStatement(new ReturnStatement(cice)); } } /** * Create the fnL() method. * @throws CodeGenerationException */ private void createMethod_fSaturatedLazy() throws CodeGenerationException { final int modifiers = Modifier.PUBLIC | Modifier.FINAL; final JavaTypeName returnType = JavaTypeNames.RTVALUE; final int nArgs = dc.getArity() + 1; // Get arg types - all RTValues. final JavaTypeName[] argTypes = new JavaTypeName[nArgs]; Arrays.fill(argTypes, JavaTypeNames.RTVALUE); // Figure out arg names. final String[] argNames = new String[nArgs]; for (int i = 0; i < argNames.length; i++) { argNames[i] = "member" + i; } argTypes[argTypes.length - 1] = JavaTypeNames.RTEXECUTION_CONTEXT; argNames[argNames.length - 1] = SCJavaDefn.EXECUTION_CONTEXT_NAME; // Add the method to the class. JavaMethod javaMethod = new JavaMethod(modifiers, returnType, argNames, argTypes, null, "f" + dc.getArity() + "L"); javaClassRep.addMethod(javaMethod); // Add the throws declaration javaMethod.addThrows(JavaTypeName.CAL_EXECUTOR_EXCEPTION); if (LECCMachineConfiguration.generateCallCounts()) { JavaExpression args[] = new JavaExpression[2]; JavaTypeName ccArgTypes[] = new JavaTypeName[2]; args[0] = LiteralWrapper.make(dc.getName().getModuleName().toSourceText()); args[1] = LiteralWrapper.make(dc.getName().getUnqualifiedName()); ccArgTypes[0] = ccArgTypes[1] = JavaTypeName.STRING; MethodInvocation mi = new MethodInvocation.Instance(SCJavaDefn.EXECUTION_CONTEXT_VAR, "dcFunctionCalled", args, ccArgTypes, JavaTypeName.VOID, MethodInvocation.InvocationType.VIRTUAL); javaMethod.addStatement(new ExpressionStatement(mi)); } boolean addEC = LECCMachineConfiguration.passExecContextToDataConstructors(); int nConstructorArgs = dc.getArity(); if (addEC) { nConstructorArgs++; } JavaExpression constructorArgs[] = new JavaExpression [nConstructorArgs]; JavaTypeName constructorArgTypes[] = new JavaTypeName [nConstructorArgs]; System.arraycopy(argTypes, 0, constructorArgTypes, 0, constructorArgTypes.length); if (addEC) { constructorArgs[constructorArgs.length - 1] = SCJavaDefn.EXECUTION_CONTEXT_VAR; } for (int i = 0; i < dc.getArity(); ++i) { constructorArgs[i] = new MethodVariable("member" + i); if (fieldStrictness[i]) { constructorArgs[i] = new MethodInvocation.Instance (constructorArgs[i], "evaluate", SCJavaDefn.EXECUTION_CONTEXT_VAR, JavaTypeNames.RTEXECUTION_CONTEXT, JavaTypeNames.RTVALUE, MethodInvocation.InvocationType.VIRTUAL); if (SCJavaDefn.canTypeBeUnboxed(fieldTypes[i])) { constructorArgs[i] = SCJavaDefn.unboxValue(SCJavaDefn.typeExprToTypeName(fieldTypes[i]), constructorArgs[i]); constructorArgTypes[i] = SCJavaDefn.typeExprToTypeName(fieldTypes[i]); } } } JavaExpression cc = new ClassInstanceCreationExpression (className, constructorArgs, constructorArgTypes); javaMethod.addStatement(new ReturnStatement(cc)); } /** * Create the isLogicalTrue() override method for the Prelude.False data constructor. * public boolean isLogicalTrue() {return false;} */ private void createMethod_isLogicalTrueOverrideFalse() { int modifiers = Modifier.PUBLIC; JavaTypeName returnType = JavaTypeName.BOOLEAN; // Add the method to the class. JavaMethod javaMethod = new JavaMethod(modifiers, returnType, "isLogicalTrue"); javaClassRep.addMethod(javaMethod); // Add the body.. // return false; javaMethod.addStatement(new ReturnStatement(LiteralWrapper.FALSE)); } /** * Create the getFieldn() method. * public final RTValue getFieldn() * @param i * @throws CodeGenerationException */ private void createMethod_fieldGetter(int i) throws CodeGenerationException { if (commonFields.contains(dc.getNthFieldName(i))) { return; } int modifiers = Modifier.PUBLIC | Modifier.FINAL; JavaTypeName returnType = JavaTypeNames.RTVALUE; // Add the method to the class. String methodName = "get" + javaFieldNames[i]; JavaMethod javaMethod = new JavaMethod(modifiers, returnType, methodName); javaClassRep.addMethod(javaMethod); // return a boxed version of the field. JavaExpression jf; if (fieldStrictness[i] && SCJavaDefn.canTypeBeUnboxed(fieldTypes[i])) { jf = new JavaField.Instance (null, javaFieldNames[i], SCJavaDefn.typeExprToTypeName(fieldTypes[i])); jf = SCJavaDefn.boxExpression(fieldTypes[i], jf); javaMethod.addStatement(new ReturnStatement(jf)); } else { JavaField field = new JavaField.Instance (null, javaFieldNames[i], JavaTypeNames.RTVALUE); jf = field; if (!fieldStrictness[i]) { // We have a non-strict field of type RTValue. In order to reduce space usage // we want to update the field value to be the result of the original suspension. // If the field value is an RTResultFunction we want to re-assign the field with // a call to 'getValue()'. // For example: //public final RTValue get_head() { // RTValue field; // if ((field = _head) instanceof RTResultFunction) { // return (_head = field.getValue()); // } // return field; //} String localName = javaFieldNames[i]+"$"; LocalVariable localVariable = new LocalVariable(localName, JavaTypeNames.RTVALUE); LocalVariableDeclaration localDeclaration = new LocalVariableDeclaration(localVariable); javaMethod.addStatement(localDeclaration); Assignment localAssignment = new Assignment(localVariable, field); InstanceOf checkType = new InstanceOf(localAssignment, JavaTypeNames.RTRESULT_FUNCTION); MethodInvocation getValue = new MethodInvocation.Instance(localVariable, "getValue", JavaTypeNames.RTVALUE, MethodInvocation.InvocationType.VIRTUAL); Assignment fieldAssignment = new Assignment(field, getValue); ReturnStatement returnNewVal = new ReturnStatement(fieldAssignment); IfThenElseStatement ifThen = new IfThenElseStatement(checkType, returnNewVal); javaMethod.addStatement(ifThen); javaMethod.addStatement(new ReturnStatement (localVariable)); } else { // The field is strict and therefore already in WHNF. javaMethod.addStatement(new ReturnStatement(jf)); } } // If the field type is primitive and strict we want to generate // a method that retrieves the field as an unboxed value. if (SCJavaDefn.canTypeBeUnboxed(fieldTypes[i]) && dc.isArgStrict((i))) { returnType = SCJavaDefn.typeExprToTypeName(fieldTypes[i]); methodName = methodName + "_As_" + SCJavaDefn.getNameForPrimitive(returnType); // If all the fields with this name/type combination across all DCs are strict we // don't need an RTExecutionContext passed to the getter. javaMethod = new JavaMethod (modifiers, returnType, methodName); javaClassRep.addMethod(javaMethod); // Add the throws declaration javaMethod.addThrows(JavaTypeName.CAL_EXECUTOR_EXCEPTION); if (fieldStrictness[i]) { jf = new JavaField.Instance (null, javaFieldNames[i], returnType); } else { // This is an error. We should only get to this point if all // instances of this field/type are strict across all DCs. throw new CodeGenerationException ("Attempt to generate unboxed accessor on lazy field " + javaFieldNames[i] + " for DC " + dc.getName()); } javaMethod.addStatement(new ReturnStatement(jf)); } } /** * Generate a method: RTValue getFieldByIndex(int fieldIndex, ErrorInfo errorInfo). * This method retrieves a field by index with a check that the object has the expected ordinal. * It is primarily used by data constructor field selection outside of a case expression. * @param forType - unboxed type to return. May be null. * @param nameForType - the string name of the type to retrieve * @throws CodeGenerationException */ private void createMethod_getFieldByIndex (JavaTypeName forType, String nameForType) throws CodeGenerationException { if (javaFieldNames.length == 0) { return; } Block methodBodyBlock = new Block(); int nReturnedFields = 0; // Check that this is an instance of the expected data constructor. MethodInvocation checkDC = new MethodInvocation.Instance( null, "checkDCOrdinalForFieldSelection", new JavaExpression[]{new MethodVariable("dcOrdinal"), new MethodVariable("errorInfo")}, new JavaTypeName[]{JavaTypeName.INT, JavaTypeName.ERRORINFO}, JavaTypeName.VOID, MethodInvocation.InvocationType.VIRTUAL); methodBodyBlock.addStatement(new ExpressionStatement(checkDC)); SwitchStatement sw = new SwitchStatement(METHODVAR_FIELDINDEX); for (int i = 0; i < javaFieldNames.length; ++i) { if (forType != null) { // If field is not strict or not primitive can't return unboxed form. if (!fieldStrictness[i] || !SCJavaDefn.canTypeBeUnboxed(fieldTypes[i])) { continue; } JavaTypeName ftn = SCJavaDefn.typeExprToTypeName(fieldTypes[i]); if (!forType.equals(ftn)) { // Check to see if we're doing return type 'Object' on a foreign type. if (!forType.equals(JavaTypeName.OBJECT) || ftn instanceof JavaTypeName.Primitive) { continue; } } } JavaExpression jf; if (fieldStrictness[i] && SCJavaDefn.canTypeBeUnboxed(fieldTypes[i])) { jf = new JavaField.Instance (null, javaFieldNames[i], SCJavaDefn.typeExprToTypeName(fieldTypes[i])); if (forType == null) { jf = SCJavaDefn.boxExpression(fieldTypes[i], jf); } } else { // We have a non-strict field of type RTValue. In order to reduce space usage // we want to update the field value to be the result of the original suspension. // We do this by using the field accessor function get_fieldName(). jf = new MethodInvocation.Instance(null, "get" + javaFieldNames[i], JavaTypeNames.RTVALUE, MethodInvocation.InvocationType.VIRTUAL); if (forType != null) { jf = SCJavaDefn.unboxValue(forType, jf); } } SwitchStatement.IntCaseGroup iCase = new SwitchStatement.IntCaseGroup(i, new ReturnStatement (jf)); sw.addCase(iCase); nReturnedFields++; } methodBodyBlock.addStatement (sw); MethodInvocation error = new MethodInvocation.Instance( null, "badFieldIndexInGetFieldByIndex", METHODVAR_FIELDINDEX, JavaTypeName.INT, JavaTypeName.VOID, MethodInvocation.InvocationType.VIRTUAL); methodBodyBlock.addStatement(new ExpressionStatement(error)); methodBodyBlock.addStatement (makeDefaultValReturnStatement(forType)); if (nReturnedFields > 0) { int modifiers = Modifier.PUBLIC | Modifier.FINAL; String methodName = "getFieldByIndex"; if (forType != null) { methodName = methodName + "_As_" + nameForType; } JavaMethod javaMethod = new JavaMethod (modifiers, (forType == null) ? JavaTypeNames.RTVALUE : forType, new String[]{"dcOrdinal", "fieldIndex", "errorInfo"}, new JavaTypeName[]{JavaTypeName.INT, JavaTypeName.INT, JavaTypeName.ERRORINFO}, null, methodName); javaMethod.addThrows(JavaTypeName.CAL_EXECUTOR_EXCEPTION); javaClassRep.addMethod (javaMethod); javaMethod.addStatement(methodBodyBlock); } } private final ReturnStatement makeDefaultValReturnStatement (JavaTypeName forType) { return new ReturnStatement(JavaDefinitionBuilder.getDefaultValueForType(forType)); } /** * Create the getModuleName() method. * public final String getModuleName() { * return "ModuleName"; * } * */ private void createMethod_getModuleName () { JavaMethod javaMethod = new JavaMethod (Modifier.PUBLIC | Modifier.FINAL, JavaTypeName.STRING, "getModuleName"); javaClassRep.addMethod (javaMethod); javaMethod.addStatement (new ReturnStatement (LiteralWrapper.make (dc.getName().getModuleName().toSourceText()))); } /** * Create the getUnqualifiedName() method. * public final String getModuleName() { * return "unqualifiedName"; * } * */ private void createMethod_getUnqualifiedName () { JavaMethod javaMethod = new JavaMethod (Modifier.PUBLIC | Modifier.FINAL, JavaTypeName.STRING, "getUnqualifiedName"); javaClassRep.addMethod (javaMethod); javaMethod.addStatement (new ReturnStatement (LiteralWrapper.make (dc.getName().getUnqualifiedName()))); } /** * Create the getQualifiedName() method. * public final String getQualifiedName () { * return "Module.unqualifiedName"; * } */ private final void createMethod_getQualifiedName () { JavaMethod jm = new JavaMethod (Modifier.PUBLIC | Modifier.FINAL, JavaTypeName.STRING, "getQualifiedName"); javaClassRep.addMethod(jm); jm.addStatement(new ReturnStatement(LiteralWrapper.make(dc.getName().getQualifiedName()))); } /** * generate the code implementing the method * boolean RTCons.isFunctionSingleton() * for non zero-arity data constructors. */ private void createMethod_isFunctionSingleton() { final int dcArity = dc.getArity(); if (dcArity == 0) { //0-arity data constructors are handled by the base case implementation of //RTCons.isFunctionSingleton() which returns true. return; } //the generated code is: //public final boolean isFunctionSingleton() { // return this == $instance; //} JavaMethod javaMethod = new JavaMethod (Modifier.PUBLIC | Modifier.FINAL, JavaTypeName.BOOLEAN, "isFunctionSingleton"); javaClassRep.addMethod (javaMethod); JavaExpression conditionExpr = new OperatorExpression.Binary( JavaOperator.EQUALS_OBJECT, new JavaExpression.JavaField.This(className), new JavaExpression.JavaField.Static(className, "$instance", className)); JavaStatement returnStatement = new JavaStatement.ReturnStatement(conditionExpr); javaMethod.addStatement(returnStatement); } /** * Generates a method similar to getFieldByIndex, except no side effects on the CalValue are permitted. * @throws CodeGenerationException */ private void createMethod_debug_getChild() throws CodeGenerationException { final int dcArity = dc.getArity(); if (dcArity == 0) { //0-arity data constructors are handled by RTCons.debug_getChild() return; } //For example, for Prelude.Cons this generates: //public final CalValue getChild(int childN) { // if (isFunctionSingleton()) { // throw new IndexOutOfBoundsException(); // } // switch (childN) { // case 0: // return _head; // case 1: // return _tail: // default: // throw new IndexOfOfBoundsException(); // } //} //for strict primitive fields, we generate stuff like "return CAL_Int.make(field)" instead. JavaMethod method = new JavaMethod (Modifier.PUBLIC | Modifier.FINAL, JavaTypeName.CAL_VALUE, "childN", JavaTypeName.INT, false, "debug_getChild"); javaClassRep.addMethod (method); MethodVariable childNVar = new JavaExpression.MethodVariable("childN"); { // if (isFunctionSingleton()) { // throw new IndexOutOfBoundsException(); // } JavaExpression conditionExpr = new MethodInvocation.Instance(null, "isFunctionSingleton", JavaTypeName.BOOLEAN, MethodInvocation.InvocationType.VIRTUAL); JavaStatement thenStatement = new JavaStatement.ThrowStatement( new JavaExpression.ClassInstanceCreationExpression(JavaTypeName.INDEX_OUT_OF_BOUNDS_EXCEPTION)); JavaStatement.IfThenElseStatement ifThenStatement = new JavaStatement.IfThenElseStatement (conditionExpr, thenStatement); method.addStatement(ifThenStatement); } SwitchStatement switchStatement = new SwitchStatement(childNVar); for (int i = 0; i < dcArity; ++i) { TypeExpr calFieldType = fieldTypes[i]; String javaFieldName = javaFieldNames[i]; JavaExpression javaFieldExpr; if (fieldStrictness[i] && SCJavaDefn.canTypeBeUnboxed(calFieldType)) { JavaTypeName javaFieldType = SCJavaDefn.typeExprToTypeName(calFieldType); javaFieldExpr = new JavaExpression.JavaField.Instance(null, javaFieldName, javaFieldType); javaFieldExpr = SCJavaDefn.boxExpression(javaFieldType, javaFieldExpr); } else { javaFieldExpr = new JavaExpression.JavaField.Instance(null, javaFieldName, JavaTypeNames.RTVALUE); } switchStatement.addCase( new SwitchStatement.IntCaseGroup(i, new ReturnStatement(javaFieldExpr))); } switchStatement.addCase( new SwitchStatement.DefaultCase( new JavaStatement.ThrowStatement( new JavaExpression.ClassInstanceCreationExpression(JavaTypeName.INDEX_OUT_OF_BOUNDS_EXCEPTION)))); method.addStatement(switchStatement); } /** * Create an override of the method RTValue.buildDeepSeq(). * This method will apply deepSeq to each member field. * @throws CodeGenerationException */ private void createMethod_buildDeepSeq() throws CodeGenerationException { if (dc.getArity() == 0) { return; } // public RTValue buildDeepSeq(RTSupercombinator deepSeq, RTValue rhs) throws CALExecutorException { int modifiers = Modifier.PUBLIC | Modifier.FINAL; // Add the method to the class. JavaMethod javaMethod = new JavaMethod(modifiers, JavaTypeNames.RTVALUE, new String[]{"deepSeq", "rhs"}, new JavaTypeName[]{JavaTypeNames.RTSUPERCOMBINATOR, JavaTypeNames.RTVALUE}, null, "buildDeepSeq"); javaClassRep.addMethod(javaMethod); // Add the throws declaration javaMethod.addThrows(JavaTypeName.CAL_EXECUTOR_EXCEPTION); // Add the method body. // Evaluate each child. JavaExpression rhs = METHODVAR_RHS; for (int i = dc.getArity() - 1; i >= 0; --i) { if (fieldStrictness[i] && !SCJavaDefn.typeExprToTypeName(fieldTypes[i]).equals(JavaTypeNames.RTVALUE)) { // Strict primitive/foreign types don't need to have anything done. continue; } // We want to build up an expression like: // deepSeq(field1 (deepSeq field2 (deepSeq field3 rhs))) // However, for non-strict fields we want to access them via the get_field accessor // to ensure the compacting of any indirection chains. JavaExpression deepSeqArgs[] = new JavaExpression [2]; if (fieldStrictness[i]) { deepSeqArgs[0] = new JavaField.Instance(null, javaFieldNames[i], JavaTypeNames.RTVALUE); } else { deepSeqArgs[0] = new MethodInvocation.Instance(null, "get"+javaFieldNames[i], JavaTypeNames.RTVALUE, MethodInvocation.InvocationType.VIRTUAL); } deepSeqArgs[1] = rhs; rhs = SCJavaDefn.createApplyInvocation(METHODVAR_DEEPSEQ, deepSeqArgs); } javaMethod.addStatement(new ReturnStatement(rhs)); } /** * If generation of statistics is turned on this method * adds the appropriate code to augment the statistics. * @param javaCons */ private void addStatsBlock(JavaConstructor javaCons) { if (LECCMachineConfiguration.generateStatistics()) { MethodInvocation mi = new MethodInvocation.Instance(SCJavaDefn.EXECUTION_CONTEXT_VAR, "incrementNDataTypeInstances", JavaTypeName.VOID, MethodInvocation.InvocationType.VIRTUAL); javaCons.addStatement(new ExpressionStatement(mi)); } if (LECCMachineConfiguration.generateCallCounts()) { JavaExpression args[] = new JavaExpression[2]; JavaTypeName argTypes[] = new JavaTypeName[2]; args[0] = LiteralWrapper.make(dc.getName().getModuleName().toSourceText()); args[1] = LiteralWrapper.make(dc.getName().getUnqualifiedName()); argTypes[0] = argTypes[1] = JavaTypeName.STRING; MethodInvocation mi = new MethodInvocation.Instance(SCJavaDefn.EXECUTION_CONTEXT_VAR, "dcConstructorCalled", args, argTypes, JavaTypeName.VOID, MethodInvocation.InvocationType.VIRTUAL); javaCons.addStatement(new ExpressionStatement(mi)); } } } /** * A helper class used to bundle a field name, TypeExpr and the strictness of all fields with * that name/type. */ private static final class FieldTypeAndStrictness { final FieldName fieldName; final String javaFieldName; final TypeExpr type; int strictness; // 1 - all strict, 0 - all lazy, -1 - mixed FieldTypeAndStrictness (FieldName fieldName, String javaFieldName, TypeExpr type, int strictness) { this.fieldName = fieldName; this.javaFieldName = javaFieldName; this.type = type; this.strictness = strictness; } } }