StatementCodeGenerator.java
package com.mackenziehigh.autumn.lang.compiler.compilers;
import autumn.lang.Delegate;
import autumn.lang.compiler.ast.commons.*;
import autumn.lang.compiler.ast.nodes.*;
import autumn.lang.internals.Helpers;
import autumn.util.F;
import com.google.common.collect.Lists;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IClassType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IDeclaredType;
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.Utils;
import java.util.List;
import java.util.Stack;
import org.objectweb.asm.Opcodes;
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.TableSwitchInsnNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.tree.TypeInsnNode;
/**
* An instance of this class can generate the bytecode that implements a statement.
*
* @author Mackenzie High
*/
final class StatementCodeGenerator
extends ExpressionCodeGenerator
{
private final FunctionCompiler function;
private final Stack<LabelNode> break_labels = new Stack<LabelNode>();
private final Stack<LabelNode> continue_labels = new Stack<LabelNode>();
private final Stack<LabelNode> redo_labels = new Stack<LabelNode>();
public StatementCodeGenerator(final FunctionCompiler function)
{
super(function.module, function.vars, function.instructions);
this.function = function;
}
@Override
public void visit(final IfStatement object)
{
// Generated Bytecode
//
// <condition-1>
// IF_FALSE @END_OF_CASE_1
// <body-1>
// GOTO @END
// @END_OF_CASE_1
//
// <condition-2>
// IF_FALSE @END_OF_CASE_2
// <body-2>
// GOTO @END
// @END_OF_CASE_2
//
// <condition-N>
// IF_FALSE @END_OF_CASE_N
// <body-N>
// GOTO @END
// @END_OF_CASE_N
//
// <body-else>
//
// @END
//////////////////////////////////////////////////////////////////////////////////////////
//
// At least one conditional case is present.
//
// The <body-else> is only generated, when an else-clause is present.
//
//////////////////////////////////////////////////////////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final LabelNode END = new LabelNode();
// Put all the conditional cases into a single list.
final List<ConditionalCase> cases = Lists.newLinkedList();
cases.add(object.getMainCase());
cases.addAll(object.getElifCases().asMutableList());
for (ConditionalCase cc : cases)
{
final LabelNode END_OF_CASE = new LabelNode();
compileCondition(cc.getCondition());
code.add(new JumpInsnNode(Utils.IF_FALSE, END_OF_CASE));
{
cc.getBody().accept(this);
code.add(new JumpInsnNode(Opcodes.GOTO, END));
}
code.add(END_OF_CASE);
}
if (object.getElseCase() != null)
{
object.getElseCase().accept(this);
}
code.add(END);
}
@Override
public void visit(final WhenStatement object)
{
// Generated Bytecode
//
// <condition>
// IF_FALSE @END
// <statement>
// @END
/////////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final LabelNode END = new LabelNode();
compileCondition(object.getCondition());
code.add(new JumpInsnNode(Utils.IF_FALSE, END));
object.getBody().accept(this);
code.add(END);
}
@Override
public void visit(final GotoStatement object)
{
// Generated Bytecode
//
// GOTO @LABEL
//
////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final String name = object.getLabel().getName();
final LabelNode node = function.labels.nodeOf(name);
code.add(new JumpInsnNode(Opcodes.GOTO, node));
}
@Override
public void visit(final MarkerStatement object)
{
// Generated Bytecode
//
// @LABEL
//
////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final String name = object.getLabel().getName();
final LabelNode node = function.labels.nodeOf(name);
code.add(node);
}
@Override
public void visit(final BranchStatement object)
{
// Generated Bytecode
//
// <index> - Evaluate the <index> expression.
// <conversion> - Unbox or auto-widen the index, if needed.
// TABLESWITCH label[0] , ... , label[n] - Generate a jump-table.
//
////////////////////////////////////////////////////////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
/**
* Before we can generate any bytecode, we must convert the labels to ASM labels.
*/
final int max = object.getLabels().size() - 1;
final LabelNode[] labels = new LabelNode[max + 1];
for (int i = 0; i < labels.length; i++)
{
labels[i] = function.labels.nodeOf(object.getLabels().get(i).getName());
}
final LabelNode dflt = function.labels.nodeOf(object.getDefaultLabel().getName());
/**
* Now we can perform the actual code generation.
*/
object.getIndex().accept(this);
convert(program.typesystem.utils.PRIMITIVE_INT, object.getIndex());
/**
* If only the default label is present, then then a simple goto must be generated.
* Otherwise, a verifier error may result at runtime.
*/
if (labels.length != 0)
{
code.add(new TableSwitchInsnNode(0, max, dflt, labels));
}
else
{
code.add(new JumpInsnNode(Opcodes.GOTO, dflt));
}
}
@Override
public void visit(final WhileStatement object)
{
// Generated Bytecode
//
// @CONTINUE - This is where continue-statements jump to.
// <condition> - Evaluate the condition.
// UNBOX condition - Unbox the condition, if needed.
// IF_FALSE @BREAK - If the condition was false, exit the loop.
// @REDO - This is where redo-statements jump to.
// <body> - Execute the body.
// GOTO @CONTINUE - Start the next iteration of the loop.
// @BREAK - This is where break-statements jump to.
//
///////////////////////////////////////////////////////////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final LabelNode BREAK = new LabelNode();
final LabelNode CONTINUE = new LabelNode();
final LabelNode REDO = new LabelNode();
code.add(CONTINUE);
compileCondition(object.getCondition());
code.add(new JumpInsnNode(Utils.IF_FALSE, BREAK));
code.add(REDO);
{
break_labels.push(BREAK);
continue_labels.push(CONTINUE);
redo_labels.push(REDO);
object.getBody().accept(this);
break_labels.pop();
continue_labels.pop();
redo_labels.pop();
}
code.add(new JumpInsnNode(Opcodes.GOTO, CONTINUE));
code.add(BREAK);
}
@Override
public void visit(final UntilStatement object)
{
// Generated Bytecode
//
// @CONTINUE - This is where continue-statements jump to.
// <condition> - Evaluate the condition.
// UNBOX condition - Unbox the condition, if needed.
// IF_TRUE @BREAK - If the condition was true, exit the loop.
// @REDO - This is where redo-statements jump to.
// <body> - Execute the body.
// GOTO @CONTINUE - Start the next iteration of the loop.
// @BREAK - This is where break-statements jump to.
//
///////////////////////////////////////////////////////////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final LabelNode BREAK = new LabelNode();
final LabelNode CONTINUE = new LabelNode();
final LabelNode REDO = new LabelNode();
code.add(CONTINUE);
compileCondition(object.getCondition());
code.add(new JumpInsnNode(Utils.IF_TRUE, BREAK));
code.add(REDO);
{
break_labels.push(BREAK);
continue_labels.push(CONTINUE);
redo_labels.push(REDO);
object.getBody().accept(this);
break_labels.pop();
continue_labels.pop();
redo_labels.pop();
}
code.add(new JumpInsnNode(Opcodes.GOTO, CONTINUE));
code.add(BREAK);
}
@Override
public void visit(final DoWhileStatement object)
{
// Generated Bytecode:
//
// @TOP - This is where each iteration starts.
// @REDO - This is where redo-statements jump to.
// <body> - Execute the body.
// @CONTINUE - This is where continue-statements jump to.
// <condition> - Evaluate the condition.
// UNBOX condition - Unbox the condition, if needed.
// IF_TRUE @TOP - If the condition was true, start the next iteration.
// @BREAK - This is where break-statements jump to.
//
///////////////////////////////////////////////////////////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final LabelNode BREAK = new LabelNode();
final LabelNode CONTINUE = new LabelNode();
final LabelNode REDO = new LabelNode();
final LabelNode TOP = new LabelNode();
code.add(TOP);
code.add(REDO);
{
break_labels.push(BREAK);
continue_labels.push(CONTINUE);
redo_labels.push(REDO);
object.getBody().accept(this);
break_labels.pop();
continue_labels.pop();
redo_labels.pop();
}
code.add(CONTINUE);
compileCondition(object.getCondition());
code.add(new JumpInsnNode(Utils.IF_TRUE, TOP));
code.add(BREAK);
}
@Override
public void visit(final DoUntilStatement object)
{
// Generated Bytecode:
//
// @TOP - This is where each iteration starts.
// @REDO - This is where redo-statements jump to.
// <body> - Execute the body.
// @CONTINUE - This is where continue-statements jump to.
// <condition> - Evaluate the condition.
// UNBOX condition - Unbox the condition, if needed.
// IF_FALSE @TOP - If the condition was false, start the next iteration.
// @BREAK - This is where break-statements jump to.
//
///////////////////////////////////////////////////////////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final LabelNode BREAK = new LabelNode();
final LabelNode CONTINUE = new LabelNode();
final LabelNode REDO = new LabelNode();
final LabelNode TOP = new LabelNode();
code.add(TOP);
code.add(REDO);
{
break_labels.push(BREAK);
continue_labels.push(CONTINUE);
redo_labels.push(REDO);
object.getBody().accept(this);
break_labels.pop();
continue_labels.pop();
redo_labels.pop();
}
code.add(CONTINUE);
compileCondition(object.getCondition());
code.add(new JumpInsnNode(Utils.IF_FALSE, TOP));
code.add(BREAK);
}
@Override
public void visit(final ForeverStatement object)
{
// Generated Bytecode:
//
// @CONTINUE - This is where continue-statements jump to.
// @REDO - This is where redo-statements jump to.
// <body> - Execute the body.
// GOTO @CONTINUE - Start the next iteration of the loop.
// @BREAK - This is where break-statements jump to.
//
///////////////////////////////////////////////////////////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final LabelNode BREAK = new LabelNode();
final LabelNode CONTINUE = new LabelNode();
final LabelNode REDO = new LabelNode();
code.add(CONTINUE);
code.add(REDO);
{
break_labels.push(BREAK);
continue_labels.push(CONTINUE);
redo_labels.push(REDO);
object.getBody().accept(this);
break_labels.pop();
continue_labels.pop();
redo_labels.pop();
}
code.add(new JumpInsnNode(Opcodes.GOTO, CONTINUE));
code.add(BREAK);
}
@Override
public void visit(final ForStatement object)
{
// Generated Bytecode:
//
// <initializer> - Evaluate the initializer.
// UNBOX initializer - Unbox the initializer, if needed.
// ISTORE assignee - Place the initializer into the assignee variable.
//
// @TOP - This is where each loop iteration starts.
//
// <condition> - Evaluate the condition.
// UNBOX condition - Unbox the condition, if needed.
// IF_FALSE @BREAK - If the condition was false, exit the loop.
//
// @REDO - This is where redo-statements jump to.
//
// <body> - Execute the body.
//
// @CONTINUE - This is where continue-statements jump to.
//
// <next> - Evaluate the modifier.
// UNBOX next - Unbox the modifier, if needed.
// ISTORE assignee - Place the modifier (aka next value) into the assignee variable.
//
// GOTO @TOP - Start the next loop iteration.
//
// @BREAK - This is where break-statements jump to.
//
///////////////////////////////////////////////////////////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final LabelNode BREAK = new LabelNode();
final LabelNode CONTINUE = new LabelNode();
final LabelNode REDO = new LabelNode();
final LabelNode TOP = new LabelNode();
object.getInitializer().accept(this);
convert(program.typesystem.utils.PRIMITIVE_INT, object.getInitializer());
vars.store(object.getVariable().getName());
code.add(TOP);
compileCondition(object.getCondition());
code.add(new JumpInsnNode(Utils.IF_FALSE, BREAK));
code.add(REDO);
{
break_labels.push(BREAK);
continue_labels.push(CONTINUE);
redo_labels.push(REDO);
object.getBody().accept(this);
code.add(CONTINUE);
object.getNext().accept(this);
convert(program.typesystem.utils.PRIMITIVE_INT, object.getNext());
vars.store(object.getVariable().getName());
break_labels.pop();
continue_labels.pop();
redo_labels.pop();
}
code.add(new JumpInsnNode(Opcodes.GOTO, TOP));
code.add(BREAK);
}
@Override
public void visit(final ForeachStatement object)
{
// Generated Bytecode:
//
// <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.
//
// <body> - Execute the body of the loop.
// 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.
//
//////////////////////////////////////////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final IDeclaredType type = (IDeclaredType) function.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();
function.allocator.declareTemp(iterator, program.typesystem.utils.OBJECT);
// 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.
function.vars.store(iterator);
code.add(CONTINUE);
// If the iterator is consumed, then break out of the loop.
function.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.
function.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.
function.vars.store(object.getVariable().getName());
// Execute the body of the loop.
code.add(REDO);
{
break_labels.push(BREAK);
continue_labels.push(CONTINUE);
redo_labels.push(REDO);
object.getBody().accept(this);
break_labels.pop();
continue_labels.pop();
redo_labels.pop();
}
code.add(new JumpInsnNode(Opcodes.GOTO, CONTINUE));
code.add(BREAK);
// Clear the iterator temporary variable.
code.add(new InsnNode(Opcodes.ACONST_NULL));
function.vars.store(iterator);
}
@Override
public void visit(final BreakStatement object)
{
// Generated Bytecode:
//
// GOTO @BREAK
//
////////////////////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final LabelNode BREAK = break_labels.peek();
code.add(new JumpInsnNode(Opcodes.GOTO, BREAK));
}
@Override
public void visit(final ContinueStatement object)
{
// Generated Bytecode:
//
// GOTO @CONTINUE
//
////////////////////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final LabelNode CONTINUE = continue_labels.peek();
code.add(new JumpInsnNode(Opcodes.GOTO, CONTINUE));
}
@Override
public void visit(final RedoStatement object)
{
// Generated Bytecode:
//
// GOTO @REDO
//
////////////////////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final LabelNode REDO = redo_labels.peek();
code.add(new JumpInsnNode(Opcodes.GOTO, REDO));
}
@Override
public void visit(final VarStatement object)
{
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
/**
* Get the name of the variable that is being assigned to.
*/
final String assignee = object.getVariable().getName();
/**
* Generate the bytecode that produces the value.
*/
object.getValue().accept(this);
/**
* Generate the bytecode that actually performs the assignment.
*/
vars.store(assignee);
}
@Override
public void visit(final ValStatement object)
{
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
/**
* Get the name of the variable that is being assigned to.
*/
final String name = object.getVariable().getName();
/**
* Generate the bytecode that produces the value.
*/
object.getValue().accept(this);
/**
* Generate the bytecode that actually performs the assignment.
*/
vars.store(name);
}
@Override
public void visit(final LetStatement object)
{
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
/**
* Get the name of the variable that is being assigned to.
*/
final String name_of_variable = object.getVariable().getName();
/**
* Get the type of the variable that is being assigned to.
*/
final IType type_of_variable = function.allocator.typeOf(name_of_variable);
/**
* Generate the bytecode that produces the value.
*/
object.getValue().accept(this);
/**
* Generate the bytecode that performs auto-boxing, auto-unboxing, or coercion, if needed.
*/
convert(type_of_variable, object.getValue());
/**
* Generate the bytecode that actually performs the assignment.
*/
vars.store(object.getVariable().getName());
}
@Override
public void visit(final LambdaStatement object)
{
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final LambdaCompiler lambda = program.symbols.lambdas.get(object);
/**
* Create a new instance of the lambda.
* Capture the local variables.
* Load the lambda onto the operand-stack.
*/
lambda.load(code);
/**
* Place the lambda object into the variable that will store it.
*/
function.vars.store(object.getVariable().getName());
}
@Override
public void visit(final DelegateStatement object)
{
final IClassType functor = module.imports.resolveDefinedFunctorType(object.getType());
final IMethod handler = program.symbols.delegates.get(object);
// Generated Bytecode:
//
// NEW functor - Create a new uninitialized instance of the functor-type.
// DUP - Duplicate the reference to the functor.
// INVOKESTATIC module.instance() - Get the singleton instance of the module that contains the delegate function.
// LDC name - Load the name of the delegate function.
// INVOKESTATIC Helpers.delegate(Module, String) - Create the delegate object.
// INVOKESPECIAL functor.<init>(Functor) - Wrap the delegate object in the functor object.
// STORE variable - Place the wrapped delegate object into the variable.
//
////////////////////////////////////////////////////////////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
code.add(new TypeInsnNode(Opcodes.NEW, Utils.internalName(functor)));
code.add(new InsnNode(Opcodes.DUP));
code.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
Utils.internalName(handler.getOwner()),
"instance",
"()" + handler.getOwner().getDescriptor()));
code.add(new LdcInsnNode(handler.getName()));
Utils.invoke(code,
Opcodes.INVOKESTATIC,
Helpers.class,
Delegate.class,
"delegate",
autumn.lang.Module.class,
String.class);
code.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
Utils.internalName(functor),
"<init>",
"(Lautumn/lang/TypedFunctor;)V"));
function.vars.store(object.getVariable().getName());
}
@Override
public void visit(final SequenceStatement object)
{
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final int break_count = break_labels.size();
for (IStatement s : object.getElements())
{
s.accept(this);
}
assert break_labels.size() == break_count;
assert continue_labels.size() == break_count;
assert redo_labels.size() == break_count;
}
@Override
public void visit(final ExpressionStatement object)
{
// Generated Bytecode:
//
// <expression> - Evaluate the expression.
// POP-X - Pop the value, if any, of the expression off of the stack.
//
///////////////////////////////////////////////////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
object.getExpression().accept(this);
final IType type = program.symbols.expressions.get(object.getExpression());
code.add(Utils.selectPop(type));
}
@Override
public void visit(final NopStatement object)
{
// Generated Bytecode:
//
// NOP
//
//////////////////////////////////////
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
code.add(new InsnNode(Opcodes.NOP));
}
@Override
public void visit(final TryCatchStatement object)
{
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final List<ExceptionHandler> handlers = program.symbols.handlers.get(object);
/**
* This label marks the start of the protected region.
*/
final LabelNode START = new LabelNode();
/**
* This label marks the end of the protected region.
*/
final LabelNode END = new LabelNode();
/**
* This label node marks the end of the entire try-catch construct.
*/
final LabelNode EXIT = new LabelNode();
// Enter the protected region.
code.add(START);
// Execute the protected region.
// If an exception occurs, jump directly to the appropriate handler.
object.getBody().accept(this);
// Exit the protected region.
code.add(END);
// Since no exception occurred, skip the exception handlers.
code.add(new JumpInsnNode(Opcodes.GOTO, EXIT));
// Compile the exception handlers.
for (ExceptionHandler handler : handlers)
{
// Compile a single exception handler.
generateExceptionHandler(START, END, handler);
// If the handler was executed, then immediately exit the try-catch construct.
code.add(new JumpInsnNode(Opcodes.GOTO, EXIT));
}
// Exit the try-catch construct.
code.add(EXIT);
}
private void generateExceptionHandler(final LabelNode start,
final LabelNode end,
final ExceptionHandler handler)
{
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, handler);
/**
* This is the name of the variable that will contain the exception object.
*/
final String variable = handler.getVariable().getName();
/**
* This is the name of the type that this exception-handler handles.
*/
final String type = Utils.internalName((IDeclaredType) module.imports.resolveReturnType(handler.getType()));
/**
* This label marks the entry-point of this try-catch handler.
*/
final LabelNode CATCHER = new LabelNode();
/**
* This object describes the simplified try-catch block created by this exception-handler.
*/
final TryCatchBlockNode block = new TryCatchBlockNode(start, end, CATCHER, type);
// Add the exception-handler to the function.
function.trycatches.add(block);
// Enter the exception handler.
code.add(CATCHER);
// Store the exception object in the appropriate variable.
vars.store(variable);
// Execute the exception handling code.
handler.getBody().accept(this);
}
@Override
public void visit(final ThrowStatement object)
{
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
object.getValue().accept(this);
code.add(new InsnNode(Opcodes.ATHROW));
}
@Override
public void visit(final AssertStatement object)
{
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final String descriptor;
final LabelNode END = new LabelNode();
compileCondition(object.getCondition());
code.add(new JumpInsnNode(Utils.IF_TRUE, END));
code.add(new TypeInsnNode(Opcodes.NEW, "autumn/lang/exceptions/AssertionFailedException"));
code.add(new InsnNode(Opcodes.DUP));
code.add(new LdcInsnNode(object.getLocation().getFile().toString()));
code.add(new LdcInsnNode(object.getLocation().getLine()));
if (object.getMessage() == null)
{
descriptor = "(Ljava/lang/String;I)V";
}
else
{
descriptor = "(Ljava/lang/String;ILjava/lang/String;)V";
object.getMessage().accept(this);
}
code.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
"autumn/lang/exceptions/AssertionFailedException",
"<init>",
descriptor));
code.add(new InsnNode(Opcodes.ATHROW));
code.add(END);
}
@Override
public void visit(final AssumeStatement object)
{
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
final String descriptor;
final LabelNode END = new LabelNode();
code.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
"autumn/lang/compiler/Autumn",
"isAssumeOn",
"()Z"));
code.add(new JumpInsnNode(Utils.IF_FALSE, END));
compileCondition(object.getCondition());
code.add(new JumpInsnNode(Utils.IF_TRUE, END));
code.add(new TypeInsnNode(Opcodes.NEW, "autumn/lang/exceptions/AssumptionFailedException"));
code.add(new InsnNode(Opcodes.DUP));
code.add(new LdcInsnNode(object.getLocation().getFile().toString()));
code.add(new LdcInsnNode(object.getLocation().getLine()));
if (object.getMessage() == null)
{
descriptor = "(Ljava/lang/String;I)V";
}
else
{
descriptor = "(Ljava/lang/String;ILjava/lang/String;)V";
object.getMessage().accept(this);
}
code.add(new MethodInsnNode(Opcodes.INVOKESPECIAL,
"autumn/lang/exceptions/AssumptionFailedException",
"<init>",
descriptor));
code.add(new InsnNode(Opcodes.ATHROW));
code.add(END);
}
@Override
public void visit(final ReturnVoidStatement object)
{
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
/**
* Return from the function immediately.
*/
code.add(new InsnNode(Opcodes.RETURN));
}
@Override
public void visit(final ReturnValueStatement object)
{
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
/**
* Evaluate the expression that produces the value to return.
*/
object.getValue().accept(this);
/**
* Perform auto-boxing or auto-unboxing, if needed.
*/
convert(function.type.getReturnType(), object.getValue());
/**
* Return from the function immediately.
*/
code.add(Utils.selectReturnInsn(function.type.getReturnType()));
}
@Override
public void visit(final RecurStatement object)
{
/**
* Embed the line number in the bytecode for debugging purposes.
*/
Utils.addLineNumber(code, object);
int parameter_index = 0;
/**
* Evaluate the arguments and store them in the parameter variables.
*/
for (IExpression arg : object.getArguments())
{
final String parameter = function.node // Get the function's AST node.
.getParameters() // Get formal-parameter-list.
.getParameters() // From the formal-parameter-list, get the actual list.
.asMutableList() // Convert the list, because we need the get(int) method.
.get(parameter_index++) // Get the formal-parameter from the list.
.getVariable() // We only need the variable, not the type.
.getName(); // Get the name of the variable as a string.
/**
* Generate the argument's bytecode.
*/
arg.accept(this);
/**
* Generate the bytecode that performs auto-boxing, auto-unboxing, and coercions, if needed.
*/
code.add(program.typesystem.utils.assign(program.symbols.expressions.get(arg),
function.allocator.typeOf(parameter)));
/**
* Generate the bytecode that assigns the argument to the parameter's local-variable.
*/
function.vars.store(parameter);
}
/**
* Goto the start of the function.
*/
code.add(new JumpInsnNode(Opcodes.GOTO, function.recur_label));
}
}