FunctionCompiler.java
package com.mackenziehigh.autumn.lang.compiler.compilers;
import autumn.lang.compiler.ast.nodes.FormalParameter;
import autumn.lang.compiler.ast.nodes.FunctionDefinition;
import autumn.lang.compiler.ast.nodes.Variable;
import autumn.lang.compiler.errors.ErrorCode;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.mackenziehigh.autumn.lang.compiler.typesystem.CustomFormalParameter;
import com.mackenziehigh.autumn.lang.compiler.typesystem.CustomMethod;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IAnnotation;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IAnnotationType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IFormalParameter;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IMethod;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IVariableType;
import com.mackenziehigh.autumn.lang.compiler.utils.Utils;
import com.mackenziehigh.autumn.resources.Finished;
import java.lang.reflect.Modifier;
import java.util.List;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
/**
* An instance of this class controls the compilation of a function-definition.
*
* @author Mackenzie High
*/
@Finished("2015/01/18")
final class FunctionCompiler
extends AbstractFunctionCompiler
implements ICompiler
{
/**
* This is the Abstract-Syntax-Tree representation of the function.
*/
public final FunctionDefinition node;
/**
* This is the type-system representation of the function.
*/
public final CustomMethod type;
/**
* This label marks the location that recur-statements redirect execution to.
*/
public final LabelNode recur_label = new LabelNode();
/**
* These are the formal-parameter variables.
*/
private final List<Variable> param_vars = Lists.newArrayList();
/**
* These are the formal-parameter types.
*/
private final List<IVariableType> param_types = Lists.newArrayList();
/**
* Sole Constructor.
*
* @param module is the compiler of the module that this definition is part of.
* @param node is the AST node that represents this definition.
*/
public FunctionCompiler(final ModuleCompiler module,
final FunctionDefinition node)
{
super(module, new VariableAllocator(0));
this.node = node;
this.type = new CustomMethod(module.program.typesystem.typefactory(), false);
}
/**
* {@inheritDoc}
*/
@Override
public void performTypeDeclaration()
{
// Pass
}
/**
* {@inheritDoc}
*/
@Override
public void performTypeInitialization()
{
/**
* Add this function's type to the collection of functions in the module's type.
*/
final List<IMethod> functions = Lists.newLinkedList(module.type.getMethods());
functions.add(type);
module.type.setMethods(functions);
/**
* Convert the Abstract-Syntax-Tree representations of the formal-parameters
* to their type-system representations.
*/
final List<IFormalParameter> params = Lists.newArrayList();
{
for (FormalParameter p : node.getParameters().getParameters())
{
final IVariableType param_type = module.imports.resolveVariableType(p.getType());
final CustomFormalParameter param = new CustomFormalParameter();
param.setAnnotations(ImmutableList.<IAnnotation>of());
param.setType(param_type);
params.add(param);
param_vars.add(p.getVariable());
param_types.add(param_type);
}
}
/**
* Create the type-system representation the function.
*/
type.setOwner(module.type);
type.setAnnotations(module.anno_utils.typesOf(node.getAnnotations()));
type.setModifiers(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | (isAnnotationPresent(program.typesystem.utils.SYNC) ? Modifier.SYNCHRONIZED : 0));
type.setName(node.getName().getName());
type.setParameters(params);
type.setReturnType(module.imports.resolveReturnType(node.getReturnType()));
/**
* Add a special annotation.
*/
module.anno_utils.add(type, autumn.lang.internals.annotations.FunctionDefinition.class);
}
/**
* {@inheritDoc}
*/
@Override
public void performTypeStructureChecking()
{
/**
* Check the list of annotations.
*/
program.checker.checkAnnotations(node.getAnnotations(), type.getAnnotations());
/**
* Some names cannot be used.
*/
program.checker.checkName(node.getName(), "instance");
/**
* Check the signature of a infer-function.
*/
if (isAnnotationPresent(program.typesystem.utils.INFER))
{
boolean error = false;
if (param_types.isEmpty())
{
error = true;
}
else if (param_types.get(0).isReferenceType() == false)
{
error = true;
}
else if (type.getReturnType().equals(param_types.get(0)) == false)
{
error = true;
}
if (error)
{
program.checker.reportWrongSignatureForAnnotation(this, ErrorCode.WRONG_SIGNATURE_FOR_INFER, "(T, ...) : T, where T is some reference-type");
}
}
/**
* Check the signature of a start-function.
*/
if (isAnnotationPresent(program.typesystem.utils.START))
{
if (type.getNamePlusDescriptor().equals("main([Ljava/lang/String;)V") == false)
{
program.checker.reportWrongSignatureForAnnotation(this, ErrorCode.WRONG_SIGNATURE_FOR_START, "main (String[]) : void");
}
}
/**
* Check the signature of a setup-function.
*/
if (isAnnotationPresent(program.typesystem.utils.SETUP))
{
if (type.getDescriptor().equals("()V") == false)
{
program.checker.reportWrongSignatureForAnnotation(this, ErrorCode.WRONG_SIGNATURE_FOR_SETUP, "() : void");
}
}
/**
* Check the signature of a test-function.
*/
if (isAnnotationPresent(program.typesystem.utils.TEST))
{
if (type.getDescriptor().equals("(Lautumn/util/test/TestCase;)V") == false)
{
program.checker.reportWrongSignatureForAnnotation(this, ErrorCode.WRONG_SIGNATURE_FOR_TEST, "(TestCase) : void");
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void performTypeUsageChecking()
{
/**
* The function defines a scope for local variables.
*/
allocator.enterScope();
{
/**
* Allocate the formal-parameter local variables.
*/
for (int i = 0; i < type.getParameters().size(); i++)
{
allocator.declareParameter(param_vars.get(i).getName(), param_types.get(i));
}
/**
* Visit and type-check the body of the function.
*/
try
{
final StatementTypeChecker checker = new StatementTypeChecker(this);
node.getBody().accept(checker);
/**
* Type-checking of labels was deferred in order to avoid
* the need for another compiler pass.
*/
labels.check();
}
catch (TypeCheckFailed ex)
{
// Pass, because the error was already reported via the error-reporter.
}
}
allocator.exitScope();
/**
* A function defines the widest available scope for local variables.
* Therefore, no variable should be in-scope after the function's scope is exited.
*/
allocator.checkExitStatus();
}
/**
* This method generates the bytecode representation of the function.
*
* @return the aforedescribed bytecode.
*/
public MethodNode build()
{
// Generated Bytecode:
//
// LABEL @RECUR
//
// <init-variables>
// <body>
// <default-termination>
//
//
///////////////////////////////////////////////////////////////////////////////////////////
/**
* Generate the bytecode, excluding the body.
*/
final MethodNode method = Utils.bytecodeOf(module, type);
/**
* The body of the function is simply a statement.
* So, we will use a statement-compiler to compile the body.
*/
final StatementCodeGenerator codegen = new StatementCodeGenerator(this);
/**
* At runtime, execution will goto the recur-label,
* whenever a recur-statement is executed inside the function.
*/
method.instructions.add(recur_label);
/**
* Generate bytecode that assigns default-values to the local-variables.
*/
vars.initScope();
/**
* Generate the bytecode of the body.
*/
node.getBody().accept(codegen);
/**
* Transfer the generated instructions into the generated method.
*/
for (AbstractInsnNode insn : instructions.toArray())
{
method.instructions.add(insn);
}
/**
* Generate bytecode that throws an exception,
* if the function unexpectedly terminates.
*/
addDefaultMethodTermination(method);
/**
* If any bytecode level try-catch blocks were created,
* then add them to the generated method.
*/
method.tryCatchBlocks = ImmutableList.copyOf(trycatches);
/**
* Return the generated bytecode representation of the function.
*/
return method;
}
/**
* This method generates the default method termination bytecode.
*
* <p>
* If a function does not contain a return statement, then execution may reach the end
* of the function during an invocation. The JVM requires that this special case be handled.
* Thus, it is necessary to generate bytecode to handle this special situation.
* </p>
*
* <p>
* Per the specification, a function will simply return, if the return-type is void.
* On the other hand, the function will raise an exception, if the return-type is non-void.
* </p>
*
* @param is the bytecode representation of the function.
*/
private void addDefaultMethodTermination(final MethodNode method)
{
if (isReturnTypeVoid())
{
method.instructions.add(new InsnNode(Opcodes.RETURN));
}
else
{
method.instructions.add(new TypeInsnNode(Opcodes.NEW, "autumn/lang/exceptions/UnexpectedTerminationException"));
method.instructions.add(new InsnNode(Opcodes.DUP));
method.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
"autumn/lang/exceptions/UnexpectedTerminationException",
"<init>",
"()V"));
method.instructions.add(new InsnNode(Opcodes.ATHROW));
}
}
/**
* This convenience function determines whether the function's return-type is void.
*
* @return true, iff the function returns void.
*/
private boolean isReturnTypeVoid()
{
return "void".equals(node.getReturnType().getName().getName())
&& node.getReturnType().getDimensions() == null
&& node.getReturnType().getNamespace() == null;
}
/**
* This method determines whether a particular annotation is applied to this method.
*
* @param anno is the type of the annotation.
* @return true, iff an annotation of the specified type is applied to this function.
*/
public final boolean isAnnotationPresent(final IAnnotationType anno)
{
Preconditions.checkNotNull(anno);
for (IAnnotation x : type.getAnnotations())
{
if (x.getAnnotationType().equals(anno))
{
return true;
}
}
return false;
}
}