ExceptionCompiler.java
package com.mackenziehigh.autumn.lang.compiler.compilers;
import autumn.lang.compiler.ClassFile;
import autumn.lang.compiler.ast.nodes.ExceptionDefinition;
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.CustomConstructor;
import com.mackenziehigh.autumn.lang.compiler.typesystem.CustomDeclaredType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IAnnotation;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IClassType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IConstructor;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IField;
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.IType;
import com.mackenziehigh.autumn.lang.compiler.utils.TypeSystemUtils;
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.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.VarInsnNode;
/**
* An instance of this class controls the compilation of an exception-definition.
*
* @author Mackenzie High
*/
@Finished("2014/08/11")
final class ExceptionCompiler
implements ICompiler
{
/**
* Essentially, this is the program being compiled.
*/
public final ProgramCompiler program;
/**
* Essentially, this is the module that contains the exception-definition.
*/
public final ModuleCompiler module;
/**
* This is the Abstract-Syntax-Tree representation of the exception-definition.
*/
public final ExceptionDefinition node;
/**
* This will be the type-system representation of the exception-definition.
*
* This field is set during the type-declaration compiler pass.
*/
public CustomDeclaredType type;
/**
* This flag is used to prevent a stack-overflow during constructor-inference.
*/
private boolean inferred = false;
/**
* Sole Constructor.
*
* @param module is the module that contains the exception being compiled.
* @param node is the AST node that represents the exception being compiled.
*/
public ExceptionCompiler(final ModuleCompiler module,
final ExceptionDefinition node)
{
Preconditions.checkNotNull(module);
Preconditions.checkNotNull(node);
this.program = module.program;
this.module = module;
this.node = node;
}
/**
* This method generates the compiled class-file.
*
* @return the compiled class-file.
*/
public ClassFile build()
{
final String exception_internal_name = Utils.internalName(type);
final String exception_source_name = Utils.sourceName(type);
/**
* Create the bytecode representation of the exception itself.
*/
final ClassNode clazz = new ClassNode();
{
clazz.version = Opcodes.V1_6;
clazz.visibleAnnotations = module.anno_utils.compileAnnotationList(type.getAnnotations());
clazz.access = type.getModifiers();
clazz.name = exception_internal_name;
clazz.superName = Utils.internalName(type.getSuperclass());
clazz.interfaces = ImmutableList.of();
clazz.fields = ImmutableList.of();
clazz.methods = Lists.newLinkedList();
clazz.sourceFile = String.valueOf(node.getLocation().getFile());
addInferredCtors(clazz);
}
/**
* Assemble the bytecode into an array of bytes.
*/
final ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
clazz.accept(writer);
final byte[] bytecode = writer.toByteArray();
/**
* Create the class-file object that will store the emitted bytecode.
*/
final ClassFile file = new ClassFile(exception_source_name, bytecode);
return file;
}
/**
* {@inheritDoc}
*/
@Override
public void performTypeDeclaration()
{
/**
* Determine the descriptor of the exception.
*/
final String namespace = module.type.getNamespace().replace('.', '/');
final String name = node.getName().getName();
final String descriptor = "L" + namespace + '/' + name + ';';
/**
* Ensure that the type was not already declared elsewhere.
*/
program.checker.requireNonDuplicateType(node.getName(), descriptor);
/**
* Declare the exception.
*/
this.type = program.typesystem.typefactory().newClassType(descriptor);
/**
* Publicize this exception, so that it will be possible to infer constructors.
*/
program.symbols.exceptions.put(type, this);
}
/**
* {@inheritDoc}
*/
@Override
public void performTypeInitialization()
{
final IType supertype = module.imports.resolveClassType(node.getSuperclass());
this.type.setAnnotations(module.anno_utils.typesOf(node.getAnnotations()));
this.type.setModifiers(Opcodes.ACC_PUBLIC);
this.type.setSuperclass((IClassType) supertype);
this.type.setSuperinterfaces(ImmutableList.<IInterfaceType>of());
this.type.setFields(ImmutableList.<IField>of());
this.type.setConstructors(ImmutableList.<IConstructor>of());
this.type.setMethods(ImmutableList.<IMethod>of());
/**
* Add a special annotation.
*/
module.anno_utils.add(type, autumn.lang.internals.annotations.ExceptionDefinition.class);
/**
* Check the list of annotations.
*/
program.checker.checkAnnotations(node.getAnnotations(), type.getAnnotations());
// Note: The constructors will be inferred after all types are partially initialized.
// An exception-type may infer contructors from a type being created simultaneously.
// If we infer the constructors right now, the superclass may not be ready.
// So, we will wait for the types themselves to be partially constructed.
// Then, the constructors can be safely inferred.
}
/**
* {@inheritDoc}
*/
@Override
public void performTypeStructureChecking()
{
/**
* If circular inheritance exists, bad things will happen.
*/
if (TypeSystemUtils.detectCircularInheritance(type))
{
program.checker.reportCircularInheritance(node, type);
}
/**
* The superclass must be a subtype of Throwable.
*/
program.checker.requireThrowable(node.getSuperclass(), type.getSuperclass());
}
/**
* {@inheritDoc}
*/
@Override
public void performTypeUsageChecking()
{
// Pass, because because no type-usage needs checked.
}
/**
* This method generates the bytecode representation of the inherited constructors.
*
* @param clazz is the bytecode representation of the exception's class.
*/
private void addInferredCtors(final ClassNode clazz)
{
for (IConstructor ctor : type.getConstructors())
{
final MethodNode m = Utils.bytecodeOf(module, ctor);
clazz.methods.add(m);
// Load 'this'
m.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
// Load each actual-parameter onto the operand-stack.
int offset = 1; // skip 'this'
for (IFormalParameter param : ctor.getParameters())
{
m.instructions.add(Utils.selectLoadVarInsn(param.getType(), offset));
offset = offset + Utils.sizeof(param.getType());
}
// Invoke the super constructor.
final String owner = Utils.internalName(type.getSuperclass());
final String init = "<init>";
final String desc = ctor.getDescriptor();
m.instructions.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, owner, init, desc));
// Return.
m.instructions.add(new InsnNode(Opcodes.RETURN));
}
}
/**
* This method causes the exception's type to inherit constructors from its superclass.
*/
public void inferConstructors()
{
// The superclass may have to infer its constructors first.
if (!inferred && program.symbols.exceptions.containsKey(type.getSuperclass()))
{
inferred = true;
program.symbols.exceptions.get(type.getSuperclass()).inferConstructors();
}
final List<IConstructor> ctors = Lists.newLinkedList();
for (IConstructor ctor : type.getSuperclass().getConstructors())
{
final boolean public_access = Modifier.isPublic(ctor.getModifiers());
// The constructor must be public.
if (!public_access)
{
continue;
}
// Copy the constructor from the supertype.
final CustomConstructor custom = new CustomConstructor(ctor.getOwner().getTypeFactory());
custom.setOwner(type);
custom.setAnnotations(ImmutableList.<IAnnotation>of());
custom.setModifiers(ctor.getModifiers());
custom.setName(ctor.getName());
custom.setParameters(ctor.getParameters());
custom.setReturnType(ctor.getReturnType());
custom.setThrowsClause(ctor.getThrowsClause());
ctors.add(custom);
}
// Add all the constructors that were copied from the supertype to the type.
type.setConstructors(ctors);
}
}