/**
* 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 static com.github.anba.es6draft.compiler.BindingInitializationGenerator.BindingInitialization;
import static com.github.anba.es6draft.compiler.BindingInitializationGenerator.InitializeBoundNameWithUndefined;
import static com.github.anba.es6draft.semantics.StaticSemantics.BoundNames;
import static com.github.anba.es6draft.semantics.StaticSemantics.LexicallyDeclaredNames;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.github.anba.es6draft.ast.Comprehension;
import com.github.anba.es6draft.ast.ComprehensionFor;
import com.github.anba.es6draft.ast.ComprehensionIf;
import com.github.anba.es6draft.ast.ComprehensionQualifier;
import com.github.anba.es6draft.ast.Expression;
import com.github.anba.es6draft.ast.LegacyComprehension;
import com.github.anba.es6draft.ast.LegacyComprehensionFor;
import com.github.anba.es6draft.ast.LegacyComprehensionFor.IterationKind;
import com.github.anba.es6draft.ast.Node;
import com.github.anba.es6draft.ast.scope.BlockScope;
import com.github.anba.es6draft.ast.scope.Name;
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.Type;
import com.github.anba.es6draft.compiler.assembler.Variable;
import com.github.anba.es6draft.runtime.DeclarativeEnvironmentRecord;
import com.github.anba.es6draft.runtime.LexicalEnvironment;
import com.github.anba.es6draft.runtime.internal.ScriptIterator;
/**
* <h1>12 ECMAScript Language: Expressions</h1><br>
* <h2>12.2 Primary Expression</h2>
* <ul>
* <li>Array Comprehension
* </ul>
*/
abstract class ComprehensionGenerator extends DefaultCodeGenerator<Void> {
private static final class Methods {
// class: GeneratorObject
static final MethodName GeneratorObject_isLegacyGenerator = MethodName.findVirtual(
Types.GeneratorObject, "isLegacyGenerator", Type.methodType(Type.BOOLEAN_TYPE));
// class: Iterator
static final MethodName Iterator_hasNext = MethodName.findInterface(Types.Iterator,
"hasNext", Type.methodType(Type.BOOLEAN_TYPE));
static final MethodName Iterator_next = MethodName.findInterface(Types.Iterator, "next",
Type.methodType(Types.Object));
// class: ScriptRuntime
static final MethodName ScriptRuntime_enumerate = MethodName.findStatic(
Types.ScriptRuntime, "enumerate",
Type.methodType(Types.ScriptIterator, Types.Object, Types.ExecutionContext));
static final MethodName ScriptRuntime_enumerateValues = MethodName.findStatic(
Types.ScriptRuntime, "enumerateValues",
Type.methodType(Types.ScriptIterator, Types.Object, Types.ExecutionContext));
static final MethodName ScriptRuntime_iterate = MethodName.findStatic(Types.ScriptRuntime,
"iterate",
Type.methodType(Types.ScriptIterator, Types.Object, Types.ExecutionContext));
}
private Iterator<Node> elements;
private Iterator<Variable<ScriptIterator<?>>> iterators;
protected ComprehensionGenerator(CodeGenerator codegen) {
super(codegen);
}
@Override
protected Void visit(Node node, CodeVisitor mv) {
throw new IllegalStateException(String.format("node-class: %s", node.getClass()));
}
/**
* Runtime Semantics: ComprehensionEvaluation
* <p>
* ComprehensionTail : AssignmentExpression
*/
@Override
protected abstract Void visit(Expression node, CodeVisitor mv);
/**
* Runtime Semantics: ComprehensionEvaluation
*/
@Override
public Void visit(Comprehension node, CodeVisitor mv) {
ArrayList<Node> list = new ArrayList<>(node.getList().size() + 1);
list.addAll(node.getList());
list.add(node.getExpression());
elements = list.iterator();
// Create variables early so they'll appear next to each other in the local variable map.
ArrayList<Variable<ScriptIterator<?>>> iters = new ArrayList<>();
for (ComprehensionQualifier e : node.getList()) {
if (e instanceof ComprehensionFor || e instanceof LegacyComprehensionFor) {
Variable<ScriptIterator<?>> iter = mv.newVariable("iter", ScriptIterator.class)
.uncheckedCast();
iters.add(iter);
}
}
iterators = iters.iterator();
// Start generating code.
elements.next().accept(this, mv);
return null;
}
/**
* Runtime Semantics: ComprehensionEvaluation
*/
@Override
public Void visit(LegacyComprehension node, CodeVisitor mv) {
BlockScope scope = node.getScope();
if (scope.isPresent()) {
mv.enterVariableScope();
Variable<LexicalEnvironment<DeclarativeEnvironmentRecord>> env = mv.newVariable("env",
LexicalEnvironment.class).uncheckedCast();
Variable<DeclarativeEnvironmentRecord> envRec = mv.newVariable("envRec",
DeclarativeEnvironmentRecord.class);
newDeclarativeEnvironment(scope, mv);
mv.store(env);
getEnvRec(env, envRec, mv);
for (Name name : LexicallyDeclaredNames(node.getScope())) {
BindingOp<DeclarativeEnvironmentRecord> op = BindingOp.of(envRec, name);
op.createMutableBinding(envRec, name, false, mv);
InitializeBoundNameWithUndefined(envRec, name, mv);
}
mv.load(env);
pushLexicalEnvironment(mv);
mv.exitVariableScope();
}
mv.enterScope(node);
visit((Comprehension) node, mv);
mv.exitScope();
if (scope.isPresent()) {
popLexicalEnvironment(mv);
}
return null;
}
/**
* Runtime Semantics: ComprehensionComponentEvaluation
* <p>
* ComprehensionIf : if ( AssignmentExpression )
*/
@Override
public Void visit(ComprehensionIf node, CodeVisitor mv) {
/* steps 1-2 */
ValType type = expression(node.getTest(), mv);
/* steps 3-4 */
ToBoolean(type, mv);
/* steps 5-6 */
Jump lblTest = new Jump();
mv.ifeq(lblTest);
{
/* step 5a */
elements.next().accept(this, mv);
}
mv.mark(lblTest);
return null;
}
/**
* Runtime Semantics: ComprehensionComponentEvaluation
* <p>
* ComprehensionFor : for ( ForBinding of AssignmentExpression )
*/
@Override
public Void visit(ComprehensionFor node, CodeVisitor mv) {
Jump lblTest = new Jump(), lblLoop = new Jump();
Variable<ScriptIterator<?>> iter = iterators.next();
/* steps 1-2 */
expressionBoxed(node.getExpression(), mv);
/* steps 3-4 */
mv.loadExecutionContext();
mv.lineInfo(node.getExpression());
mv.invoke(Methods.ScriptRuntime_iterate);
mv.store(iter);
/* step 5 (not applicable) */
/* step 6 */
mv.nonDestructiveGoTo(lblTest);
/* steps 6.d-e */
mv.mark(lblLoop);
mv.load(iter);
mv.lineInfo(node);
mv.invoke(Methods.Iterator_next);
/* steps 6.f-j */
BlockScope scope = node.getScope();
if (scope.isPresent()) {
mv.enterVariableScope();
Variable<LexicalEnvironment<DeclarativeEnvironmentRecord>> env = mv.newVariable("env",
LexicalEnvironment.class).uncheckedCast();
Variable<DeclarativeEnvironmentRecord> envRec = mv.newVariable("envRec",
DeclarativeEnvironmentRecord.class);
// stack: [nextValue] -> []
Variable<Object> nextValue = mv.newVariable("nextValue", Object.class);
mv.store(nextValue);
newDeclarativeEnvironment(scope, mv);
mv.store(env);
getEnvRec(env, envRec, mv);
for (Name name : BoundNames(node.getBinding())) {
BindingOp<DeclarativeEnvironmentRecord> op = BindingOp.of(envRec, name);
op.createMutableBinding(envRec, name, false, mv);
}
BindingInitialization(codegen, envRec, node.getBinding(), nextValue, mv);
mv.load(env);
pushLexicalEnvironment(mv);
mv.exitVariableScope();
} else {
// stack: [nextValue] -> []
mv.pop();
}
/* step 6.k */
mv.enterScope(node);
new IterationGenerator<ComprehensionFor>(codegen) {
@Override
protected Completion iterationBody(ComprehensionFor node, Variable<ScriptIterator<?>> iterator,
CodeVisitor mv) {
elements.next().accept(ComprehensionGenerator.this, mv);
return Completion.Normal;
}
@Override
protected MutableValue<Object> enterIteration(ComprehensionFor node, CodeVisitor mv) {
return mv.enterIteration();
}
@Override
protected List<TempLabel> exitIteration(ComprehensionFor node, CodeVisitor mv) {
return mv.exitIteration();
}
}.generate(node, iter, mv);
mv.exitScope();
/* steps 6.l-m */
if (scope.isPresent()) {
popLexicalEnvironment(mv);
}
/* steps 6.a-c */
mv.mark(lblTest);
mv.load(iter);
mv.lineInfo(node);
mv.invoke(Methods.Iterator_hasNext);
mv.ifne(lblLoop);
return null;
}
/**
* Runtime Semantics: ComprehensionComponentEvaluation
* <p>
* ComprehensionFor : for ( ForBinding of AssignmentExpression )
*/
@Override
public Void visit(LegacyComprehensionFor node, CodeVisitor mv) {
Jump lblTest = new Jump(), lblLoop = new Jump(), lblFail = new Jump();
Variable<ScriptIterator<?>> iter = iterators.next();
ValType type = expressionBoxed(node.getExpression(), mv);
if (type != ValType.Object) {
// fail-safe behaviour for null/undefined values in legacy comprehensions
Jump loopstart = new Jump();
mv.dup();
isUndefinedOrNull(mv);
mv.ifeq(loopstart);
mv.pop();
mv.goTo(lblFail);
mv.mark(loopstart);
}
IterationKind iterationKind = node.getIterationKind();
if (iterationKind == IterationKind.Enumerate
|| iterationKind == IterationKind.EnumerateValues) {
// legacy generator mode, both, for-in and for-each, perform Iterate on generators
Jump l0 = new Jump(), l1 = new Jump();
mv.dup();
mv.instanceOf(Types.GeneratorObject);
mv.ifeq(l0);
mv.dup();
mv.checkcast(Types.GeneratorObject);
mv.invoke(Methods.GeneratorObject_isLegacyGenerator);
mv.ifeq(l0);
mv.loadExecutionContext();
mv.lineInfo(node.getExpression());
mv.invoke(Methods.ScriptRuntime_iterate);
mv.goTo(l1);
mv.mark(l0);
mv.loadExecutionContext();
mv.lineInfo(node.getExpression());
if (iterationKind == IterationKind.Enumerate) {
mv.invoke(Methods.ScriptRuntime_enumerate);
} else {
mv.invoke(Methods.ScriptRuntime_enumerateValues);
}
mv.mark(l1);
} else {
assert iterationKind == IterationKind.Iterate;
mv.loadExecutionContext();
mv.lineInfo(node.getExpression());
mv.invoke(Methods.ScriptRuntime_iterate);
}
mv.store(iter);
mv.nonDestructiveGoTo(lblTest);
mv.mark(lblLoop);
mv.load(iter);
mv.lineInfo(node);
mv.invoke(Methods.Iterator_next);
new IterationGenerator<LegacyComprehensionFor>(codegen) {
@Override
protected Completion iterationBody(LegacyComprehensionFor node, Variable<ScriptIterator<?>> iterator,
CodeVisitor mv) {
// stack: [nextValue] -> []
BindingInitialization(codegen, node.getBinding(), mv);
elements.next().accept(ComprehensionGenerator.this, mv);
return Completion.Normal;
}
@Override
protected MutableValue<Object> enterIteration(LegacyComprehensionFor node, CodeVisitor mv) {
return mv.enterIteration();
}
@Override
protected List<TempLabel> exitIteration(LegacyComprehensionFor node, CodeVisitor mv) {
return mv.exitIteration();
}
}.generate(node, iter, mv);
mv.mark(lblTest);
mv.load(iter);
mv.lineInfo(node);
mv.invoke(Methods.Iterator_hasNext);
mv.ifne(lblLoop);
mv.mark(lblFail);
return null;
}
}