BridgeMethod.java
package com.mackenziehigh.autumn.lang.compiler.utils;
import com.google.common.base.Preconditions;
import com.mackenziehigh.autumn.lang.compiler.compilers.ModuleCompiler;
import com.mackenziehigh.autumn.lang.compiler.typesystem.CustomDeclaredType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.CustomMethod;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IMethod;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IReferenceType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IReturnType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IVariableType;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
/**
* An instance of this method simplifies the compilation of a bridge method.
*
* @author Mackenzie High
*/
public final class BridgeMethod
{
/**
* This is the type-system representation of the bridge method definition.
*/
public final IMethod caller;
/**
* This is the type-system representation of the actual method definition.
*/
public final IMethod callee;
/**
* Constructor.
*
* @param owner is the owner of the bridge definition.
* @param returns is the return-type of the bridge method definition.
* @param target is the target method definition.
*/
public BridgeMethod(final CustomDeclaredType owner,
final IReturnType returns,
final IMethod target)
{
/**
* Create the type-system representation of the bridge method.
*/
CustomMethod builder = new CustomMethod(returns.getTypeFactory(), false);
builder.setAnnotations(target.getAnnotations());
builder.setModifiers(target.getModifiers());
builder.setName(target.getName());
builder.setOwner(owner);
builder.setParameters(target.getParameters());
builder.setReturnType(returns);
builder.setThrowsClause(target.getThrowsClause());
this.caller = builder;
this.callee = target;
}
/**
* Constructor.
*
* @param bridge is the bridge method.
* @param target is the method that the bridge method invokes.
*/
public BridgeMethod(final IMethod bridge,
final IMethod target)
{
Preconditions.checkNotNull(bridge);
Preconditions.checkNotNull(target);
this.caller = bridge;
this.callee = target;
}
/**
* This method creates the bytecode representation of the bridge method definition.
*
* @param module is the compiler of the enclosing module.
* @return the generated bytecode.
*/
public MethodNode compile(final ModuleCompiler module)
{
assert caller.getReturnType().isReferenceType();
final MethodNode method = Utils.bytecodeOf(module, caller);
// Remove the abstract and final modifiers.
method.access = method.access & (~Opcodes.ACC_ABSTRACT);
method.access = method.access & (~Opcodes.ACC_FINAL);
// Add the bridge flag.
method.access = method.access | Opcodes.ACC_BRIDGE;
// Change the return-type.
method.desc = caller.getDescriptor();
// Load 'this'
method.instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
int address = 1;
// Load the arguments.
for (int i = 0; i < caller.getParameters().size(); i++)
{
final IVariableType argument_type = caller.getParameters().get(i).getType();
final IVariableType parameter_type = callee.getParameters().get(i).getType();
method.instructions.add(Utils.selectLoadVarInsn(argument_type, address));
method.instructions.add(Utils.conditionalCast(argument_type, parameter_type));
address += Utils.sizeof(argument_type);
}
// Invoke the non-bridge method.
method.instructions.add(new MethodInsnNode(callee.getOwner().isInterfaceType() ? Opcodes.INVOKEINTERFACE : Opcodes.INVOKEVIRTUAL,
Utils.internalName(callee.getOwner()),
callee.getName(),
callee.getDescriptor()));
// Cast the result produced by the non-bridge method to the appropriate type.
method.instructions.add(new TypeInsnNode(Opcodes.CHECKCAST,
Utils.internalName((IReferenceType) caller.getReturnType())));
// Return the result.
method.instructions.add(new InsnNode(Opcodes.ARETURN));
return method;
}
/**
* This method creates the bytecode representation of an abstract bridge method definition.
*
* @param module is the compiler of the enclosing module.
* @return the generated bytecode.
*/
public MethodNode compileAbstract(final ModuleCompiler module)
{
assert caller.getReturnType().isReferenceType();
final MethodNode method = Utils.bytecodeOf(module, caller);
// Add the abstract and final modifiers.
method.access = method.access | Opcodes.ACC_ABSTRACT;
// Add the bridge flag.
method.access = method.access | Opcodes.ACC_BRIDGE;
// Change the return-type.
method.desc = caller.getDescriptor();
return method;
}
}