/**
* Copyright (c) 2012-2016 André Bargull
* Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms.
*
* <https://github.com/anba/es6draft>
*/
package com.github.anba.es6draft.compiler;
import java.util.List;
import com.github.anba.es6draft.ast.Node;
import com.github.anba.es6draft.compiler.Labels.TempLabel;
import com.github.anba.es6draft.compiler.StatementGenerator.Completion;
import com.github.anba.es6draft.compiler.assembler.Jump;
import com.github.anba.es6draft.compiler.assembler.MethodName;
import com.github.anba.es6draft.compiler.assembler.MutableValue;
import com.github.anba.es6draft.compiler.assembler.TryCatchLabel;
import com.github.anba.es6draft.compiler.assembler.Type;
import com.github.anba.es6draft.compiler.assembler.Value;
import com.github.anba.es6draft.compiler.assembler.Variable;
import com.github.anba.es6draft.runtime.internal.ScriptException;
/**
*
*/
abstract class AbstractIterationGenerator<NODE extends Node, ITERATOR> {
private static final class Methods {
// class: ScriptRuntime
static final MethodName ScriptRuntime_stackOverflowError = MethodName.findStatic(Types.ScriptRuntime,
"stackOverflowError", Type.methodType(Types.StackOverflowError, Types.Error));
}
private final CodeGenerator codegen;
AbstractIterationGenerator(CodeGenerator codegen) {
this.codegen = codegen;
}
/**
* Emit code for the iteration body.
*
* @param node
* the ast node
* @param iterator
* the iterator variable
* @param mv
* the code visitor
* @return the completion value
*/
protected abstract Completion iterationBody(NODE node, Variable<ITERATOR> iterator, CodeVisitor mv);
/**
* Emit code for the iteration epilogue.
*
* @param node
* the ast node
* @param iterator
* the iterator variable
* @param mv
* the code visitor
*/
protected void epilogue(NODE node, Variable<ITERATOR> iterator, CodeVisitor mv) {
// Default implementation is empty.
}
/**
* Called before emitting the iteration body.
*
* @param node
* the ast node
* @param mv
* the code visitor
* @return the temporary completion object variable or {@code null}
*/
protected abstract MutableValue<Object> enterIteration(NODE node, CodeVisitor mv);
/**
* Called after emitting the iteration body.
*
* @param node
* the ast node
* @param mv
* the code visitor
* @return the list of generated labels
*/
protected abstract List<TempLabel> exitIteration(NODE node, CodeVisitor mv);
/**
* Emit code for the wrapped iteration.
*
* @param node
* the ast node
* @param iterator
* the iterator variable
* @param mv
* the code visitor
* @return the completion value
*/
public final Completion generate(NODE node, Variable<ITERATOR> iterator, CodeVisitor mv) {
return generate(node, iterator, null, mv);
}
/**
* Emit code for the wrapped iteration.
*
* @param node
* the ast node
* @param iterator
* the iterator variable
* @param target
* the target label
* @param mv
* the code visitor
* @return the completion value
*/
public final Completion generate(NODE node, Variable<ITERATOR> iterator, Jump target, CodeVisitor mv) {
TryCatchLabel startIteration = new TryCatchLabel(), endIteration = new TryCatchLabel();
TryCatchLabel handlerCatch = new TryCatchLabel();
TryCatchLabel handlerCatchStackOverflow = null;
if (codegen.isEnabled(Compiler.Option.IterationCatchStackOverflow)) {
handlerCatchStackOverflow = new TryCatchLabel();
}
boolean hasTarget = target != null;
if (!hasTarget) {
target = new Jump();
}
mv.enterVariableScope();
MutableValue<Object> completion = enterIteration(node, mv);
// Emit loop body
mv.mark(startIteration);
Completion loopBodyResult = iterationBody(node, iterator, mv);
if (!loopBodyResult.isAbrupt()) {
mv.goTo(target);
}
mv.mark(endIteration);
// Restore temporary abrupt targets
List<TempLabel> tempLabels = exitIteration(node, mv);
// Emit throw handler
Completion throwResult = emitThrowHandler(node, iterator, handlerCatch, handlerCatchStackOverflow, mv);
// Emit return handler
Completion returnResult = emitReturnHandler(node, iterator, completion, tempLabels, mv);
mv.exitVariableScope();
mv.tryCatch(startIteration, endIteration, handlerCatch, Types.ScriptException);
if (handlerCatchStackOverflow != null) {
mv.tryCatch(startIteration, endIteration, handlerCatchStackOverflow, Types.Error);
}
if (!hasTarget) {
mv.mark(target);
epilogue(node, iterator, mv);
}
if (tempLabels.isEmpty()) {
// No Return handler installed
return throwResult.select(loopBodyResult);
}
return returnResult.select(throwResult.select(loopBodyResult));
}
private Completion emitThrowHandler(NODE node, Variable<ITERATOR> iterator, TryCatchLabel handlerCatch,
TryCatchLabel handlerCatchStackOverflow, CodeVisitor mv) {
mv.enterVariableScope();
Variable<? extends Throwable> throwable;
if (handlerCatchStackOverflow == null) {
throwable = mv.newVariable("throwable", ScriptException.class);
} else {
throwable = mv.newVariable("throwable", Throwable.class);
}
if (handlerCatchStackOverflow != null) {
mv.catchHandler(handlerCatchStackOverflow, Types.Error);
mv.invoke(Methods.ScriptRuntime_stackOverflowError);
}
mv.catchHandler(handlerCatch, Types.ScriptException);
mv.store(throwable);
IteratorClose(node, iterator, throwable, mv);
mv.load(throwable);
mv.athrow();
mv.exitVariableScope();
return Completion.Throw;
}
private Completion emitReturnHandler(NODE node, Variable<ITERATOR> iterator, Value<Object> completion,
List<TempLabel> tempLabels, CodeVisitor mv) {
// (1) Intercept return instructions
assert tempLabels.isEmpty() || completion != null;
for (TempLabel temp : tempLabels) {
if (temp.isTarget()) {
mv.mark(temp);
IteratorClose(node, iterator, mv);
mv.goTo(temp, completion);
}
}
return Completion.Abrupt; // Return or Break
}
protected abstract void IteratorClose(NODE node, Variable<ITERATOR> iterator,
Variable<? extends Throwable> throwable, CodeVisitor mv);
protected abstract void IteratorClose(NODE node, Variable<ITERATOR> iterator, CodeVisitor mv);
}