ModuleCompiler.java
package com.mackenziehigh.autumn.lang.compiler.compilers;
import autumn.lang.compiler.ClassFile;
import autumn.lang.compiler.ast.nodes.AnnotationDefinition;
import autumn.lang.compiler.ast.nodes.DesignDefinition;
import autumn.lang.compiler.ast.nodes.EnumDefinition;
import autumn.lang.compiler.ast.nodes.ExceptionDefinition;
import autumn.lang.compiler.ast.nodes.FormalParameter;
import autumn.lang.compiler.ast.nodes.FunctionDefinition;
import autumn.lang.compiler.ast.nodes.FunctorDefinition;
import autumn.lang.compiler.ast.nodes.ImportDirective;
import autumn.lang.compiler.ast.nodes.Module;
import autumn.lang.compiler.ast.nodes.ModuleDirective;
import autumn.lang.compiler.ast.nodes.Name;
import autumn.lang.compiler.ast.nodes.StructDefinition;
import autumn.lang.compiler.ast.nodes.TupleDefinition;
import autumn.lang.compiler.ast.nodes.TypeSpecifier;
import autumn.lang.internals.annotations.ModuleDefinition;
import autumn.util.F;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.mackenziehigh.autumn.lang.compiler.typesystem.CustomDeclaredType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.CustomFormalParameter;
import com.mackenziehigh.autumn.lang.compiler.typesystem.CustomMethod;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IDeclaredType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IExpressionType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IFormalParameter;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IInterfaceType;
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.AnnotationUtils;
import com.mackenziehigh.autumn.lang.compiler.utils.Utils;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TableSwitchInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
/**
* An instance of this class controls the compilation of an entire module.
*
* @author Mackenzie High
*/
public final class ModuleCompiler
implements ICompiler
{
/**
* An instance of this class represents a hidden field within the module class.
*
* <p>
* Hidden fields are used to implement once-expressions, etc.
* </p>
*/
static final class HiddenField
{
/**
* At the bytecode level, all fields must have a name.
*/
public final String name = "autumn$hidden$field$" + F.unique();
/**
* This is the static-type of the field.
*/
public final IVariableType type;
/**
* This is true, iff the field is final.
*/
public final boolean readonly;
/**
* These are instructions to add to the static constructor.
*/
public InsnList initializer = new InsnList();
/**
* Sole Constructor.
*
* @param type is the static-type of the new hidden field.
* @param readonly is true, iff the field is final.
* @throws NullPointerException if type is null.
*/
public HiddenField(final IVariableType type,
final boolean readonly)
{
Preconditions.checkNotNull(type);
this.type = type;
this.readonly = readonly;
}
/**
* This method generates the bytecode representation of the field.
*/
public FieldNode build()
{
final int access = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | (readonly ? Opcodes.ACC_FINAL : 0);
final FieldNode field = new FieldNode(access, name, type.getDescriptor(), null, null);
return field;
}
}
/**
* This is the full name of the module being compiled.
* This field is initialized during the type-declaration pass.
*/
private String module_name;
/**
* Essentially, this is the program that is being compiled.
*/
public final ProgramCompiler program;
/**
* This is the Abstract-Syntax-Tree representation of the module.
*/
public final Module node;
/**
* This will be assigned the Abstract-Syntax-Tree of the module-directive
* that is contained within the module.
*/
private ModuleDirective module_directive;
/**
* This will be assigned the type-system representation of the module.
*/
public CustomDeclaredType type;
/**
* This object makes compiling annotations easier.
*
* <p>
* This object will be needed during the compilation of code inside the module.
* </p>
*/
public AnnotationUtils anno_utils;
/**
* This field is true, iff the module being compiled is anonymous.
* This field is initialized during the type-declaration pass.
*/
private boolean anonymous;
/**
* Essentially, this object implements the import-directives contained in the module.
*/
public final Importer imports;
/**
* Essentially, these are the annotations that are defined in the module.
*/
final List<AnnotationCompiler> annotations = Lists.newLinkedList();
/**
* Essentially, these are the exceptions that are defined in the module.
*/
final List<ExceptionCompiler> exceptions = Lists.newLinkedList();
/**
* Essentially, these are the designs that are defined in the module.
*/
final List<DesignCompiler> designs = Lists.newLinkedList();
/**
* Essentially, these are the structs that are defined in the module.
*/
final List<StructCompiler> structs = Lists.newLinkedList();
/**
* Essentially, these are the tuples that are defined in the module.
*/
final List<TupleCompiler> tuples = Lists.newLinkedList();
/**
* Essentially, these are the enums that are defined in the module.
*/
final List<EnumCompiler> enums = Lists.newLinkedList();
/**
* Essentially, these are the functors that are defined in the module.
*/
final List<FunctorCompiler> functors = Lists.newLinkedList();
/**
* Essentially, these are the functions that are defined in the module.
*/
final List<FunctionCompiler> functions = Lists.newLinkedList();
/**
* These objects represent the hidden fields to add to the module.
*
* <p>
* Hidden fields are needed to implement some constructs, such as once-expressions.
* </p>
*/
final List<HiddenField> hidden = Lists.newLinkedList();
/**
* This bytecode field caches the ModuleInfo object that describes the module.
*/
private final FieldNode info = new FieldNode(Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL,
"module_info",
"Lautumn/lang/ModuleInfo;",
null,
null);
/**
* This bytecode field stores a list of delegates.
* Each element is a delegate that refers to a function in this module.
*/
private final FieldNode delegates = new FieldNode(Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL,
"delegates",
"Ljava/util/List;",
null,
null);
/**
* This bytecode field stores the only instance of the module's class.
*/
private FieldNode instance_field;
/**
* Sole Constructor.
*
* @param program is the program being compiled.
* @param node is the Abstract-Syntax-Tree representation of the module to compile.
*/
public ModuleCompiler(final ProgramCompiler program,
final Module node)
{
this.program = program;
this.node = node;
this.imports = new Importer(this);
for (AnnotationDefinition x : node.getAnnotations())
{
annotations.add(new AnnotationCompiler(this, x));
}
for (ExceptionDefinition x : node.getExceptions())
{
exceptions.add(new ExceptionCompiler(this, x));
}
for (EnumDefinition x : node.getEnums())
{
enums.add(new EnumCompiler(this, x));
}
for (DesignDefinition x : node.getDesigns())
{
designs.add(new DesignCompiler(this, x));
}
for (StructDefinition x : node.getStructs())
{
structs.add(new StructCompiler(this, x));
}
for (TupleDefinition x : node.getTuples())
{
tuples.add(new TupleCompiler(this, x));
}
for (FunctorDefinition x : node.getFunctors())
{
functors.add(new FunctorCompiler(this, x));
}
for (FunctionDefinition x : node.getFunctions())
{
functions.add(new FunctionCompiler(this, x));
}
this.anno_utils = new AnnotationUtils(this);
}
/**
* This method performs the bytecode-generation of the module and the code therein.
*
* @return the objects that contain the generated bytecode.
*/
public Set<ClassFile> build()
{
final Set<ClassFile> classes = Sets.newHashSet();
for (AnnotationCompiler x : annotations)
{
classes.add(x.build());
}
for (ExceptionCompiler x : exceptions)
{
classes.add(x.build());
}
for (EnumCompiler x : enums)
{
classes.add(x.build());
}
for (DesignCompiler x : designs)
{
classes.add(x.build());
}
for (StructCompiler x : structs)
{
classes.add(x.build());
}
for (TupleCompiler x : tuples)
{
classes.add(x.build());
}
for (FunctorCompiler x : functors)
{
classes.add(x.build());
}
/**
* Generate the bytecode of the module itself.
*/
classes.add(buildModule());
return classes;
}
/**
* This method performs the bytecode-generation of a module itself.
*
* <p>
* Basically, a module compiles to a JVM class.
* </p>
*
* @return an object that contains the generated bytecode.
*/
private ClassFile buildModule()
{
final List<FieldNode> fields = Lists.newLinkedList();
final List<MethodNode> methods = Lists.newLinkedList();
/**
* Generate the bytecode that implements the special methods of the module.
*/
methods.add(buildClinit());
methods.add(buildInit());
methods.add(buildModuleInfo());
methods.add(buildInstance());
methods.add(buildModuleInvokeFunction());
/**
* Generate the bytecode that implements the user-defined functions.
*/
for (FunctionCompiler x : functions)
{
methods.add(x.build());
}
/**
* Generate the bytecode that implements the hidden fields.
*/
for (HiddenField field : hidden)
{
fields.add(field.build());
}
final String module_internal_name = Utils.internalName(type);
final String module_source_name = Utils.sourceName(type);
/**
* Generate the bytecode of the class that is the module.
*/
final ClassNode clazz = new ClassNode();
{
clazz.version = Opcodes.V1_6;
clazz.visibleAnnotations = anno_utils.compileAnnotationList(type.getAnnotations());
clazz.access = type.getModifiers();
clazz.name = module_internal_name;
clazz.superName = Utils.internalName(type.getSuperclass());
clazz.interfaces = Lists.newLinkedList();
clazz.fields = Lists.newLinkedList();
clazz.fields.add(info);
clazz.fields.add(delegates);
clazz.fields.add(instance_field);
clazz.fields.addAll(fields);
clazz.methods = ImmutableList.copyOf(methods);
clazz.sourceFile = String.valueOf(node.getLocation().getFile());
assert clazz.superName.equals("autumn/lang/internals/AbstractModule");
assert clazz.access == Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL;
}
/**
* Convert ObjectWeb ASM representation of the bytecode to an array of bytes.
*/
final ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
clazz.accept(writer);
final byte[] bytecode = writer.toByteArray();
/**
* Wrap the bytecode in an object.
*/
final ClassFile file = new ClassFile(module_source_name, bytecode);
return file;
}
/**
* This method generates the static constructor for the module's class.
*
* @return an object representation of the module's static constructor.
*/
private MethodNode buildClinit()
{
final MethodNode m = new MethodNode();
m.access = Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL;
m.name = "<clinit>";
m.desc = "()V";
m.exceptions = ImmutableList.of();
String owner;
String name;
String desc;
// Create the only instance of the module's class.
owner = Utils.internalName(type);
m.instructions.add(new TypeInsnNode(Opcodes.NEW, owner));
m.instructions.add(new InsnNode(Opcodes.DUP));
name = "<init>";
desc = "()V";
m.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, owner, name, desc));
// Put the instance in to the field named "instance".
owner = Utils.internalName(type);
name = "instance";
desc = type.getDescriptor();
m.instructions.add(new FieldInsnNode(Opcodes.PUTSTATIC, owner, name, desc));
/**
* Initialize the hidden fields.
*/
for (HiddenField field : hidden)
{
m.instructions.add(field.initializer);
}
/**
* Invoke the setup functions.
*/
for (FunctionCompiler function : functions)
{
if (function.isAnnotationPresent(program.typesystem.utils.SETUP))
{
owner = Utils.internalName(type);
name = function.type.getName();
desc = "()V";
m.instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC, owner, name, desc));
}
}
// Exit the static constructor.
m.instructions.add(new InsnNode(Opcodes.RETURN));
return m;
}
/**
* This method generates the instance constructor for the module's class.
*
* @return an object representation of the module's instance constructor.
*/
private MethodNode buildInit()
{
final MethodNode m = new MethodNode();
m.access = Opcodes.ACC_PRIVATE;
m.name = "<init>";
m.desc = "()V";
m.exceptions = ImmutableList.of();
String owner;
String name;
String desc;
/**
* Invoke super().
*/
owner = Utils.internalName(program.typesystem.utils.ABSTRACT_MODULE);
name = "<init>";
desc = "()V";
m.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // Load 'this'.
m.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, owner, name, desc));
/**
* Create the ModuleInfoBuilder object.
*/
owner = Utils.internalName(program.typesystem.utils.MODULE_INFO_BUILDER);
name = "<init>";
desc = "(Lautumn/lang/Module;)V";
m.instructions.add(new TypeInsnNode(Opcodes.NEW, Utils.internalName(program.typesystem.utils.MODULE_INFO_BUILDER)));
m.instructions.add(new InsnNode(Opcodes.DUP));
m.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // Load 'this'.
m.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, owner, name, desc));
/**
* Add the delegates to the user-defined functions to the ModuleInfoBuilder object.
*/
for (FunctionCompiler function : functions)
{
/**
* Duplicate the reference to the ModuleInfoBuilder object.
*/
m.instructions.add(new InsnNode(Opcodes.DUP));
/**
* Create a new delegate object.
*/
loadDelegate(m, function);
/**
* Add the delegate object in the ModuleInfoBuilder.
*/
owner = Utils.internalName(program.typesystem.utils.MODULE_INFO_BUILDER);
name = "add";
desc = "(Lautumn/lang/Delegate;)V";
m.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, owner, name, desc));
}
// Note: After the loop exited a reference to the ModuleInfoBuilder is on the operand-stack.
/**
* Add the annotation-types declared in the module to the ModuleInfoBuilder object.
*/
for (AnnotationCompiler x : annotations)
{
owner = Utils.internalName(program.typesystem.utils.MODULE_INFO_BUILDER);
name = "addAnnotation";
desc = "(Ljava/lang/Class;)V";
m.instructions.add(new InsnNode(Opcodes.DUP));
m.instructions.add(Utils.ldcClass(x.type));
m.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, owner, name, desc));
}
/**
* Add the enum-types declared in the module to the ModuleInfoBuilder object.
*/
for (EnumCompiler x : enums)
{
owner = Utils.internalName(program.typesystem.utils.MODULE_INFO_BUILDER);
name = "addEnum";
desc = "(Ljava/lang/Class;)V";
m.instructions.add(new InsnNode(Opcodes.DUP));
m.instructions.add(Utils.ldcClass(x.type));
m.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, owner, name, desc));
}
/**
* Add the exception-types declared in the module to the ModuleInfoBuilder object.
*/
for (ExceptionCompiler x : exceptions)
{
owner = Utils.internalName(program.typesystem.utils.MODULE_INFO_BUILDER);
name = "addException";
desc = "(Ljava/lang/Class;)V";
m.instructions.add(new InsnNode(Opcodes.DUP));
m.instructions.add(Utils.ldcClass(x.type));
m.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, owner, name, desc));
}
/**
* Add the design-types declared in the module to the ModuleInfoBuilder object.
*/
for (DesignCompiler x : designs)
{
owner = Utils.internalName(program.typesystem.utils.MODULE_INFO_BUILDER);
name = "addDesign";
desc = "(Ljava/lang/Class;)V";
m.instructions.add(new InsnNode(Opcodes.DUP));
m.instructions.add(Utils.ldcClass(x.type));
m.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, owner, name, desc));
}
/**
* Add the struct-types declared in the module to the ModuleInfoBuilder object.
*/
for (StructCompiler x : structs)
{
owner = Utils.internalName(program.typesystem.utils.MODULE_INFO_BUILDER);
name = "addStruct";
desc = "(Ljava/lang/Class;)V";
m.instructions.add(new InsnNode(Opcodes.DUP));
m.instructions.add(Utils.ldcClass(x.type));
m.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, owner, name, desc));
}
/**
* Add the tuple-types declared in the module to the ModuleInfoBuilder object.
*/
for (TupleCompiler x : tuples)
{
owner = Utils.internalName(program.typesystem.utils.MODULE_INFO_BUILDER);
name = "addTuple";
desc = "(Ljava/lang/Class;)V";
m.instructions.add(new InsnNode(Opcodes.DUP));
m.instructions.add(Utils.ldcClass(x.type));
m.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, owner, name, desc));
}
/**
* Add the functor-types declared in the module to the ModuleInfoBuilder object.
*/
for (FunctorCompiler x : functors)
{
owner = Utils.internalName(program.typesystem.utils.MODULE_INFO_BUILDER);
name = "addFunctor";
desc = "(Ljava/lang/Class;)V";
m.instructions.add(new InsnNode(Opcodes.DUP));
m.instructions.add(Utils.ldcClass(x.type));
m.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, owner, name, desc));
}
// Note: After the loop exited a reference to the ModuleInfoBuilder is on the operand-stack.
/**
* Convert the ModuleInfoBuilder object into a ModuleInfo object.
*/
owner = Utils.internalName(program.typesystem.utils.MODULE_INFO_BUILDER);
name = "build";
desc = "()" + program.typesystem.utils.MODULE_INFO.getDescriptor();
m.instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, owner, name, desc));
/**
* Store the ModuleInfo object in the appropriate field.
*/
owner = Utils.internalName(type);
name = info.name;
desc = info.desc;
m.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // Load 'this'.
m.instructions.add(new InsnNode(Opcodes.SWAP));
m.instructions.add(new FieldInsnNode(Opcodes.PUTFIELD, owner, name, desc));
/**
* Exit the static constructor.
*/
m.instructions.add(new InsnNode(Opcodes.RETURN));
return m;
}
/**
* This method generates the info() special method.
*
* @return an object representation of the special method.
*/
private MethodNode buildModuleInfo()
{
final MethodNode m = new MethodNode();
m.access = Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL;
m.name = "info";
m.desc = "()Lautumn/lang/ModuleInfo;";
m.exceptions = ImmutableList.of();
// Load the ModuleInfo object onto the operand-stack.
m.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0)); // Load 'this'.
m.instructions.add(new FieldInsnNode(Opcodes.GETFIELD,
Utils.internalName(type),
info.name,
info.desc));
// Return the ModuleInfo object.
m.instructions.add(new InsnNode(Opcodes.ARETURN));
return m;
}
/**
* This method generates bytecode that creates a new delegate object.
*
* <p>
* The delegate must refer to a function in the module being compiled by this object.
* </p>
*
* @param m is the method that will have bytecode added to it.
* @param function is the function that the delegate refers to.
*/
private void loadDelegate(final MethodNode m,
final FunctionCompiler function)
{
assert functions.contains(function);
String owner;
String name;
String desc;
final InsnList code = new InsnList();
// This object can create a list containing the types of the function's parameters.
final CollectionCompiler<IFormalParameter> params = new CollectionCompiler<IFormalParameter>()
{
@Override
public void compile(IFormalParameter element)
{
code().add(Utils.ldcClass(element.getType()));
}
@Override
public InsnList code()
{
return code;
}
};
////////////////////////////////////////////////////////////////////////////////////////////
// Now we need to create an instance of the ModuleDelegate class.
// This involves two steps.
// First, we must create an uninitialized ModuleDelegate object.
// Second, we must invoke a constructor in the object ot finalize object construction.
// The constructor will expect several arguments.
// Argument #1 is the singleton instance of the module's class.
// Argument #2 is the name of the function that the delegate refers to.
// Argument #3 is a list of Class objects that represent the types of
// the function's formal parameters.
// Argument #4 is a Class object that represents the function's return type.
// Argument #5 is the index of the function within the module.
// This is needed to facilitate invocation of the function via the delegate.
////////////////////////////////////////////////////////////////////////////////////////////
// Create the uninitialized object.
owner = Utils.internalName(program.typesystem.utils.MODULE_DELEGATE);
code.add(new TypeInsnNode(Opcodes.NEW, owner));
// Duplicate the reference to the delegate object.
// We need one reference for use during the constructor invocation.
// Then, we will need to leave another reference on the operand-stack.
// Whoever called this code generation method expects the object reference to be there.
code.add(new InsnNode(Opcodes.DUP));
// Load Argument #1.
code.add(new VarInsnNode(Opcodes.ALOAD, 0)); // Load 'this'.
// Load Argument #2.
code.add(new LdcInsnNode(function.type.getName()));
// Load Argument #3.
params.compile(function.type.getParameters());
// Load Argument #4.
code.add(Utils.ldcClass(function.type.getReturnType()));
// Load Argument #5.
code.add(new LdcInsnNode(functions.indexOf(function)));
// Invoke the constructor.
name = "<init>";
desc = "(Lautumn/lang/Module;Ljava/lang/String;Ljava/util/List;Ljava/lang/Class;I)V";
code.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, owner, name, desc));
// Now, an initialized ModuleDelegate object is on top of the operand-stack.
// Add the code to the method.
m.instructions.add(code);
}
/**
* This method generates the instance() special method.
*
* <p>
* The special method returns the only instance of the module's class.
* The special method is static and takes no arguments.
* </p>
*
* @return an object representation of the special method.
*/
private MethodNode buildInstance()
{
final MethodNode m = new MethodNode();
m.access = Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL;
m.name = "instance";
m.desc = "()" + type.getDescriptor();
m.exceptions = ImmutableList.of();
// The only instance of the module's class is stored in a static final field.
// Get the value from the field and push it onto the operand-stack.
m.instructions.add(new FieldInsnNode(Opcodes.GETSTATIC,
Utils.internalName(type),
instance_field.name,
instance_field.desc));
// Return the instance of the module.
m.instructions.add(new InsnNode(Opcodes.ARETURN));
return m;
}
/**
* This method generates the invoke(int, ArgumentStack) special method.
*
* @return an object representation of the special method.
*/
private MethodNode buildModuleInvokeFunction()
{
final MethodNode m = new MethodNode();
String owner;
String name;
String desc;
m.access = Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL;
m.name = "invoke";
m.desc = "(ILautumn/lang/internals/ArgumentStack;)V";
m.exceptions = ImmutableList.of("java/lang/Throwable");
final InsnList code = new InsnList();
final LabelNode[] cases = new LabelNode[functions.size()];
for (int i = 0; i < functions.size(); i++)
{
cases[i] = new LabelNode();
}
final LabelNode default_case = new LabelNode();
code.add(new VarInsnNode(Opcodes.ILOAD, 1));
code.add(new TableSwitchInsnNode(0,
functions.size() - 1,
default_case,
cases));
int c = 0;
for (FunctionCompiler function : functions)
{
// Add the label that marks the entry-point of the switch case.
code.add(cases[c++]);
// Load the argument-stack onto the operand-stack.
// We will need it later in order to return the return-value from the method.
code.add(new VarInsnNode(Opcodes.ALOAD, 2));
// Pop the arguments off of the argument-stack and push them onto the operand-stack.
for (int i = 0; i < function.type.getParameters().size(); i++)
{
// Load the argument-stack onto the operand-stack.
// The return-value of the function will need pushed onto the argument-stack later.
code.add(new VarInsnNode(Opcodes.ALOAD, 2));
final IExpressionType param = (IExpressionType) function.type
.getParameters()
.get(i)
.getType();
// Transfer the i-th argument from the argument-stack to the operand-stack.
Utils.getArgument(program, code, param, i);
}
// Load the argument-stack onto the operand-stack.
code.add(new VarInsnNode(Opcodes.ALOAD, 2));
// Clear the argument-stack.
owner = Utils.internalName(program.typesystem.utils.ARGUMENT_STACK);
name = "clear";
desc = "()V";
code.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, owner, name, desc));
// Invoke the function.
owner = Utils.internalName(type);
name = function.type.getName();
desc = function.type.getDescriptor();
code.add(new MethodInsnNode(Opcodes.INVOKESTATIC, owner, name, desc));
// Transfer the return-value from the operand-stack to the argument-stack.
if (function.type.getReturnType().isVoidType() == false)
{
Utils.pushArgument(program, code, function.type.getReturnType());
}
else
{
// Since the method returns void, null must be loaded onto the argument-stack.
code.add(new InsnNode(Opcodes.ACONST_NULL));
Utils.pushArgument(program, code, function.type.getReturnType());
}
// Do not fall through to the next switch-case.
code.add(new InsnNode(Opcodes.RETURN));
}
code.add(default_case);
code.add(new InsnNode(Opcodes.RETURN));
m.instructions.add(code);
return m;
}
/**
* {@inheritDoc}
*/
@Override
public void performTypeDeclaration()
{
/**
* This object only compiles non empty nodes.
*/
assert isEmpty(node) == false;
if (node.getModuleDirectives().size() < 1)
{
// Error - missing module directive
// fail fast
}
if (node.getModuleDirectives().size() > 1)
{
// Error - too many directives
// fail fast
}
final ModuleDirective directive = node.getModuleDirectives().asMutableList().get(0);
final String module_descriptor = processModuleDirective(directive);
/**
* Ensure that the type was not already declared elsewhere.
*/
final Name module_name = node.getModuleDirectives().asMutableList().get(0).getName();
program.checker.requireNonDuplicateType(module_name, module_descriptor);
/**
* Declare the module's type.
*/
this.type = program.typesystem.typefactory().newClassType(module_descriptor);
/**
* Declare all the designs, structs, tuples, etc that are defined in the module.
*/
for (ICompiler x : compilers())
{
x.performTypeDeclaration();
}
/**
* Process the import directives.
*/
loadImports();
}
/**
* {@inheritDoc}
*/
@Override
public void performTypeInitialization()
{
/**
* Create the type-system representations of the annotation-list.
*/
type.setAnnotations(anno_utils.typesOf(module_directive.getAnnotations()));
/**
* Add a special annotation.
*/
anno_utils.add(type, ModuleDefinition.class);
/**
* Initialize the module's type.
*/
this.type.setModifiers(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL);
this.type.setSuperclass(program.typesystem.utils.ABSTRACT_MODULE);
this.type.setSuperinterfaces(ImmutableList.<IInterfaceType>of());
for (ICompiler x : compilers())
{
x.performTypeInitialization();
}
final CustomMethod instance = new CustomMethod(type.getTypeFactory(), false);
instance.setOwner(type);
instance.setName("instance");
instance.setModifiers(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC);
instance.setParameters(new LinkedList());
instance.setReturnType(type);
instance.setThrowsClause(new LinkedList());
// Add this function's type to the collection of functions in the module's type.
final List<IMethod> list = Lists.newLinkedList(type.getMethods());
list.add(instance);
type.setMethods(list);
// Create the field that will store the only instance of the module's class.
instance_field = new FieldNode(Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL,
"instance",
type.getDescriptor(),
null,
null);
}
/**
* {@inheritDoc}
*/
@Override
public void performTypeStructureChecking()
{
/**
* Perform type-structure-checking on all of the functions, etc.
*/
for (ICompiler x : compilers())
{
x.performTypeStructureChecking();
}
/**
* Check the list of annotations.
*/
program.checker.checkAnnotations(module_directive.getAnnotations(), type.getAnnotations());
/**
* No two functions can have the exact same name and parameters.
*/
reportDuplicateFunctions();
}
/**
* This method ensures that the module does not contain duplicate functions.
*
* <p>
* A function is a duplicate, if it has the same name and parameter-types as another function.
* </p>
*/
private void reportDuplicateFunctions()
{
final Map<String, FunctionCompiler> signatures = Maps.newTreeMap();
/**
* For each function F, map the signature of F to the compiler of F.
*/
for (FunctionCompiler function : functions)
{
/**
* Note: If the map already contains an entry for the signature,
* the old entry will be replaced, which is fine.
*/
signatures.put(function.type.getNamePlusParameterListDescriptor(), function);
}
/**
* For each function compiler F, ensure that the signature of F is mapped to F.
*/
for (FunctionCompiler function : functions)
{
/**
* Yes, I really do mean identity inequality here.
*/
if (function != signatures.get(function.type.getNamePlusParameterListDescriptor()))
{
/**
* This will throw an exception and issue a compiler-warning.
*/
program.checker.reportDuplicateFunction(function);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void performTypeUsageChecking()
{
for (ICompiler x : compilers())
{
x.performTypeUsageChecking();
}
}
/**
* This method executes all the import-directives and imports the types defined in this module.
*/
private void loadImports()
{
/**
* Import the module itself.
*/
imports.importType(module_name.substring(module_name.lastIndexOf('.') + 1),
Utils.sourceName(type));
// The module name "My" is used to generically refer to the current module.
imports.importType("My", Utils.sourceName(type));
/**
* Execute all the import directives.
*/
for (ImportDirective import_directive : node.getImportDirectives())
{
final TypeSpecifier imported_type = import_directive.getType();
final String typename = program.typesystem.utils.extractTypeName(imported_type);
final String simple_name = imported_type.getName().getName();
if (imported_type.getDimensions() != null)
{
// TODO: error
}
imports.importType(simple_name, typename);
}
/**
* Import all the annotations defined in this module.
*/
for (AnnotationCompiler cmp : annotations)
{
autoimport(cmp.type);
}
/**
* Import all the exceptions defined in this module.
*/
for (ExceptionCompiler cmp : exceptions)
{
autoimport(cmp.type);
}
/**
* Import all the enums defined in this module.
*/
for (EnumCompiler cmp : enums)
{
autoimport(cmp.type);
}
/**
* Import all the designs defined in this module.
*/
for (DesignCompiler cmp : designs)
{
autoimport(cmp.type);
}
/**
* Import all the structs defined in this module.
*/
for (StructCompiler cmp : structs)
{
autoimport(cmp.type);
}
/**
* Import all the tuples defined in this module.
*/
for (TupleCompiler cmp : tuples)
{
autoimport(cmp.type);
}
/**
* Import all the functors defined in this module.
*/
for (FunctorCompiler cmp : functors)
{
autoimport(cmp.type);
}
}
/**
* This method performs the importation of a type declared within the module.
*
* @param type is the type to import.
*/
private void autoimport(final IDeclaredType type)
{
/**
* Get the fully-qualified internal-name of the type.
*/
final String internal_name = Utils.internalName(type).replace('/', '.');
/**
* Get the simple-name of the type.
*/
final String simple_name = internal_name.substring(internal_name.lastIndexOf('.') + 1, internal_name.length());
/**
* Import the type.
*/
imports.importType(simple_name, internal_name);
}
private String processModuleDirective(final ModuleDirective directive)
{
this.module_directive = directive;
// This is the name of the package that the module resides in.
final StringBuilder module_package = new StringBuilder();
// String together the pieces of the package's name.
for (Name n : directive.getNamespace().getNames())
{
module_package.append(n.getName());
module_package.append('/');
}
// This is the name of the module, excluding the name of the enclosing package.
final String module_name = directive.getName() == null
? createNameForAnonymousModule()
: directive.getName().getName();
// Remember the full name of the module for later use.
this.module_name = module_package.toString().replace('/', '.') + module_name;
final String module_descriptor = "L" + module_package + module_name + ";";
return module_descriptor;
}
/**
* This method creates a name for an anonymous module, since there is really no such thing.
*
* @return a name for the module that is being compiled.
*/
private String createNameForAnonymousModule()
{
return "Module$" + F.unique();
}
/**
* This method avoids the need for multiple for-loops elsewhere.
*
* @return the compilers used by this compiler.
*/
private Set<ICompiler> compilers()
{
final Set<ICompiler> result = Sets.newHashSet();
result.addAll(annotations);
result.addAll(exceptions);
result.addAll(enums);
result.addAll(designs);
result.addAll(structs);
result.addAll(tuples);
result.addAll(functors);
result.addAll(functions);
return result;
}
/**
* This method converts a list of AST nodes that represent formal parameters
* to their type-system based representation.
*
* @param parameters are the formal parameters as represented by AST nodes.
* @return the formal parameters as represented in the type-system.
*/
public List<IFormalParameter> formals(final Iterable<FormalParameter> parameters)
{
final List<IFormalParameter> formals = Lists.newArrayList();
for (FormalParameter param : parameters)
{
// This method will issue an error-message, if the type does not exist, etc.
final IVariableType param_type = imports.resolveVariableType(param.getType());
// Create the type-system representation of the formal parameter.
final CustomFormalParameter formal = new CustomFormalParameter();
formal.setAnnotations(new LinkedList());
formal.setType(param_type);
formals.add(formal);
}
return formals;
}
/**
* This method determines whether a module is empty.
*
* <p>
* Empty modules are caused, for example, by empty source files.
* </p>
*
* @param node is the AST representation of the module.
* @return true, iff the is an empty module.
*/
public static boolean isEmpty(Module node)
{
return node.getAnnotations().isEmpty()
&& node.getDesigns().isEmpty()
&& node.getEnums().isEmpty()
&& node.getExceptions().isEmpty()
&& node.getFunctions().isEmpty()
&& node.getFunctors().isEmpty()
&& node.getImportDirectives().isEmpty()
&& node.getModuleDirectives().isEmpty()
&& node.getStructs().isEmpty()
&& node.getTuples().isEmpty();
}
}