ExpressionCodeGenerator.java
package com.mackenziehigh.autumn.lang.compiler.compilers;
import autumn.lang.annotations.Infer;
import autumn.lang.compiler.ast.commons.ConstructList;
import autumn.lang.compiler.ast.commons.IBinaryOperation;
import autumn.lang.compiler.ast.commons.IExpression;
import autumn.lang.compiler.ast.commons.IUnaryOperation;
import autumn.lang.compiler.ast.nodes.*;
import autumn.util.F;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.mackenziehigh.autumn.lang.compiler.compilers.ModuleCompiler.HiddenField;
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.IField;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IInvokableMember;
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.IType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IVariableType;
import com.mackenziehigh.autumn.lang.compiler.utils.Conversion;
import com.mackenziehigh.autumn.lang.compiler.utils.TypeSystemUtils;
import com.mackenziehigh.autumn.lang.compiler.utils.Utils;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;
/**
* An instance of this class performs the bytecode generation related to an expression.
*
* @author Mackenzie High
*/
class ExpressionCodeGenerator
extends AbstractAstVisitor
{
/**
* Basically, this is the program that is being compiled.
*/
protected final ProgramCompiler program;
/**
* Basically, this is the module that contains the expressions compiled by this object.
*/
protected final ModuleCompiler module;
/**
* This is the type-system in use by the compiler.
*/
protected final TypeSystem types;
/**
* This object manages the allocation of local variables.
*/
protected final VariableAllocator allocator;
/**
* This object simplifies the manipulation of variables.
*/
protected final VariableManipulator vars;
/**
* This is the list of instructions being generated.
* The whole purpose of this ExpressionCodeGenerator is to add instructions to this list.
*/
protected final InsnList code;
/**
* Sole Constructor.
*
* @param module is the module being compiled.
* @param vars is the enclosing scope.
* @param code is the list of bytecode instructions being generated.
*/
public ExpressionCodeGenerator(final ModuleCompiler module,
final VariableManipulator vars,
final InsnList code)
{
Preconditions.checkNotNull(module);
Preconditions.checkNotNull(vars);
Preconditions.checkNotNull(code);
this.program = module.program;
this.module = module;
this.types = program.typesystem;
this.vars = vars;
this.allocator = vars.allocator();
this.code = code;
}
/**
* This method generalizes the compilation of a unary operator.
*
* @param operation is the operation to compile.
*/
protected void compileUnaryOperator(final IUnaryOperation operation)
{
Preconditions.checkNotNull(operation);
final IMethod method = (IMethod) program.symbols.calls.get(operation);
ConstructList<IExpression> operands = new ConstructList<IExpression>();
operands = operands.add(operation.getOperand());
compileStaticMethodCall(method, operands);
}
/**
* This method generalizes the compilation of a binary operator.
*
* @param operation is the operation to compile.
*/
protected void compileBinaryOperator(final IBinaryOperation operation)
{
Preconditions.checkNotNull(operation);
// During type-checking a method was selected that implements the operation.
// The method is a static utility method in the standard-library Operators class.
final IMethod method = (IMethod) program.symbols.calls.get(operation);
// Get the types of the operands.
final IExpression left = operation.getLeftOperand();
final IExpression right = operation.getRightOperand();
// Generate an invocation of the static utility method.
// The optimizer may optimize out the invocation later.
compileStaticMethodCall(method, Lists.newArrayList(left, right));
}
/**
* This method generalizes the compilation of a static method invocation.
*
* @param method is the method being invoked.
* @param arguments are the expressions that produce the arguments.
*/
protected void compileStaticMethodCall(final IMethod method,
final Iterable<IExpression> arguments)
{
Preconditions.checkNotNull(method);
Preconditions.checkNotNull(arguments);
// Convert the iterable to a list.
final List<IExpression> args = Lists.newArrayList(arguments);
/**
* Generate the bytecode for each argument.
*/
for (int i = 0; i < args.size(); i++)
{
final IType parameter = method.getParameters().get(i).getType();
final IType argument = program.symbols.expressions.get(args.get(i));
// Generate the argument's bytecode.
args.get(i).accept(this);
// Generate code to box/unbox the argument, if needed.
code.add(types.utils.assign(argument, parameter));
}
/**
* Generate the method invocation itself.
*/
code.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
Utils.internalName(method.getOwner()),
method.getName(),
method.getDescriptor()));
/**
* Downcast the return-value, if the return-type is inferred.
*/
if (TypeSystemUtils.isAnnotationPresent(method, Infer.class))
{
if (method.getParameters().isEmpty())
{
// Pass, there should not be a @InferReturnType annotation on the method.
}
else if (program.symbols.expressions.get(args.get(0)).isReferenceType() == false)
{
// Pass, there should not be a @InferReturnType annotation on the method.
}
else
{
final IReferenceType type = (IReferenceType) program.symbols.expressions.get(args.get(0));
code.add(new TypeInsnNode(Opcodes.CHECKCAST, Utils.internalName(type)));
}
}
}
/**
* This method generalizes the compilation of a condition expression.
*
* <p>
* The condition will be evaluated, and automatically boxed or unboxed when necessary.
* </p>
*
* @param condition is the expression that is really a condition.
*/
protected final void compileCondition(final IExpression condition)
{
Preconditions.checkNotNull(condition);
// Retrieve the type of the expression.
final IType type = program.symbols.expressions.get(condition);
// Compile the expression itself.
condition.accept(this);
// If the expression evaluates to a boxed-boolean rather than a primitive-boolean,
// then it is necessary to unbox the value.
if (type.equals(program.typesystem.utils.BOXED_BOOLEAN))
{
final IType input = program.typesystem.utils.BOXED_BOOLEAN;
final IType output = program.typesystem.utils.PRIMITIVE_BOOLEAN;
code.add(program.typesystem.utils.unbox(input, output));
}
}
/**
* This method generates bytecode that converts an expression's value to another type.
*
* <p>
* The generated bytecode expects that the expression's value is the topmost value
* on the operand-stack.
* </p>
*
* @param type is the type to which the value will be converted.
* @param expression is the expression whose value is being converted.
*/
protected final void convert(final IType type,
final IExpression expression)
{
Preconditions.checkNotNull(type);
Preconditions.checkNotNull(expression);
final IType etype = program.symbols.expressions.get(expression);
code.add(program.typesystem.utils.assign(etype, type));
}
@Override
public void visit(final BooleanDatum object)
{
// Generated Bytecode;
//
// LDC value
//
/////////////////////////////////////////////////
code.add(new LdcInsnNode(object.getValue()));
}
@Override
public void visit(final CharDatum object)
{
// Generated Bytecode;
//
// LDC value
//
/////////////////////////////////////////////////
code.add(new LdcInsnNode(object.getValue().value()));
}
@Override
public void visit(final ByteDatum object)
{
// Generated Bytecode;
//
// LDC value
//
/////////////////////////////////////////////////
code.add(new LdcInsnNode(object.getValue().value()));
}
@Override
public void visit(final ShortDatum object)
{
// Generated Bytecode;
//
// LDC value
//
/////////////////////////////////////////////////
code.add(new LdcInsnNode(object.getValue().value()));
}
@Override
public void visit(final IntDatum object)
{
// Generated Bytecode;
//
// LDC value
//
/////////////////////////////////////////////////
code.add(new LdcInsnNode(object.getValue().value()));
}
@Override
public void visit(final LongDatum object)
{
// Generated Bytecode;
//
// LDC value
//
/////////////////////////////////////////////////
final long value = object.getValue().value();
code.add(new LdcInsnNode(value));
}
@Override
public void visit(final FloatDatum object)
{
// Generated Bytecode;
//
// LDC value
//
/////////////////////////////////////////////////
code.add(new LdcInsnNode(object.getValue().value()));
}
@Override
public void visit(final DoubleDatum object)
{
// Generated Bytecode;
//
// LDC value
//
/////////////////////////////////////////////////////////////////////////
final double value = object.getValue().value();
code.add(new LdcInsnNode(value));
}
@Override
public void visit(final BigIntegerDatum object)
{
// Generated Bytecode;
//
// LDC source
// INVOKESTATIC Helpers.createBigInteger(String) : BigInteger
//
/////////////////////////////////////////////////////////////////////////
final String source = object.getValue().value().toString();
code.add(new LdcInsnNode(source));
code.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
Utils.internalName(program.typesystem.utils.HELPERS),
"createBigInteger",
"(Ljava/lang/String;)Ljava/math/BigInteger;"));
}
@Override
public void visit(final BigDecimalDatum object)
{
// Generated Bytecode;
//
// LDC source
// INVOKESTATIC Helpers.createBigDecimal(String) : BigDecimal
//
/////////////////////////////////////////////////////////////////////////
final String source = object.getValue().value().toString();
code.add(new LdcInsnNode(source));
code.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
Utils.internalName(program.typesystem.utils.HELPERS),
"createBigDecimal",
"(Ljava/lang/String;)Ljava/math/BigDecimal;"));
}
@Override
public void visit(final StringDatum object)
{
// Generated Bytecode;
//
// LDC value
//
/////////////////////////////////////////////////
final String value = object.getValue();
final String string = object.getVerbatim() ? value : F.escape(value);
code.add(new LdcInsnNode(string));
}
@Override
public void visit(final NullDatum object)
{
// Generated Bytecode;
//
// ACONST_NULL
//
/////////////////////////////////////////////////
code.add(new InsnNode(Opcodes.ACONST_NULL));
}
@Override
public void visit(final VariableDatum object)
{
vars.load(object.getVariable().getName());
}
@Override
public void visit(final ClassDatum object)
{
// Generated Bytecode;
//
// LDC class
//
/////////////////////////////////////////////////
final IReturnType type = module.imports.resolveReturnType(object.getType());
code.add(Utils.ldcClass(type));
}
@Override
public void visit(final PrognExpression object)
{
final Iterator<IExpression> iter = object.getElements().iterator();
// For each expression in the progn-expression.
while (iter.hasNext())
{
// Get the expression and its type.
final IExpression element = iter.next();
final IType type = program.symbols.expressions.get(element);
// Generate the expression's bytecode.
element.accept(this);
// If the expression is not the last element in the progn-expression,
// then pop its value off of the operand-stack.
if (iter.hasNext())
{
code.add(Utils.selectPop(type));
}
}
}
@Override
public void visit(final ListExpression object)
{
final ExpressionCodeGenerator THIS = this;
final CollectionCompiler<IExpression> cmp = new CollectionCompiler<IExpression>()
{
@Override
public void compile(final IExpression element)
{
final IType element_type = program.symbols.expressions.get(element);
element.accept(THIS);
program.typesystem.utils.autoboxToObject(code(), element_type);
}
@Override
public InsnList code()
{
return THIS.code;
}
};
// Create list.
cmp.compile(object.getElements());
}
@Override
public void visit(final ListComprehensionExpression object)
{
// Generated Bytecode:
//
// NEW LinkedList - Create a new uninitialized linked-list.
// DUP - Duplicate the reference to the list.
// INVOKESPECIAL <init>() - Initialize the linked-list.
// ASTORE list - Store the list in a temporary variable.
// - This list will be the result of the comprehension.
//
// <iterable> - Evaluate the iterable.
// INVOKEINTERFACE iterable.iterator() - Get an Iterator from the Iterable.
// ASTORE iterator - Save the iterator for later.
//
// @CONTINUE - This is where each iteration starts.
// ALOAD iterator - Load the iterator object.
// INVOKEINTERFACE iterator.hasNext() - Ask whether the iterator has more elements.
// IF_FALSE @BREAK - If no, we can now exit the loop.
//
// ALOAD iterator - Load the iterator object.
// INVOKEINTERFACE iterator.next() - Get the next element in the sequence.
// CHECKCAST type - Cast the element to the required type.
// ASTORE variable - Put the element into the variable.
//
// @REDO - This is where redo-statements jump to.
//
//
// - The following code is not always generated.
// <condition> - Evaluate the condition.
// <unbox> - Unbox the condition, if needed.
// IF_FALSE @CONTINUE - If the condition is false skip the element.
// - The code above is not always generated.
//
// <modifier> - Evaluate the modifier expression.
// <box> - Box the result, if needed.
// ALOAD list - Get the list that is being created.
// INVOKEINTERFACE List.add(Object) - Add the result to the list.
// POP - Pop the return-value of the add(Object) method.
//
// GOTO CONTINUE - Goto the start of the next iteration.
//
// @BREAK - This is the exit-point of the loop.
//
// ACONST_NULL - Load null.
// ASTORE iterator - Clear the iterator temporary variable.
//
// ALOAD list - Return the list.
//
// ACONST_NULL - Load null.
// ASTORE list - Clear the list temporary variable.
//
//////////////////////////////////////////////////////////////////////////
final IDeclaredType type = (IDeclaredType) module.imports.resolveVariableType(object.getType());
final LabelNode BREAK = new LabelNode();
final LabelNode CONTINUE = new LabelNode();
final LabelNode REDO = new LabelNode();
/**
* Declare the temporary variable that will store the iterator.
*/
final String iterator = "autumn$temp$" + F.unique();
allocator.declareTemp(iterator, program.typesystem.utils.OBJECT);
/**
* Declare the temporary variable that will store the list.
*/
final String list = "autumn$temp$" + F.unique();
allocator.declareTemp(list, program.typesystem.utils.LIST);
// Create the list and put it into the variable that will store it.
code.add(new TypeInsnNode(Opcodes.NEW, "java/util/LinkedList"));
code.add(new InsnNode(Opcodes.DUP));
code.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
"java/util/LinkedList",
"<init>",
"()V"));
vars.store(list);
// Evaluate the iterable.
object.getIterable().accept(this);
// Get an iterator over the iterable.
code.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
"java/lang/Iterable",
"iterator",
"()Ljava/util/Iterator;"));
// Save the iterator for later.
vars.store(iterator);
code.add(CONTINUE);
// If the iterator is consumed, then break out of the loop.
vars.load(iterator);
code.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
"java/util/Iterator",
"hasNext",
"()Z"));
code.add(new JumpInsnNode(Utils.IF_FALSE, BREAK));
// Get the next element from the iterator.
vars.load(iterator);
code.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
"java/util/Iterator",
"next",
"()Ljava/lang/Object;"));
// Cast the value to the expected type.
code.add(new TypeInsnNode(Opcodes.CHECKCAST, Utils.internalName(type)));
// Assign the value to the variable.
vars.store(object.getVariable().getName());
// Execute the body of the loop.
code.add(REDO);
{
// Evaluate the condition, if it exists.
// If the condition evaluates to false, jump to @CONTINUE.
if (object.getCondition() != null)
{
compileCondition(object.getCondition());
code.add(new JumpInsnNode(Utils.IF_FALSE, CONTINUE));
}
// Evaluate the expression that produces the value to add to the list.
object.getModifier().accept(this);
// Autobox the value, if needed.
program.typesystem.utils.autoboxToObject(code, program.symbols.expressions.get(object.getModifier()));
// Add the value to the list.
vars.load(list);
code.add(new InsnNode(Opcodes.SWAP));
code.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE,
"java/util/List",
"add",
"(Ljava/lang/Object;)Z"));
code.add(new InsnNode(Opcodes.POP)); // Pop the boolean off the operand-stack.
}
code.add(new JumpInsnNode(Opcodes.GOTO, CONTINUE));
code.add(BREAK);
// Clear the iterator temporary variable.
code.add(new InsnNode(Opcodes.ACONST_NULL));
vars.store(iterator);
// Load the generated list onto the operand-stack.
vars.load(list);
// Clear the list temporary variable.
code.add(new InsnNode(Opcodes.ACONST_NULL));
vars.store(list);
}
@Override
public void visit(final DispatchExpression object)
{
/**
* Dispatch-expressions are compiled using a specialized object,
* because of the intricacy of such expressions.
*/
final DispatchCompiler cmp = program.symbols.dispatches.get(object);
/**
* Generate the bytecode.
*/
cmp.compile(this);
}
@Override
public void visit(final CallStaticMethodExpression object)
{
/**
* Get the method overload that was selected during type-checking.
*/
final IMethod method = (IMethod) module.program.symbols.calls.get(object);
/**
* Generate the bytecode that evaluates the arguments invokes the overload.
*/
compileStaticMethodCall(method, object.getArguments());
}
@Override
public void visit(final SetStaticFieldExpression object)
{
// Generated Bytecode:
//
// <value> - Evaluate the value expression.
// <convert> - Box, Unbox, or Coerce the value, if needed.
// PUTSTATIC field = value - Put the value into the field.
//
////////////////////////////////////////////////////////////////////
/**
* Get the type of the value.
*/
final IExpressionType value = program.symbols.expressions.get(object.getValue());
/**
* Get the field that was selected during type-checking.
*/
final IField field = program.symbols.fields.get(object);
final String owner = Utils.internalName(field.getOwner());
final String name = field.getName();
final String desc = field.getType().getDescriptor();
// Evaluate the expression that produces the value to put into the field.
object.getValue().accept(this);
// Convert the value to the type stored in the field.
code.add(program.typesystem.utils.assign(value, field.getType()));
// Set the field.
code.add(new FieldInsnNode(Opcodes.PUTSTATIC, owner, name, desc));
}
@Override
public void visit(final GetStaticFieldExpression object)
{
// Generated Bytecode:
//
// GETSTATIC field = value - Get the value from the field.
//
////////////////////////////////////////////////////////////////////
/**
* Get the field that was selected during type-checking.
*/
final IField field = program.symbols.fields.get(object);
final String owner = Utils.internalName(field.getOwner());
final String name = field.getName();
final String desc = field.getType().getDescriptor();
// Get the value from the field.
code.add(new FieldInsnNode(Opcodes.GETSTATIC, owner, name, desc));
}
@Override
public void visit(final NewExpression object)
{
// Generated Bytecode:
//
// NEW type - Create a new uninitialized instance of the type.
// DUP - Duplicate the reference to the instance.
// <argument [0]> - Evaluate argument[0]
// <argument [1]> - Evaluate argument[1]
// <argument [2]> - Evaluate argument[2]
// <argument [.]> - Evaluate argument[.]
// <argument [N]> - Evaluate argument[N]
// INVOKESPECIAL <init> - Invoke the selected constructor.
//
/////////////////////////////////////////////////////////
/**
* Get the constructor overload that was selected during type-checking.
*/
final IInvokableMember method = program.symbols.calls.get(object);
/**
* Compute the internal name of the type that will be instantiated.
*/
final String type = Utils.internalName(method.getOwner());
/**
* Generate the bytecode.
*/
code.add(new TypeInsnNode(Opcodes.NEW, type));
code.add(new InsnNode(Opcodes.DUP));
final List<IExpression> arguments = object.getArguments().asMutableList();
for (int i = 0; i < arguments.size(); i++)
{
final IType parameter = method.getParameters().get(i).getType();
final IType argument = program.symbols.expressions.get(arguments.get(i));
// Generate the argument's bytecode.
arguments.get(i).accept(this);
// Generate code to box/unbox the argument, if needed.
code.add(types.utils.assign(argument, parameter));
}
code.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
type,
method.getName(),
method.getDescriptor()));
}
@Override
public void visit(final CallMethodExpression object)
{
// Generated Bytecode:
//
// <owner> - Evaluate the owner expression.
// <argument [0]> - Evaluate argument[0]
// <argument [1]> - Evaluate argument[1]
// <argument [2]> - Evaluate argument[2]
// <argument [.]> - Evaluate argument[.]
// <argument [N]> - Evaluate argument[N]
// INVOKE-X method - Invoke the selected method overload.
//
///////////////////////////////////////////////////////////////////////
/**
* Get the method overload that was selected during type-checking.
*/
final IMethod method = (IMethod) program.symbols.calls.get(object);
/**
* Generate the bytecode of the owner expression.
*/
object.getOwner().accept(this);
/**
* Generate the bytecode of the arguments.
*/
final List<IExpression> arguments = object.getArguments().asMutableList();
for (int i = 0; i < arguments.size(); i++)
{
final IType parameter = method.getParameters().get(i).getType();
final IType argument = program.symbols.expressions.get(arguments.get(i));
// Generate the argument's bytecode.
arguments.get(i).accept(this);
// Generate code to box/unbox the argument, if needed.
code.add(types.utils.assign(argument, parameter));
}
/**
* Generate the actual invocation.
*/
final int opcode = method.getOwner().isInterfaceType()
? Opcodes.INVOKEINTERFACE
: Opcodes.INVOKEVIRTUAL;
code.add(new MethodInsnNode(opcode,
Utils.internalName(method.getOwner()),
method.getName(),
method.getDescriptor()));
}
@Override
public void visit(final SetFieldExpression object)
{
// Generated Bytecode:
//
// <owner> - Evaluate the owner expression.
// <value> - Evaluate the value expression.
// <convert> - Box, Unbox, or Coerce the value, if needed.
// PUTFIELD owner.field = value - Put the value into the field.
//
/////////////////////////////////////////////////////////////////////////////////////
/**
* Get the type of the value.
*/
final IExpressionType value = program.symbols.expressions.get(object.getValue());
/**
* Get the field that was chosen during type-checking.
*/
final IField field = program.symbols.fields.get(object);
final String owner = Utils.internalName(field.getOwner());
final String name = field.getName();
final String desc = field.getType().getDescriptor();
// Evaluate the expression that produces the owner of the field.
object.getOwner().accept(this);
// Evaluate the expression that produces the value to put into the field.
object.getValue().accept(this);
// Convert the value to the type stored in the field.
code.add(program.typesystem.utils.assign(value, field.getType()));
// Set the field.
code.add(new FieldInsnNode(Opcodes.PUTFIELD, owner, name, desc));
}
@Override
public void visit(final GetFieldExpression object)
{
// Generated Bytecode:
//
// <owner> - Evaluate the owner expression.
// GETFIELD owner.field - Get the value stored in the field.
//
//////////////////////////////////////////////////////////////////
// Get the field that was chosen during type-checking.
final IField field = program.symbols.fields.get(object);
final String owner = Utils.internalName(field.getOwner());
final String name = field.getName();
final String desc = field.getType().getDescriptor();
// Evaluate the expression that produces the owner of the field.
object.getOwner().accept(this);
// Get the value from the field.
code.add(new FieldInsnNode(Opcodes.GETFIELD, owner, name, desc));
}
@Override
public void visit(final InstanceOfExpression object)
{
// Generated Bytecode:
//
// <value> - Evaluate the value expression.
// INSTANCEOF value : type - Perform the instance-of operation.
//
////////////////////////////////////////////////////////////////////
final IType type = module.imports.resolveDeclaredType(object.getType());
// Evaluate the expression.
object.getValue().accept(this);
// Perform the instance-of check.
code.add(new TypeInsnNode(Opcodes.INSTANCEOF, Utils.internalName((IDeclaredType) type)));
}
@Override
public void visit(final TernaryConditionalExpression object)
{
// Generated Bytecode:
//
// <condition> - Evaluate the condition expression.
// IF_FALSE @ELSE - Conditionally jump to the expression to evaluate.
// <true-case> - Evaluate the true-case expression.
// GOTO @END - Exit the expression.
// @ELSE
// <false-case> - Evaluate the false-case expression.
// @END
//
////////////////////////////////////////////////////////////////////////////////////
final LabelNode ELSE = new LabelNode();
final LabelNode END = new LabelNode();
compileCondition(object.getCondition());
code.add(new JumpInsnNode(Utils.IF_FALSE, ELSE));
{
object.getCaseTrue().accept(this);
}
code.add(new JumpInsnNode(Opcodes.GOTO, END));
code.add(ELSE);
{
object.getCaseFalse().accept(this);
}
code.add(END);
}
@Override
public void visit(final AsOperation object)
{
// The type-checker created an object that describes the conversion to perform.
final Conversion conversion = program.symbols.conversions.get(object);
if (conversion.cast)
{
final LabelNode ELSE = new LabelNode();
final LabelNode END = new LabelNode();
// Evaluate the expression that produces the value to convert.
object.getValue().accept(this);
// Duplicate the object reference.
code.add(new InsnNode(Opcodes.DUP));
// Perform the instanceof check.
// This will pop one object reference off of the operand-stack.
code.add(new TypeInsnNode(Opcodes.INSTANCEOF, Utils.internalName((IReferenceType) conversion.type)));
// If the check failed, replace the value with null.
// Otherwise, perform a cast that will definitely succeed at runtime.
code.add(new JumpInsnNode(Utils.IF_TRUE, ELSE));
{
code.add(new InsnNode(Opcodes.POP));
code.add(new InsnNode(Opcodes.ACONST_NULL));
}
code.add(new JumpInsnNode(Opcodes.GOTO, END));
code.add(ELSE);
{
code.add(new TypeInsnNode(Opcodes.CHECKCAST, Utils.internalName((IReferenceType) conversion.type)));
}
code.add(END);
}
else
{
// Evaluate the expression that produces the value to convert.
object.getValue().accept(this);
// Invoke the conversion method.
final String owner = Utils.internalName(program.typesystem.utils.CONVERSIONS);
final String name = conversion.name;
final String desc = "(" + conversion.value.getDescriptor() + ")" + conversion.type.getDescriptor();
code.add(new MethodInsnNode(Opcodes.INVOKESTATIC, owner, name, desc));
}
}
@Override
public void visit(final IsOperation object)
{
// The type-checker created an object that describes the conversion to perform.
final Conversion conversion = program.symbols.conversions.get(object);
if (conversion.cast)
{
// Evaluate the expression that produces the value to convert.
object.getValue().accept(this);
// Perform the checked cast.
code.add(new TypeInsnNode(Opcodes.CHECKCAST, Utils.internalName((IReferenceType) conversion.type)));
}
else
{
// Evaluate the expression that produces the value to convert.
object.getValue().accept(this);
// Invoke the conversion method.
final String owner = Utils.internalName(program.typesystem.utils.CONVERSIONS);
final String name = conversion.name;
final String desc = "(" + conversion.value.getDescriptor() + ")" + conversion.type.getDescriptor();
code.add(new MethodInsnNode(Opcodes.INVOKESTATIC, owner, name, desc));
}
}
@Override
public void visit(final NegateOperation object)
{
compileUnaryOperator(object);
}
@Override
public void visit(final NotOperation object)
{
compileUnaryOperator(object);
}
@Override
public void visit(final DivideOperation object)
{
compileBinaryOperator(object);
}
@Override
public void visit(final ModuloOperation object)
{
compileBinaryOperator(object);
}
@Override
public void visit(final MultiplyOperation object)
{
compileBinaryOperator(object);
}
@Override
public void visit(final AddOperation object)
{
compileBinaryOperator(object);
}
@Override
public void visit(final ConcatOperation object)
{
compileBinaryOperator(object);
}
@Override
public void visit(final SubtractOperation object)
{
compileBinaryOperator(object);
}
@Override
public void visit(final LessThanOperation object)
{
compileBinaryOperator(object);
}
@Override
public void visit(final LessThanOrEqualsOperation object)
{
compileBinaryOperator(object);
}
@Override
public void visit(final GreaterThanOperation object)
{
compileBinaryOperator(object);
}
@Override
public void visit(final GreaterThanOrEqualsOperation object)
{
compileBinaryOperator(object);
}
@Override
public void visit(final EqualsOperation object)
{
compileBinaryOperator(object);
}
@Override
public void visit(final NotEqualsOperation object)
{
compileBinaryOperator(object);
}
@Override
public void visit(final IdentityEqualsOperation object)
{
compileBinaryOperator(object);
}
@Override
public void visit(final IdentityNotEqualsOperation object)
{
compileBinaryOperator(object);
}
@Override
public void visit(final AndOperation object)
{
// Generated Bytecode:
//
// <left-operand>
// IF_FALSE @ELSE
// <right-operand>
// GOTO @END
// @ELSE
// LDC false
// @END
///////////////////////////////
final LabelNode ELSE = new LabelNode();
final LabelNode END = new LabelNode();
compileCondition(object.getLeftOperand());
code.add(new JumpInsnNode(Utils.IF_FALSE, ELSE));
{
compileCondition(object.getRightOperand());
}
code.add(new JumpInsnNode(Opcodes.GOTO, END));
code.add(ELSE);
{
code.add(new LdcInsnNode(false));
}
code.add(END);
}
@Override
public void visit(final OrOperation object)
{
// Generated Bytecode:
//
// <left-operand>
// IF_TRUE @ELSE
// <right-operand>
// GOTO @END
// @ELSE
// LDC true
// @END
///////////////////////////////
final LabelNode ELSE = new LabelNode();
final LabelNode END = new LabelNode();
compileCondition(object.getLeftOperand());
code.add(new JumpInsnNode(Utils.IF_TRUE, ELSE));
{
compileCondition(object.getRightOperand());
}
code.add(new JumpInsnNode(Opcodes.GOTO, END));
code.add(ELSE);
{
code.add(new LdcInsnNode(true));
}
code.add(END);
}
@Override
public void visit(final XorOperation object)
{
// Generated Bytecode:
//
// <left-operand>
// <right-operand>
// IXOR
//
///////////////////////////////
compileCondition(object.getLeftOperand());
compileCondition(object.getRightOperand());
code.add(new InsnNode(Opcodes.IXOR));
}
@Override
public void visit(final ImpliesOperation object)
{
// Generated Bytecode:
//
// <left-operand>
// IF_FALSE @ELSE
// <right-operand>
// GOTO @END
// @ELSE
// LDC true
// @END
///////////////////////////////
final LabelNode ELSE = new LabelNode();
final LabelNode END = new LabelNode();
compileCondition(object.getLeftOperand());
code.add(new JumpInsnNode(Utils.IF_FALSE, ELSE));
{
compileCondition(object.getRightOperand());
}
code.add(new JumpInsnNode(Opcodes.GOTO, END));
code.add(ELSE);
{
code.add(new LdcInsnNode(true));
}
code.add(END);
}
@Override
public void visit(final NullCoalescingOperation object)
{
// Generated Bytecode:
//
// <left-operand>
// DUP
// IF_NON_NULL @END
// POP
// <right-operand>
// @END
///////////////////////////////
final LabelNode END = new LabelNode();
object.getLeftOperand().accept(this);
code.add(new InsnNode(Opcodes.DUP));
code.add(new JumpInsnNode(Opcodes.IFNONNULL, END));
{
code.add(new InsnNode(Opcodes.POP));
object.getRightOperand().accept(this);
}
code.add(END);
}
@Override
public void visit(final OnceExpression object)
{
// Generated Bytecode;
//
// GETSTATIC hidden.status - Get the status flag, which indicates whether a memoized value is available.
// IF_FALSE @ELSE - If no memoized value is available, this is the first evaluation of the expression.
//
// GETSTATIC hidden.value - Get the previously memoized value.
// GOTO @END - We will simply return the previously memoized value.
//
//
// @ELSE
//
// LDC true
// PUTSTATIC hidden.status - Set the status flag to indicate that a memoized value is now available.
// <value> - Evaluate the expression that produces the value to store.
// DUP - One copy will be memoized and the other one will be returned.
// PUTSTATIC hidden.value - Place the value into the hidden field.
//
// @END
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
final LabelNode ELSE = new LabelNode();
final LabelNode END = new LabelNode();
final HiddenField status = new HiddenField(program.typesystem.utils.PRIMITIVE_BOOLEAN, false);
final HiddenField value = new HiddenField((IVariableType) program.symbols.expressions.get(object), false);
module.hidden.add(status);
module.hidden.add(value);
code.add(new FieldInsnNode(Opcodes.GETSTATIC,
Utils.internalName(module.type),
status.name,
status.type.getDescriptor()));
code.add(new JumpInsnNode(Utils.IF_FALSE, ELSE));
code.add(new FieldInsnNode(Opcodes.GETSTATIC,
Utils.internalName(module.type),
value.name,
value.type.getDescriptor()));
code.add(new JumpInsnNode(Opcodes.GOTO, END));
code.add(ELSE);
code.add(new LdcInsnNode(true));
code.add(new FieldInsnNode(Opcodes.PUTSTATIC,
Utils.internalName(module.type),
status.name,
status.type.getDescriptor()));
object.getValue().accept(this);
code.add(new InsnNode(Opcodes.DUP));
code.add(new FieldInsnNode(Opcodes.PUTSTATIC,
Utils.internalName(module.type),
value.name,
value.type.getDescriptor()));
code.add(END);
}
@Override
public void visit(final LocalsExpression object)
{
/**
* Get the names of the variables that will be captured.
*/
final Set<String> visible = program.symbols.locals.get(object);
/**
* Generate the bytecode that creates the LocalsMap.
*/
loadLocalsMap(object.getLocation(), visible);
}
/**
* This method generates bytecode that creates a LocalsMap object.
*
* @param location is the source-location to embed in the LocalsMap.
* @param variables are the names of the local variables captured in the LocalsMap.
*/
protected void loadLocalsMap(final SourceLocation location,
final Set<String> variables)
{
final String STRING = "Ljava/lang/String;";
final String ITERABLE = "Ljava/lang/Iterable;";
final String owner = Utils.internalName(program.typesystem.utils.LOCALS_MAP);
final String name = "<init>";
final String desc = '(' + STRING + 'I' + 'I' + ITERABLE + ')' + 'V';
// Create the uninitialized LocalsMap object.
code.add(new TypeInsnNode(Opcodes.NEW, owner));
// Duplicate the reference to the map.
code.add(new InsnNode(Opcodes.DUP));
// Load the arguments to pass to the constructor.
code.add(new LdcInsnNode(location.getFile().toString()));
code.add(new LdcInsnNode(location.getLine()));
code.add(new LdcInsnNode(location.getColumn()));
loadLocals(variables);
// Invoke the constructor.
code.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, owner, name, desc));
// The initialized LocalsMap is now the topmost value on the operand-stack.
}
/**
* This method generates bytecode that creates a list of Local objects.
*
* @param variables are the names of the local variables described by the elements of the list.
*/
private void loadLocals(final Set<String> variables)
{
final String CLASS = "Ljava/lang/Class;";
final String STRING = "Ljava/lang/String;";
final String OBJECT = "Ljava/lang/Object;";
final String owner = Utils.internalName(program.typesystem.utils.LOCAL);
final String init = "<init>";
final String desc = '(' + STRING + CLASS + OBJECT + ')' + 'V';
/**
* This object will be used to create a list of Local objects.
* Each Local object represents a local variable in the enclosing scope.
* There will be one object in the list for each local variable.
*/
final CollectionCompiler cmp = new CollectionCompiler()
{
@Override
public void compile(Object element)
{
final String name = (String) element;
final IVariableType type = allocator.typeOf(name);
final int address = allocator.addressOf(name);
// Create the uninitialized Local object.
code().add(new TypeInsnNode(Opcodes.NEW, owner));
// Duplicate the reference to the Local object.
code().add(new InsnNode(Opcodes.DUP));
// Puch the arguments to pass to the constructor.
code().add(new LdcInsnNode(name));
code().add(Utils.ldcClass(type));
code().add(Utils.selectLoadVarInsn(type, address));
program.typesystem.utils.autoboxToObject(code(), type);
// Invoke the constructor.
code().add(new MethodInsnNode(Opcodes.INVOKESPECIAL, owner, init, desc));
// The initialized Local object is now the topmost value on the operand-stack.
}
@Override
public InsnList code()
{
return code;
}
};
// Generate the list.
cmp.compile(variables);
}
}