package me.tomassetti.turin.compiler; import com.google.common.collect.ImmutableList; import me.tomassetti.bytecode_generation.*; import me.tomassetti.bytecode_generation.pushop.PushLocalVar; import me.tomassetti.bytecode_generation.pushop.PushStaticField; import me.tomassetti.bytecode_generation.pushop.PushThis; import me.tomassetti.bytecode_generation.returnop.ReturnValueBS; import me.tomassetti.jvm.*; import me.tomassetti.bytecode_generation.returnop.ReturnVoidBS; import me.tomassetti.turin.classloading.ClassFileDefinition; import me.tomassetti.turin.compiler.errorhandling.ErrorCollector; import me.tomassetti.turin.parser.analysis.Property; import me.tomassetti.turin.parser.ast.context.ContextDefinitionNode; import me.tomassetti.turin.resolvers.ResolverRegistry; import me.tomassetti.turin.resolvers.SymbolResolver; import me.tomassetti.turin.parser.ast.*; import me.tomassetti.turin.parser.ast.invokables.FunctionDefinitionNode; import me.tomassetti.turin.parser.ast.invokables.InvokableDefinitionNode; import me.tomassetti.turin.parser.ast.annotations.AnnotationUsage; import me.tomassetti.turin.parser.ast.expressions.*; import me.tomassetti.turin.parser.ast.expressions.literals.StringLiteral; import me.tomassetti.turin.parser.ast.invokables.TurinTypeContructorDefinitionNode; import me.tomassetti.turin.parser.ast.invokables.TurinTypeMethodDefinitionNode; import me.tomassetti.turin.parser.ast.relations.RelationDefinition; import me.tomassetti.turin.parser.ast.relations.RelationFieldDefinition; import me.tomassetti.turin.parser.ast.typeusage.*; import me.tomassetti.turin.symbols.FormalParameter; import me.tomassetti.turin.typesystem.PrimitiveTypeUsage; import me.tomassetti.turin.typesystem.ReferenceTypeUsage; import me.tomassetti.turin.typesystem.TypeUsage; import org.objectweb.asm.*; import turin.compilation.DefaultParam; import turin.context.*; import turin.relations.ManyToManyRelation; import turin.relations.OneToManyRelation; import turin.relations.OneToOneRelation; import turin.relations.Relation; import java.util.*; import java.util.stream.Collectors; import static me.tomassetti.bytecode_generation.OpcodesUtils.*; import static org.objectweb.asm.Opcodes.*; /** * Wrap the status of the compilation process, like the class being currently written. */ public class Compilation { public static final int LOCALVAR_INDEX_FOR_THIS_IN_METHOD = 0; static final int LOCALVAR_INDEX_FOR_PARAM_0 = 1; static final String OBJECT_INTERNAL_NAME = JvmNameUtils.canonicalToInternal(Object.class.getCanonicalName()); static final String OBJECT_DESCRIPTOR = JvmNameUtils.canonicalToDescriptor(Object.class.getCanonicalName()); private static final int JAVA_8_CLASS_VERSION = 52; private final CompilationOfPush pushUtils = new CompilationOfPush(this); private final CompilationOfStatements compilationOfStatements = new CompilationOfStatements(this); private final static String METHOD_NAME_OF_FUNCTION = "invoke"; private ClassWriter cw; private SymbolResolver resolver; private LocalVarsSymbolTable localVarsSymbolTable; private String internalClassName; private ErrorCollector errorCollector; public Compilation(SymbolResolver resolver, ErrorCollector errorCollector) { this.resolver = resolver; this.errorCollector = errorCollector; } public List<ClassFileDefinition> compile(TurinFile turinFile) { boolean valid = turinFile.validate(resolver, errorCollector); if (!valid) { return Collections.emptyList(); } List<ClassFileDefinition> classFileDefinitions = new ArrayList<>(); for (Node node : turinFile.getChildren()) { if (node instanceof TurinTypeDefinition) { classFileDefinitions.addAll(compile((TurinTypeDefinition)node)); } else if (node instanceof Program) { classFileDefinitions.addAll(compile((Program) node)); } else if (node instanceof FunctionDefinitionNode) { classFileDefinitions.addAll(compile((FunctionDefinitionNode) node, turinFile.getNamespaceDefinition())); } else if (node instanceof RelationDefinition) { classFileDefinitions.addAll(compile((RelationDefinition) node, turinFile.getNamespaceDefinition())); } else if (node instanceof ContextDefinitionNode) { classFileDefinitions.addAll(compile((ContextDefinitionNode) node, turinFile.getNamespaceDefinition())); } } return classFileDefinitions; } private Collection<? extends ClassFileDefinition> compile(ContextDefinitionNode contextDefinition, NamespaceDefinition namespaceDefinition) { String canonicalClassName = namespaceDefinition.getName() + "." + ContextDefinitionNode.CLASS_PREFIX + contextDefinition.getName(); String internalClassName = JvmNameUtils.canonicalToInternal(canonicalClassName); String contextInternalName = JvmNameUtils.internalName(Context.class); String classSignature = "L" + contextInternalName + "<" + contextDefinition.getType().typeUsage().jvmType().getSignature() + ">;"; // Note that COMPUTE_FRAMES implies COMPUTE_MAXS cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); cw.visit(JAVA_8_CLASS_VERSION, ACC_PUBLIC + ACC_SUPER, internalClassName, classSignature, contextInternalName, null); // Add the static INSTANCE field String fieldDescriptor = "L" + internalClassName + ";"; String fieldSignature = fieldDescriptor; final String fieldName = "INSTANCE"; cw.visitField(ACC_PUBLIC + ACC_STATIC + ACC_FINAL, fieldName, fieldDescriptor, fieldSignature, null); // TODO generate private constructor just to avoid multiple instantiation MethodVisitor mv = cw.visitMethod(ACC_PRIVATE, "<init>", "()V", null, null); PushThis.getInstance().operate(mv); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, contextInternalName, "<init>", "()V", false); mv.visitInsn(RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); // initialize the field in a static initializer mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); mv.visitTypeInsn(NEW, internalClassName); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, internalClassName, "<init>", "()V"); mv.visitFieldInsn(PUTSTATIC, internalClassName, fieldName, fieldDescriptor); mv.visitInsn(RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); return ImmutableList.of(endClass(canonicalClassName)); } private List<ClassFileDefinition> compile(RelationDefinition relationDefinition, NamespaceDefinition namespaceDefinition) { String canonicalClassName = namespaceDefinition.getName() + "." + RelationDefinition.CLASS_PREFIX + relationDefinition.getName(); String internalClassName = JvmNameUtils.canonicalToInternal(canonicalClassName); // Note that COMPUTE_FRAMES implies COMPUTE_MAXS cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); cw.visit(JAVA_8_CLASS_VERSION, ACC_PUBLIC + ACC_SUPER, internalClassName, null, OBJECT_INTERNAL_NAME, null); // Add the relation field, example: // public static final OneToManyRelation<Professor, Course> RELATION = new OneToManyRelation<Professor, Course>(); String fieldDescriptor = relationDefinition.staticFieldDescriptor(); String fieldSignature = null; String fieldTypeInternalName = null; switch (relationDefinition.getRelationType()) { case MANY_TO_MANY: fieldSignature = fieldDescriptor.substring(0, fieldDescriptor.length() - 1); fieldSignature += "<"; fieldSignature += relationDefinition.firstField().getType().jvmType().getSignature(); fieldSignature += relationDefinition.secondField().getType().jvmType().getSignature(); fieldSignature += ">;"; fieldTypeInternalName = JvmNameUtils.internalName(ManyToManyRelation.class); break; case ONE_TO_MANY: fieldSignature = fieldDescriptor.substring(0, fieldDescriptor.length() - 1); fieldSignature += "<"; fieldSignature += relationDefinition.singleField().getType().jvmType().getSignature(); fieldSignature += relationDefinition.manyField().getType().jvmType().getSignature(); fieldSignature += ">;"; fieldTypeInternalName = JvmNameUtils.internalName(OneToManyRelation.class); break; case ONE_TO_ONE: fieldSignature = fieldDescriptor.substring(0, fieldDescriptor.length() - 1); fieldSignature += "<"; fieldSignature += relationDefinition.firstField().getType().jvmType().getSignature(); fieldSignature += relationDefinition.secondField().getType().jvmType().getSignature(); fieldTypeInternalName = JvmNameUtils.internalName(OneToOneRelation.class); fieldSignature += ">;"; break; default: throw new UnsupportedOperationException(); } final String fieldName = "RELATION"; cw.visitField(ACC_PUBLIC + ACC_STATIC + ACC_FINAL, fieldName, fieldDescriptor, fieldSignature, null); // initialize the field in a static initializer MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); mv.visitTypeInsn(NEW, fieldTypeInternalName); mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, fieldTypeInternalName, "<init>", "()V"); mv.visitFieldInsn(PUTSTATIC, internalClassName, fieldName, fieldDescriptor); mv.visitInsn(RETURN); mv.visitMaxs(0, 0); mv.visitEnd(); // generate methods to access the endpoints // e.g., public static Relation.ReferenceSingleEndpoint<Node, Node> parentForChildrenElement(Node childrenElement){ // return RELATION.getReferenceForB(childrenElement); // } // // public static Relation.ReferenceMultipleEndpoint<Node, Node> childrenForParent(Node parent) { // return RELATION.getReferenceForA(parent); // } switch (relationDefinition.getRelationType()) { case MANY_TO_MANY: generateMethodForRelationMultipleEndpoint(cw, relationDefinition.firstField(), relationDefinition.secondField(), relationDefinition.firstField().getType().copy(), relationDefinition.secondField().getType().copy(), true); generateMethodForRelationMultipleEndpoint(cw, relationDefinition.secondField(), relationDefinition.firstField(), relationDefinition.firstField().getType().copy(), relationDefinition.secondField().getType().copy(), false); break; case ONE_TO_MANY: generateMethodForRelationSingleEndpoint(cw, relationDefinition.singleField(), relationDefinition.manyField(), relationDefinition.singleField().getType().copy(), relationDefinition.manyField().getType().copy(), false); generateMethodForRelationMultipleEndpoint(cw, relationDefinition.manyField(), relationDefinition.singleField(), relationDefinition.singleField().getType().copy(), relationDefinition.manyField().getType().copy(), true); break; case ONE_TO_ONE: generateMethodForRelationSingleEndpoint(cw, relationDefinition.firstField(), relationDefinition.secondField(), relationDefinition.firstField().getType().copy(), relationDefinition.secondField().getType().copy(), true); generateMethodForRelationSingleEndpoint(cw, relationDefinition.secondField(), relationDefinition.firstField(), relationDefinition.firstField().getType().copy(), relationDefinition.secondField().getType().copy(), false); break; default: throw new UnsupportedOperationException(); } return ImmutableList.of(endClass(canonicalClassName)); } // e.g., public static Relation.ReferenceMultipleEndpoint<Node, Node> childrenForParent(Node parent) { // return RELATION.getReferenceForA(parent); // } private void generateMethodForRelationMultipleEndpoint(ClassWriter cw, RelationFieldDefinition fieldAccessed, RelationFieldDefinition otherField, TypeUsageNode firstParamType, TypeUsageNode secondParamType, boolean accessingFirstField) { String methodName = fieldAccessed.methodName(); String descriptor = fieldAccessed.methodDescriptor(resolver); String signature = "(" + otherField.getType().jvmType().getSignature() + ")" + JvmNameUtils.descriptor(Relation.ReferenceMultipleEndpoint.class) + "<" + firstParamType.jvmType().getSignature() + secondParamType.jvmType().getSignature() + ">"; // TODO record parameter name MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, methodName, descriptor, signature, null); String invokedMethodName = accessingFirstField ? "getReferenceForA" : "getReferenceForB"; // the method should be invoked on the instance of the relation (MyRelationClass.INSTANCE) JvmFieldDefinition instanceField = new JvmFieldDefinition( JvmNameUtils.canonicalToInternal(fieldAccessed.getRelationDefinition().getGeneratedClassQualifiedName()), "RELATION", fieldAccessed.getRelationDefinition().staticFieldDescriptor(), true ); new PushStaticField(instanceField).operate(mv); new PushLocalVar(OpcodesUtils.loadTypeFor(otherField.getType().jvmType()), 0).operate(mv); JvmMethodDefinition methodDefinition = new JvmMethodDefinition( fieldAccessed.getRelationDefinition().relationClassInternalName(), invokedMethodName, "(" + JvmNameUtils.descriptor(Object.class) + ")" + JvmNameUtils.descriptor(Relation.ReferenceMultipleEndpoint.class), false, false); new ReturnValueBS(Opcodes.ARETURN, new MethodInvocationBS(methodDefinition)).operate(mv); mv.visitMaxs(0, 0); mv.visitEnd(); } // e.g., public static Relation.ReferenceSingleEndpoint<Node, Node> parentForChildrenElement(Node childrenElement){ // return RELATION.getReferenceForB(childrenElement); // } private void generateMethodForRelationSingleEndpoint(ClassWriter cw, RelationFieldDefinition fieldAccessed, RelationFieldDefinition otherField, TypeUsageNode firstParamType, TypeUsageNode secondParamType, boolean accessingFirstField) { String methodName = fieldAccessed.methodName(); String descriptor = fieldAccessed.methodDescriptor(resolver); String signature = "(" + otherField.getType().jvmType().getSignature() + ")" + JvmNameUtils.descriptor(Relation.ReferenceSingleEndpoint.class) + "<" + firstParamType.jvmType().getSignature() + secondParamType.jvmType().getSignature() + ">"; // TODO record parameter name MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, methodName, descriptor, signature, null); String invokedMethodName = accessingFirstField ? "getReferenceForA" : "getReferenceForB"; // the method should be invoked on the instance of the relation (MyRelationClass.INSTANCE) JvmFieldDefinition instanceField = new JvmFieldDefinition( JvmNameUtils.canonicalToInternal(fieldAccessed.getRelationDefinition().getGeneratedClassQualifiedName()), "RELATION", fieldAccessed.getRelationDefinition().staticFieldDescriptor(), true ); new PushStaticField(instanceField).operate(mv); new PushLocalVar(OpcodesUtils.loadTypeFor(otherField.getType().jvmType()), 0).operate(mv); JvmMethodDefinition methodDefinition = new JvmMethodDefinition( fieldAccessed.getRelationDefinition().relationClassInternalName(), invokedMethodName, "(" + JvmNameUtils.descriptor(Object.class) + ")" + JvmNameUtils.descriptor(Relation.ReferenceSingleEndpoint.class), false, false); new ReturnValueBS(Opcodes.ARETURN, new MethodInvocationBS(methodDefinition)).operate(mv); mv.visitMaxs(0, 0); mv.visitEnd(); } private List<ClassFileDefinition> compile(FunctionDefinitionNode functionDefinition, NamespaceDefinition namespaceDefinition) { String canonicalClassName = namespaceDefinition.getName() + "." + FunctionDefinitionNode.CLASS_PREFIX + functionDefinition.getName(); String internalClassName = JvmNameUtils.canonicalToInternal(canonicalClassName); // Note that COMPUTE_FRAMES implies COMPUTE_MAXS cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); cw.visit(JAVA_8_CLASS_VERSION, ACC_PUBLIC + ACC_SUPER, internalClassName, null, OBJECT_INTERNAL_NAME, null); for (AnnotationUsage annotation : functionDefinition.getAnnotations()) { cw.visitAnnotation(annotation.getDescriptor(resolver), true); } generateInvokable(functionDefinition, METHOD_NAME_OF_FUNCTION, true); return ImmutableList.of(endClass(canonicalClassName)); } private void generateField(Property property) { JvmType jvmType = property.getTypeUsage().jvmType(); FieldVisitor fv = cw.visitField(ACC_PRIVATE, property.getName(), jvmType.getDescriptor(), jvmType.getSignature(), null); fv.visitEnd(); } private void generateGetter(Property property, String classInternalName, SymbolResolver resolver) { String getterName = property.getterName(resolver); JvmType jvmType = property.getTypeUsage().jvmType(); MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, getterName, "()" + jvmType.getDescriptor(), "()" + jvmType.getSignature(), null); mv.visitCode(); mv.visitVarInsn(ALOAD, LOCALVAR_INDEX_FOR_THIS_IN_METHOD); mv.visitFieldInsn(GETFIELD, classInternalName, property.fieldName(), jvmType.getDescriptor()); mv.visitInsn(returnTypeFor(jvmType)); // calculated for us mv.visitMaxs(0, 0); mv.visitEnd(); } private ClassFileDefinition endClass(String canonicalName) { cw.visitEnd(); byte[] programBytecode = cw.toByteArray(); cw = null; return new ClassFileDefinition(canonicalName, programBytecode); } private void generateToStringMethod(TurinTypeDefinition typeDefinition) { localVarsSymbolTable = LocalVarsSymbolTable.forInstanceMethod(); MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", "()Ljava/lang/String;", null); mv.visitCode(); if (typeDefinition.getAllProperties(resolver).isEmpty()) { pushUtils.pushExpression(new StringLiteral(typeDefinition.getName())).operate(mv); } else { List<BytecodeSequence> elements = new ArrayList<>(); elements.add(new NewInvocationBS(new JvmConstructorDefinition("java/lang/StringBuilder", "()V"), NoOp.getInstance())); appendToStringBuilder(new StringLiteral(typeDefinition.getName()+"{"), elements); int remaining = typeDefinition.getAllProperties(resolver).size(); for (Property property : typeDefinition.getAllProperties(resolver)) { appendToStringBuilder(new StringLiteral(property.getName() + "="), elements); ValueReference valueReference = new ValueReference(property.getName()); // in this way the field can be solved valueReference.setParent(typeDefinition); appendToStringBuilder(valueReference, elements); remaining--; if (remaining > 0) { appendToStringBuilder(new StringLiteral(", "), elements); } } appendToStringBuilder(new StringLiteral("}"), elements); elements.add(new MethodInvocationBS(new JvmMethodDefinition("java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false, false))); new ComposedBytecodeSequence(elements).operate(mv); } mv.visitInsn(ARETURN); // calculated for us mv.visitMaxs(0, 0); mv.visitEnd(); } private List<ClassFileDefinition> compile(TurinTypeDefinition typeDefinition) { this.internalClassName = JvmNameUtils.canonicalToInternal(typeDefinition.getQualifiedName()); // Note that COMPUTE_FRAMES implies COMPUTE_MAXS cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); // TODO consider generic signature // TODO consider visibility // calculate superclass String superClassInternalName = OBJECT_INTERNAL_NAME; if (typeDefinition.getBaseType().isPresent()) { superClassInternalName = JvmNameUtils.canonicalToInternal(typeDefinition.getBaseType().get().asReferenceTypeUsage().getQualifiedName()); } // calculate interfaces String[] interfaces = typeDefinition.getInterfaces().stream() .map((i)->JvmNameUtils.canonicalToInternal(i.asReferenceTypeUsage().getQualifiedName())) .collect(Collectors.toList()).toArray(new String[]{}); cw.visit(JAVA_8_CLASS_VERSION, ACC_PUBLIC + ACC_SUPER, internalClassName, null, superClassInternalName, interfaces); for (AnnotationUsage annotation : typeDefinition.getAnnotations()) { cw.visitAnnotation(annotation.getDescriptor(resolver), true); } // TODO consider if the property is readable and writable for (Property property : typeDefinition.getDirectProperties(resolver)){ generateField(property); generateGetter(property, internalClassName, resolver); new CompilationOfGeneratedMethods(this, cw).generateSetter(property, internalClassName); } if (!typeDefinition.defineExplicitConstructor(resolver)) { new CompilationOfGeneratedMethods(this, cw).generateConstructor(typeDefinition, internalClassName); } if (!typeDefinition.defineMethodEquals(resolver)) { new CompilationOfGeneratedMethods(this, cw).generateEqualsMethod(typeDefinition, internalClassName); } if (!typeDefinition.defineMethodHashCode(resolver)) { new CompilationOfGeneratedMethods(this, cw).generateHashCodeMethod(typeDefinition, internalClassName); } if (!typeDefinition.defineMethodToString(resolver)) { generateToStringMethod(typeDefinition); } typeDefinition.getDirectMethods().forEach((m)-> generateTurinTypeMethod(m)); typeDefinition.getExplicitConstructors().forEach((c)-> generateTurinTypeConstructor(c)); return ImmutableList.of(endClass(typeDefinition.getQualifiedName())); } private void generateTurinTypeMethod(TurinTypeMethodDefinitionNode methodDefinition) { generateInvokable(methodDefinition, methodDefinition.getName(), false); } private void generateTurinTypeConstructor(TurinTypeContructorDefinitionNode methodDefinition) { generateInvokable(methodDefinition, "<init>", false); } void addDefaultParamAnnotations(MethodVisitor mv, List<? extends FormalParameter> formalParameters) { int defaultParamIndex = 0; for (FormalParameter defaultParam : formalParameters.stream() .filter((p)->p.hasDefaultValue()) .collect(Collectors.<FormalParameter>toList())) { AnnotationVisitor annotationVisitor = mv.visitAnnotation(JvmNameUtils.canonicalToDescriptor(DefaultParam.class.getCanonicalName()), true); annotationVisitor.visit("name", defaultParam.getName()); annotationVisitor.visit("typeSignature", defaultParam.getType().jvmType().getSignature()); annotationVisitor.visit("index", defaultParamIndex); annotationVisitor.visitEnd(); defaultParamIndex++; } } private void generateInvokable(InvokableDefinitionNode invokableDefinition, String invokableName, boolean isStatic) { if (isStatic) { localVarsSymbolTable = LocalVarsSymbolTable.forStaticMethod(); } else { localVarsSymbolTable = LocalVarsSymbolTable.forInstanceMethod(); } String paramsDescriptor = String.join("", invokableDefinition.getParameters().stream().map((dp) -> dp.getType().jvmType().getDescriptor()).collect(Collectors.toList())); String paramsSignature = String.join("", invokableDefinition.getParameters().stream().map((dp) -> dp.getType().jvmType().getSignature()).collect(Collectors.toList())); String methodDescriptor = "(" + paramsDescriptor + ")" + invokableDefinition.getReturnType().jvmType().getDescriptor(); String methodSignature = "(" + paramsSignature + ")" + invokableDefinition.getReturnType().jvmType().getSignature(); // TODO consider exceptions int modifiers = ACC_PUBLIC; if (isStatic) { modifiers = modifiers | ACC_STATIC; } MethodVisitor mv = cw.visitMethod(modifiers, invokableName, methodDescriptor, methodSignature, null); addDefaultParamAnnotations(mv, invokableDefinition.getParameters()); mv.visitCode(); // Add local variables: they are necessary for supporting named parameters and useful for debugging Label start = new Label(); Label end = new Label(); mv.visitLabel(start); for (FormalParameter formalParameter : invokableDefinition.getParameters()) { int index = localVarsSymbolTable.add(formalParameter.getName(), formalParameter); mv.visitLocalVariable(formalParameter.getName(), formalParameter.getType().jvmType().getDescriptor(), formalParameter.getType().jvmType().getSignature(), start, end, index); } compilationOfStatements.compile(invokableDefinition.getBody()).operate(mv); // add implicit return when needed if (invokableDefinition.getReturnType() instanceof VoidTypeUsageNode) { // TODO do not add if there is already a return at the end new ReturnVoidBS().operate(mv); } mv.visitLabel(end); // calculated for us mv.visitMaxs(0, 0); mv.visitEnd(); localVarsSymbolTable = null; } private List<ClassFileDefinition> compile(Program program) { localVarsSymbolTable = LocalVarsSymbolTable.forStaticMethod(); String canonicalClassName = program.getQualifiedName(); String internalClassName = JvmNameUtils.canonicalToInternal(canonicalClassName); // Note that COMPUTE_FRAMES implies COMPUTE_MAXS cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); cw.visit(JAVA_8_CLASS_VERSION, ACC_PUBLIC + ACC_SUPER, internalClassName, null, OBJECT_INTERNAL_NAME, null); // TODO consider exceptions MethodVisitor mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null); localVarsSymbolTable.add(program.getFormalParameter()); mv.visitCode(); compilationOfStatements.compile(program.getStatement()).operate(mv); // Implicit return // TODO remove if already present or not needed (for example if there is an exception) mv.visitInsn(RETURN); // calculated for us mv.visitMaxs(0, 0); mv.visitEnd(); localVarsSymbolTable = null; return ImmutableList.of(endClass(canonicalClassName)); } void appendToStringBuilder(Expression piece, List<BytecodeSequence> elements) { // this is because many String Literal are created during compilation and are detached if (piece.getRoot() == piece) { ResolverRegistry.INSTANCE.record(piece, resolver); } TypeUsage pieceType = piece.calcType(); if (pieceType.sameType(ReferenceTypeUsage.STRING(resolver))) { elements.add(pushUtils.pushExpression(piece)); elements.add(new MethodInvocationBS(new JvmMethodDefinition("java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false, false))); } else if (pieceType.isReference()) { elements.add(pushUtils.pushExpression(piece)); elements.add(new MethodInvocationBS(new JvmMethodDefinition("java/lang/StringBuilder", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuilder;", false, false))); } else if (pieceType.isPrimitive() && pieceType.asPrimitiveTypeUsage().isStoredInInt()) { elements.add(pushUtils.convertAndPush(piece, JvmType.INT)); elements.add(new MethodInvocationBS(new JvmMethodDefinition("java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;", false, false))); } else if (pieceType.sameType(PrimitiveTypeUsage.BOOLEAN)) { elements.add(pushUtils.pushExpression(piece)); elements.add(new MethodInvocationBS(new JvmMethodDefinition("java/lang/StringBuilder", "append", "(Z)Ljava/lang/StringBuilder;", false, false))); } else if (pieceType.sameType(PrimitiveTypeUsage.CHAR)) { elements.add(pushUtils.pushExpression(piece)); elements.add(new MethodInvocationBS(new JvmMethodDefinition("java/lang/StringBuilder", "append", "(C)Ljava/lang/StringBuilder;", false, false))); } else if (pieceType.isPrimitive() && pieceType.asPrimitiveTypeUsage().isLong()) { elements.add(pushUtils.pushExpression(piece)); elements.add(new MethodInvocationBS(new JvmMethodDefinition("java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;", false, false))); } else if (pieceType.isPrimitive() && pieceType.asPrimitiveTypeUsage().isFloat()) { elements.add(pushUtils.pushExpression(piece)); elements.add(new MethodInvocationBS(new JvmMethodDefinition("java/lang/StringBuilder", "append", "(F)Ljava/lang/StringBuilder;", false, false))); } else if (pieceType.isPrimitive() && pieceType.asPrimitiveTypeUsage().isDouble()) { elements.add(pushUtils.pushExpression(piece)); elements.add(new MethodInvocationBS(new JvmMethodDefinition("java/lang/StringBuilder", "append", "(D)Ljava/lang/StringBuilder;", false, false))); } else { throw new UnsupportedOperationException(pieceType.toString()); } } SymbolResolver getResolver() { return resolver; } String getInternalClassName() { return internalClassName; } LocalVarsSymbolTable getLocalVarsSymbolTable() { return localVarsSymbolTable; } public void setLocalVarsSymbolTable(LocalVarsSymbolTable localVarsSymbolTable) { this.localVarsSymbolTable = localVarsSymbolTable; } public CompilationOfPush getPushUtils() { return pushUtils; } }