AnnotationCompiler.java
package com.mackenziehigh.autumn.lang.compiler.compilers;
import autumn.lang.compiler.ClassFile;
import autumn.lang.compiler.ast.nodes.AnnotationDefinition;
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.CustomDeclaredType;
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.IArrayType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IClassType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IFormalParameter;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IMethod;
import com.mackenziehigh.autumn.lang.compiler.utils.TypeSystemUtils;
import com.mackenziehigh.autumn.lang.compiler.utils.Utils;
import com.mackenziehigh.autumn.resources.Finished;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
/**
* An instance of this class is a compiler that compiles an annotation-definition.
*
* @author Mackenzie High
*/
@Finished("2014/08/11")
final class AnnotationCompiler
implements ICompiler
{
/**
* Essentially, this is the program that is being compiled.
*/
public final ProgramCompiler program;
/**
* Essentially, this is the module that contains the annotation-definition.
*/
public final ModuleCompiler module;
/**
* This is the Abstract-Syntax-Tree representation of the annotation-definition.
*/
public final AnnotationDefinition node;
/**
* This will be the type-system representation of the annotation-definition.
*
* This field is set during the type-declaration compiler pass.
*/
public CustomDeclaredType type;
/**
* Sole Constructor.
*
* @param module is the module that contains the enum being compiled.
* @param node is the AST node that represents the enum being compiled.
*/
public AnnotationCompiler(final ModuleCompiler module,
final AnnotationDefinition 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 internal_name = Utils.internalName(type);
final String source_name = Utils.sourceName(type);
/**
* Create the bytecode representation of the annotation itself.
*/
final ClassNode clazz = new ClassNode();
{
clazz.version = Opcodes.V1_6;
clazz.visibleAnnotations = Lists.newLinkedList();
clazz.visibleAnnotations.addAll(module.anno_utils.compileAnnotationList(type.getAnnotations()));
clazz.visibleAnnotations.add(createRuntimeAnnotation());
clazz.access = type.getModifiers();
clazz.name = internal_name;
clazz.superName = Utils.internalName(type.getSuperclass());
clazz.interfaces = program.typesystem.utils.internalNamesOf(type.getSuperinterfaces());
clazz.fields = ImmutableList.of();
clazz.methods = ImmutableList.of(generateMethodValue());
clazz.sourceFile = String.valueOf(node.getLocation().getFile());
}
/**
* 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(source_name, bytecode);
return file;
}
/**
* This method creates the annotation that causes the annotation
* being compiled to have runtime retention.
*
* @return the bytecode representation of the aforesaid annotation.
*/
private AnnotationNode createRuntimeAnnotation()
{
final AnnotationNode anno = new AnnotationNode("Ljava/lang/annotation/Retention;");
final String[] constant = new String[2];
constant[0] = "Ljava/lang/annotation/RetentionPolicy;";
constant[1] = "RUNTIME";
anno.values = Lists.newLinkedList();
anno.values.add("value");
anno.values.add(constant);
return anno;
}
/**
* {@inheritDoc}
*/
@Override
public void performTypeDeclaration()
{
/**
* Determine the descriptor of the annotation.
*/
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 annotation.
*/
this.type = program.typesystem.typefactory().newAnnotationType(descriptor);
}
/**
* {@inheritDoc}
*/
@Override
public void performTypeInitialization()
{
/**
* Create the type-system representations of the annotation-list.
*/
type.setAnnotations(module.anno_utils.typesOf(node.getAnnotations()));
/**
* Add a special annotation.
*/
module.anno_utils.add(type, autumn.lang.internals.annotations.AnnotationDefinition.class);
/**
* Check the list of annotations.
*/
program.checker.checkAnnotations(node.getAnnotations(), type.getAnnotations());
/**
* Set the type's modifier flags.
*/
type.setModifiers(Opcodes.ACC_PUBLIC
+ Opcodes.ACC_ABSTRACT
+ Opcodes.ACC_INTERFACE
+ Opcodes.ACC_ANNOTATION);
/**
* Set the superclass of the annotation-type.
*/
type.setSuperclass(program.typesystem.utils.OBJECT);
/**
* An annotation has a single superinterface, namely java.lang.annotation.Annotation.
*/
type.setSuperinterfaces(Lists.newArrayList(program.typesystem.utils.ANNOTATION));
/**
* Add the value() method to the type.
*/
type.setMethods(Lists.newArrayList(typeOfMethodValue()));
}
/**
* {@inheritDoc}
*/
@Override
public void performTypeStructureChecking()
{
// Pass, because the type does not have any structure to check.
}
/**
* {@inheritDoc}
*/
@Override
public void performTypeUsageChecking()
{
// Pass, because the type does not have any usages to check.
}
/**
* This method creates the type-system representation of the value() method.
*
* @return the new method-type.
*/
private IMethod typeOfMethodValue()
{
final IArrayType STRING_ARRAY = program.typesystem.typefactory().getArrayType(program.typesystem.utils.STRING, 1);
final CustomMethod method = new CustomMethod(type.getTypeFactory(), true);
method.setOwner(type);
method.setAnnotations(Lists.<IAnnotation>newArrayList());
method.setModifiers(Opcodes.ACC_PUBLIC + Opcodes.ACC_ABSTRACT);
method.setName("value");
method.setParameters(Lists.<IFormalParameter>newArrayList());
method.setReturnType(STRING_ARRAY);
method.setThrowsClause(Lists.<IClassType>newArrayList());
return method;
}
/**
* This method generates the bytecode representation of the value() method.
*
* @return the generated bytecode.
*/
private MethodNode generateMethodValue()
{
final MethodNode method = Utils.bytecodeOf(module, TypeSystemUtils.find(type.getMethods(), "value"));
method.annotationDefault = null;
return method;
}
}