/**
* 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.DefaultCodeGenerator.SetFunctionName;
import static com.github.anba.es6draft.compiler.DefaultCodeGenerator.ToPropertyKey;
import static com.github.anba.es6draft.semantics.StaticSemantics.BoundNames;
import static com.github.anba.es6draft.semantics.StaticSemantics.IsAnonymousFunctionDefinition;
import static com.github.anba.es6draft.semantics.StaticSemantics.PropName;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import com.github.anba.es6draft.ast.*;
import com.github.anba.es6draft.ast.scope.Name;
import com.github.anba.es6draft.ast.scope.Scope;
import com.github.anba.es6draft.compiler.DefaultCodeGenerator.ValType;
import com.github.anba.es6draft.compiler.Labels.TempLabel;
import com.github.anba.es6draft.compiler.StatementGenerator.Completion;
import com.github.anba.es6draft.compiler.assembler.FieldName;
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.Value;
import com.github.anba.es6draft.compiler.assembler.Variable;
import com.github.anba.es6draft.runtime.EnvironmentRecord;
import com.github.anba.es6draft.runtime.FunctionEnvironmentRecord;
import com.github.anba.es6draft.runtime.LexicalEnvironment;
import com.github.anba.es6draft.runtime.internal.ScriptIterator;
/**
* <h1>Runtime Semantics: BindingInitialization</h1>
* <ul>
* <li>12.1.5 Runtime Semantics: BindingInitialization
* <li>13.3.3.5 Runtime Semantics: BindingInitialization
* </ul>
*
* <h2>Runtime Semantics: IteratorBindingInitialization</h2>
* <ul>
* <li>13.3.3.6 Runtime Semantics: IteratorBindingInitialization
* <li>14.1.18 Runtime Semantics: IteratorBindingInitialization
* <li>14.2.14 Runtime Semantics: IteratorBindingInitialization
* </ul>
*
* <h2>Runtime Semantics: KeyedBindingInitialization</h2>
* <ul>
* <li>13.3.3.7 Runtime Semantics: KeyedBindingInitialization
* </ul>
*/
final class BindingInitializationGenerator {
private static final class Fields {
static final FieldName Collections_EMPTY_SET = FieldName.findStatic(Types.Collections, "EMPTY_SET", Types.Set);
}
private static final class Methods {
// class: AbstractOperations
static final MethodName AbstractOperations_GetV = MethodName.findStatic(Types.AbstractOperations, "GetV",
Type.methodType(Types.Object, Types.ExecutionContext, Types.Object, Types.Object));
static final MethodName AbstractOperations_GetV_String = MethodName.findStatic(Types.AbstractOperations, "GetV",
Type.methodType(Types.Object, Types.ExecutionContext, Types.Object, Types.String));
static final MethodName AbstractOperations_RequireObjectCoercible = MethodName.findStatic(
Types.AbstractOperations, "RequireObjectCoercible",
Type.methodType(Types.Object, Types.ExecutionContext, Types.Object));
// class: ExecutionContext
static final MethodName ExecutionContext_setVariableAndLexicalEnvironment = MethodName.findVirtual(
Types.ExecutionContext, "setVariableAndLexicalEnvironment",
Type.methodType(Type.VOID_TYPE, Types.LexicalEnvironment));
// class: LexicalEnvironment
static final MethodName LexicalEnvironment_newDeclarativeEnvironment = MethodName.findStatic(
Types.LexicalEnvironment, "newDeclarativeEnvironment",
Type.methodType(Types.LexicalEnvironment, Types.LexicalEnvironment));
// class: ScriptRuntime
static final MethodName ScriptRuntime_createRestArray = MethodName.findStatic(Types.ScriptRuntime,
"createRestArray", Type.methodType(Types.ArrayObject, Types.Iterator, Types.ExecutionContext));
static final MethodName ScriptRuntime_createRestObject = MethodName.findStatic(Types.ScriptRuntime,
"createRestObject",
Type.methodType(Types.OrdinaryObject, Types.Object, Types.Set, Types.ExecutionContext));
static final MethodName ScriptRuntime_iterate = MethodName.findStatic(Types.ScriptRuntime, "iterate",
Type.methodType(Types.ScriptIterator, Types.Object, Types.ExecutionContext));
static final MethodName ScriptRuntime_iteratorNextAndIgnore = MethodName.findStatic(Types.ScriptRuntime,
"iteratorNextAndIgnore", Type.methodType(Type.VOID_TYPE, Types.Iterator));
static final MethodName ScriptRuntime_iteratorNextOrUndefined = MethodName.findStatic(Types.ScriptRuntime,
"iteratorNextOrUndefined", Type.methodType(Types.Object, Types.Iterator));
// class: HashSet
static final MethodName HashSet_init = MethodName.findConstructor(Types.HashSet,
Type.methodType(Type.VOID_TYPE));
static final MethodName HashSet_add = MethodName.findVirtual(Types.HashSet, "add",
Type.methodType(Type.BOOLEAN_TYPE, Types.Object));
}
private BindingInitializationGenerator() {
}
/**
* 12.1.5.1 Runtime Semantics: InitializeBoundName(name, value, environment)
* <p>
* stack: [value] {@literal ->} []
*
* @param node
* the binding identifier
* @param mv
* the code visitor
*/
static <ENVREC extends EnvironmentRecord> void InitializeBoundName(BindingIdentifier node, CodeVisitor mv) {
IdReferenceOp op = IdReferenceOp.of(node);
/* steps 1-2 (not applicable) */
/* step 3 */
// stack: [value] -> [reference, value]
ValType refType = op.resolveBinding(node, mv);
mv.swap(ValType.Any, refType);
// stack: [reference, value] -> []
op.putValue(node, ValType.Any, mv);
}
/**
* 12.1.5.1 Runtime Semantics: InitializeBoundName(name, value, environment)
* <p>
* stack: [] {@literal ->} []
*
* @param envRec
* the environment record
* @param name
* the binding name
* @param value
* the value
* @param mv
* the code visitor
*/
static <ENVREC extends EnvironmentRecord> void InitializeBoundName(Variable<? extends ENVREC> envRec, Name name,
Value<?> value, CodeVisitor mv) {
BindingOp<ENVREC> op = BindingOp.of(envRec, name);
op.initializeBinding(envRec, name, value, mv);
}
/**
* 12.1.5.1 Runtime Semantics: InitializeBoundName(name, value, environment)
* <p>
* stack: [] {@literal ->} []
*
* @param envRec
* the environment record
* @param name
* the binding name
* @param mv
* the code visitor
*/
static <ENVREC extends EnvironmentRecord> void InitializeBoundNameWithUndefined(Variable<? extends ENVREC> envRec,
Name name, CodeVisitor mv) {
InitializeBoundName(envRec, name, mv.undefinedValue(), mv);
}
/**
* 12.1.5.1 Runtime Semantics: InitializeBoundName(name, value, environment)
* <p>
* stack: [] {@literal ->} []
*
* @param mv
* the code visitor
*/
static <ENVREC extends EnvironmentRecord> void InitializeBoundNameWithInitializer(CodeGenerator codegen,
Variable<? extends ENVREC> envRec, Name name, Expression initializer, CodeVisitor mv) {
InitializeBoundName(envRec, name, asm -> {
codegen.expressionBoxed(initializer, mv);
if (IsAnonymousFunctionDefinition(initializer)) {
SetFunctionName(initializer, name, mv);
}
}, mv);
}
/**
* 12.1.5 Runtime Semantics: BindingInitialization<br>
* 13.3.3.5 Runtime Semantics: BindingInitialization
* <p>
* stack: [value] {@literal ->} []
*
* @param codegen
* the code generator
* @param node
* the binding node
* @param mv
* the code visitor
*/
static void BindingInitialization(CodeGenerator codegen, Binding node, CodeVisitor mv) {
if (node instanceof BindingIdentifier) {
InitializeBoundName((BindingIdentifier) node, mv);
} else {
BindingInitialization(codegen, (BindingPattern) node, mv);
}
}
/**
* 13.3.3.5 Runtime Semantics: BindingInitialization
* <p>
* stack: [value] {@literal ->} []
*
* @param codegen
* the code generator
* @param node
* the binding node
* @param mv
* the code visitor
*/
static void BindingInitialization(CodeGenerator codegen, BindingPattern node, CodeVisitor mv) {
BindingInitialization init = new BindingInitialization(codegen, mv, null);
node.accept(init, null);
}
/**
* 12.1.5 Runtime Semantics: BindingInitialization<br>
* 13.3.3.5 Runtime Semantics: BindingInitialization
* <p>
* stack: [] {@literal ->} []
*
* @param codegen
* the code generator
* @param envRec
* the environment record
* @param node
* the binding node
* @param value
* the value
* @param mv
* the code visitor
*/
static <ENVREC extends EnvironmentRecord> void BindingInitialization(CodeGenerator codegen,
Variable<? extends ENVREC> envRec, Binding node, Value<?> value, CodeVisitor mv) {
if (node instanceof BindingIdentifier) {
InitializeBoundName(envRec, ((BindingIdentifier) node).getName(), value, mv);
} else {
BindingInitialization(codegen, envRec, (BindingPattern) node, value, mv);
}
}
/**
* 13.3.3.5 Runtime Semantics: BindingInitialization
* <p>
* stack: [] {@literal ->} []
*
* @param codegen
* the code generator
* @param envRec
* the environment record
* @param node
* the binding node
* @param value
* the value
* @param mv
* the code visitor
*/
static <ENVREC extends EnvironmentRecord> void BindingInitialization(CodeGenerator codegen,
Variable<? extends ENVREC> envRec, BindingPattern node, Value<?> value, CodeVisitor mv) {
mv.load(value);
BindingInitialization init = new BindingInitialization(codegen, mv, envRec);
node.accept(init, null);
}
/**
* 13.3.3.5 Runtime Semantics: BindingInitialization
* <p>
* stack: [value] {@literal ->} []
*
* @param codegen
* the code generator
* @param envRec
* the environment record
* @param node
* the binding node
* @param mv
* the code visitor
*/
static <ENVREC extends EnvironmentRecord> void BindingInitialization(CodeGenerator codegen,
Variable<? extends ENVREC> envRec, BindingPattern node, CodeVisitor mv) {
BindingInitialization init = new BindingInitialization(codegen, mv, envRec);
node.accept(init, null);
}
/**
* 14.1.18 Runtime Semantics: IteratorBindingInitialization
* <p>
* stack: [] {@literal ->} []
*
* @param codegen
* the code generator
* @param node
* the function node
* @param env
* the current lexical and variable environment
* @param iterator
* the arguments iterator
* @param mv
* the code visitor
*/
static void BindingInitialization(CodeGenerator codegen, FunctionNode node,
Variable<LexicalEnvironment<FunctionEnvironmentRecord>> env, Variable<Iterator<?>> iterator,
CodeVisitor mv) {
FormalsIteratorBindingInitialization init = new FormalsIteratorBindingInitialization(
codegen, mv, env, null);
node.getParameters().accept(init, iterator);
}
/**
* 14.1.18 Runtime Semantics: IteratorBindingInitialization
* <p>
* stack: [] {@literal ->} []
*
* @param codegen
* the code generator
* @param node
* the function node
* @param env
* the current lexical and variable environment
* @param envRec
* the current environment record
* @param iterator
* the arguments iterator
* @param mv
* the code visitor
*/
static void BindingInitialization(CodeGenerator codegen, FunctionNode node,
Variable<LexicalEnvironment<FunctionEnvironmentRecord>> env, Variable<? extends EnvironmentRecord> envRec,
Variable<Iterator<?>> iterator, CodeVisitor mv) {
FormalsIteratorBindingInitialization init = new FormalsIteratorBindingInitialization(
codegen, mv, env, envRec);
node.getParameters().accept(init, iterator);
}
private static abstract class RuntimeSemantics<V> extends
com.github.anba.es6draft.ast.DefaultVoidNodeVisitor<V> {
protected final CodeGenerator codegen;
protected final CodeVisitor mv;
protected final Variable<? extends EnvironmentRecord> envRec;
RuntimeSemantics(CodeGenerator codegen, CodeVisitor mv, Variable<? extends EnvironmentRecord> envRec) {
this.codegen = codegen;
this.mv = mv;
this.envRec = envRec;
}
protected final void BindingInitialization(BindingPattern node) {
node.accept(new BindingInitialization(codegen, mv, envRec), null);
}
protected final void IteratorBindingInitialization(ArrayBindingPattern node,
Variable<? extends Iterator<?>> iterator) {
node.accept(new IteratorBindingInitialization(codegen, mv, envRec), iterator);
}
protected final void KeyedBindingInitialization(BindingProperty node, String key, Variable<Object> value,
Variable<HashSet<?>> propertyNames) {
node.accept(new LiteralKeyedBindingInitialization(codegen, mv, envRec, value, propertyNames), key);
}
protected final void KeyedBindingInitialization(BindingProperty node, ComputedPropertyName key,
Variable<Object> value, Variable<HashSet<?>> propertyNames) {
node.accept(new ComputedKeyedBindingInitialization(codegen, mv, envRec, value, propertyNames), key);
}
@Override
protected final void visit(Node node, V value) {
throw new IllegalStateException();
}
protected final ValType expression(Expression node, CodeVisitor mv) {
return codegen.expression(node, mv);
}
protected final ValType expressionBoxed(Expression node, CodeVisitor mv) {
return codegen.expressionBoxed(node, mv);
}
protected final void emitDefaultInitializer(Expression initializer) {
// stack: [value] -> [value']
Jump undef = new Jump();
mv.dup();
mv.loadUndefined();
mv.ifacmpne(undef);
{
mv.pop();
expressionBoxed(initializer, mv);
}
mv.mark(undef);
}
protected final void emitDefaultInitializer(Expression initializer,
BindingIdentifier bindingId) {
// stack: [value] -> [value']
Jump undef = new Jump();
mv.dup();
mv.loadUndefined();
mv.ifacmpne(undef);
{
mv.pop();
expressionBoxed(initializer, mv);
if (IsAnonymousFunctionDefinition(initializer)) {
SetFunctionName(initializer, bindingId.getName(), mv);
}
}
mv.mark(undef);
}
}
/**
* <h1>Runtime Semantics: BindingInitialization</h1>
* <ul>
* <li>13.3.3.5 Runtime Semantics: BindingInitialization
* </ul>
*/
private static final class BindingInitialization extends RuntimeSemantics<Void> {
BindingInitialization(CodeGenerator codegen, CodeVisitor mv, Variable<? extends EnvironmentRecord> envRec) {
super(codegen, mv, envRec);
}
@Override
public void visit(ArrayBindingPattern node, Void value) {
// steps 1-3:
// stack: [value] -> []
mv.enterVariableScope();
Variable<ScriptIterator<?>> iterator = mv.newVariable("iterator", ScriptIterator.class)
.uncheckedCast();
mv.loadExecutionContext();
mv.lineInfo(node);
mv.invoke(Methods.ScriptRuntime_iterate);
mv.store(iterator);
new IterationGenerator<ArrayBindingPattern>(codegen) {
@Override
protected Completion iterationBody(ArrayBindingPattern node, Variable<ScriptIterator<?>> iterator,
CodeVisitor mv) {
// step 4
IteratorBindingInitialization(node, iterator);
return Completion.Normal;
}
@Override
protected void epilogue(ArrayBindingPattern node, Variable<ScriptIterator<?>> iterator,
CodeVisitor mv) {
// step 5
IteratorClose(node, iterator, mv);
}
@Override
protected MutableValue<Object> enterIteration(ArrayBindingPattern node, CodeVisitor mv) {
return mv.enterIteration();
}
@Override
protected List<TempLabel> exitIteration(ArrayBindingPattern node, CodeVisitor mv) {
return mv.exitIteration();
}
}.generate(node, iterator, mv);
mv.exitVariableScope();
}
@Override
public void visit(ObjectBindingPattern node, Void value) {
// stack: [value] -> [value]
mv.loadExecutionContext();
mv.swap();
mv.lineInfo(node);
mv.invoke(Methods.AbstractOperations_RequireObjectCoercible);
if (node.getProperties().isEmpty() && node.getRest() == null) {
// stack: [value] -> []
mv.pop();
return;
}
// stack: [value] -> []
mv.enterVariableScope();
Variable<Object> val = mv.newVariable("value", Object.class);
mv.store(val);
Variable<HashSet<?>> propertyNames;
if (!node.getProperties().isEmpty() && node.getRest() != null) {
propertyNames = mv.newVariable("propertyNames", HashSet.class).uncheckedCast();
mv.anew(Types.HashSet, Methods.HashSet_init);
mv.store(propertyNames);
} else {
propertyNames = null;
}
// step 1: [...]
for (BindingProperty property : node.getProperties()) {
if (property.getPropertyName() == null) {
// BindingProperty : SingleNameBinding
Name name = BoundNames(property.getBinding()).get(0);
KeyedBindingInitialization(property, name.getIdentifier(), val, propertyNames);
} else {
// BindingProperty : PropertyName : BindingElement
String name = PropName(property.getPropertyName());
if (name != null) {
KeyedBindingInitialization(property, name, val, propertyNames);
} else {
PropertyName propertyName = property.getPropertyName();
assert propertyName instanceof ComputedPropertyName;
KeyedBindingInitialization(property, (ComputedPropertyName) propertyName, val, propertyNames);
}
}
}
BindingRestProperty rest = node.getRest();
if (rest != null) {
// FIXME: spec bug? - evaluation order for resolving ref and CopyDataProperties
// The current spec calls CopyDataProperties before resolving the reference.
BindingIdentifier identifier = rest.getBindingIdentifier();
if (envRec == null) {
// stack: [] -> [ref]
IdReferenceOp op = IdReferenceOp.of(identifier);
op.resolveBinding(identifier, mv);
// stack: [ref] -> [ref, object]
emitCreateRestObject(node, val, propertyNames);
// stack: [ref, object] -> []
op.putValue(identifier, ValType.Any, mv);
} else {
BindingOp<EnvironmentRecord> op = BindingOp.of(envRec, identifier.getName());
op.initializeBinding(envRec, identifier.getName(),
asm -> emitCreateRestObject(node, val, propertyNames), mv);
}
}
mv.exitVariableScope();
}
private void emitCreateRestObject(ObjectBindingPattern node, Variable<Object> val,
Variable<HashSet<?>> propertyNames) {
// stack: [] -> [object]
mv.load(val);
if (propertyNames != null) {
mv.load(propertyNames);
} else {
mv.get(Fields.Collections_EMPTY_SET);
}
mv.loadExecutionContext();
mv.lineInfo(node.getRest());
mv.invoke(Methods.ScriptRuntime_createRestObject);
}
}
/**
* <h2>Runtime Semantics: IteratorBindingInitialization</h2>
* <ul>
* <li>13.3.3.6 Runtime Semantics: IteratorBindingInitialization
* <li>14.1.18 Runtime Semantics: IteratorBindingInitialization
* <li>14.2.14 Runtime Semantics: IteratorBindingInitialization
* </ul>
*/
private static final class FormalsIteratorBindingInitialization extends
RuntimeSemantics<Variable<? extends Iterator<?>>> {
private final Variable<LexicalEnvironment<FunctionEnvironmentRecord>> env;
private final IteratorBindingInitialization iteratorBindingInit;
FormalsIteratorBindingInitialization(CodeGenerator codegen, CodeVisitor mv,
Variable<LexicalEnvironment<FunctionEnvironmentRecord>> env,
Variable<? extends EnvironmentRecord> envRec) {
super(codegen, mv, envRec);
this.env = env;
this.iteratorBindingInit = new IteratorBindingInitialization(codegen, mv, envRec);
}
@Override
public void visit(FormalParameterList node, Variable<? extends Iterator<?>> iterator) {
for (FormalParameter formal : node) {
formal.accept(this, iterator);
}
}
@Override
public void visit(FormalParameter node, Variable<? extends Iterator<?>> iterator) {
Scope scope = node.getScope();
if (scope != null) {
mv.enterScope(node);
}
if (scope == null || !scope.isPresent()) {
/* step 1 (+ optimization if no direct eval present in formal parameter) */
node.getElement().accept(iteratorBindingInit, iterator);
} else {
/* steps 2-5 (not applicable) */
/* steps 6-8 */
newParameterEnvironment(env);
/* step 9 */
node.getElement().accept(iteratorBindingInit, iterator);
/* steps 10-11 */
setVariableAndLexicalEnvironment(env);
/* step 12 (return) */
}
if (scope != null) {
mv.exitScope();
}
}
private void newParameterEnvironment(Variable<? extends LexicalEnvironment<?>> env) {
// stack: [] -> []
mv.loadExecutionContext();
mv.load(env);
mv.invoke(Methods.LexicalEnvironment_newDeclarativeEnvironment);
mv.invoke(Methods.ExecutionContext_setVariableAndLexicalEnvironment);
}
private void setVariableAndLexicalEnvironment(Variable<? extends LexicalEnvironment<?>> env) {
// stack: [] -> []
mv.loadExecutionContext();
mv.load(env);
mv.invoke(Methods.ExecutionContext_setVariableAndLexicalEnvironment);
}
}
/**
* <h2>Runtime Semantics: IteratorBindingInitialization</h2>
* <ul>
* <li>13.3.3.6 Runtime Semantics: IteratorBindingInitialization
* </ul>
*/
private static final class IteratorBindingInitialization extends
RuntimeSemantics<Variable<? extends Iterator<?>>> {
IteratorBindingInitialization(CodeGenerator codegen, CodeVisitor mv,
Variable<? extends EnvironmentRecord> envRec) {
super(codegen, mv, envRec);
}
@Override
public void visit(ArrayBindingPattern node, Variable<? extends Iterator<?>> iterator) {
for (BindingElementItem element : node.getElements()) {
element.accept(this, iterator);
}
}
@Override
public void visit(BindingElision node, Variable<? extends Iterator<?>> iterator) {
mv.load(iterator);
mv.lineInfo(node);
mv.invoke(Methods.ScriptRuntime_iteratorNextAndIgnore);
}
@Override
public void visit(BindingElement node, Variable<? extends Iterator<?>> iterator) {
Binding binding = node.getBinding();
Expression initializer = node.getInitializer();
if (binding instanceof BindingPattern) {
// BindingElement : BindingPattern Initializer{opt}
/* steps 1-2 */
emitIteratorNext(node, iterator);
/* step 3 */
// stack: [v] -> [v']
if (initializer != null) {
emitDefaultInitializer(initializer);
}
/* step 4 */
// stack: [v'] -> []
BindingInitialization((BindingPattern) binding);
return;
}
// BindingElement : SingleNameBinding
// SingleNameBinding : BindingIdentifier Initializer{opt}
assert binding instanceof BindingIdentifier;
if (envRec == null) {
/* step 1 */
BindingIdentifier bindingId = (BindingIdentifier) binding;
/* steps 2-3 */
// stack: [] -> [ref]
IdReferenceOp op = IdReferenceOp.of(bindingId);
op.resolveBinding(bindingId, mv);
/* steps 4-5 */
emitIteratorNext(node, iterator);
/* step 6 */
// stack: [ref, v] -> [ref, v']
if (initializer != null) {
emitDefaultInitializer(initializer, bindingId);
}
/* steps 7-8 */
// stack: [ref, v'] -> []
op.putValue(bindingId, ValType.Any, mv);
} else {
/* step 1 */
BindingIdentifier bindingId = (BindingIdentifier) binding;
/* steps 2-3, 7-8 */
BindingOp<EnvironmentRecord> op = BindingOp.of(envRec, bindingId.getName());
op.initializeBinding(envRec, bindingId.getName(), asm -> {
/* steps 4-5 */
emitIteratorNext(node, iterator);
/* step 6 */
// stack: [<env, id>|ref, v] -> [<env, id>|ref, v']
if (initializer != null) {
emitDefaultInitializer(initializer, bindingId);
}
}, mv);
}
}
private void emitIteratorNext(BindingElement node, Variable<? extends Iterator<?>> iterator) {
mv.load(iterator);
mv.lineInfo(node);
mv.invoke(Methods.ScriptRuntime_iteratorNextOrUndefined);
}
@Override
public void visit(BindingRestElement node, Variable<? extends Iterator<?>> iterator) {
Binding binding = node.getBinding();
if (binding instanceof BindingPattern) {
// BindingRestElement : ... BindingPattern
// stack: [] -> [array]
emitCreateRestArray(node, iterator);
// stack: [array] -> []
BindingInitialization((BindingPattern) binding);
return;
}
// BindingRestElement : ... BindingIdentifier
BindingIdentifier identifier = (BindingIdentifier) binding;
if (envRec == null) {
/* steps 1-2 */
// stack: [] -> [ref]
IdReferenceOp op = IdReferenceOp.of(identifier);
op.resolveBinding(identifier, mv);
/* steps 3-5 */
// stack: [ref] -> [ref, array]
emitCreateRestArray(node, iterator);
/* step 5.b */
// stack: [ref, array] -> []
op.putValue(identifier, ValType.Any, mv);
} else {
/* steps 1-2, 5.b */
BindingOp<EnvironmentRecord> op = BindingOp.of(envRec, identifier.getName());
op.initializeBinding(envRec, identifier.getName(), asm -> {
/* steps 3-5 */
emitCreateRestArray(node, iterator);
}, mv);
}
}
private void emitCreateRestArray(BindingRestElement node, Variable<? extends Iterator<?>> iterator) {
// stack: [] -> [array]
mv.load(iterator);
mv.loadExecutionContext();
mv.lineInfo(node);
mv.invoke(Methods.ScriptRuntime_createRestArray);
}
}
/**
* <h2>Runtime Semantics: KeyedBindingInitialization</h2>
* <ul>
* <li>13.3.3.7 Runtime Semantics: KeyedBindingInitialization
* </ul>
*/
private static abstract class KeyedBindingInitialization<PROPERTYNAME> extends
RuntimeSemantics<PROPERTYNAME> {
private final Variable<Object> value;
KeyedBindingInitialization(CodeGenerator codegen, CodeVisitor mv, Variable<? extends EnvironmentRecord> envRec,
Variable<Object> value) {
super(codegen, mv, envRec);
this.value = value;
}
abstract ValType evaluatePropertyName(PROPERTYNAME propertyName);
abstract boolean isSimplePropertyName(PROPERTYNAME propertyName);
final boolean isSimplePropertyNameOrTarget(BindingIdentifier target,
PROPERTYNAME propertyName) {
if (isSimplePropertyName(propertyName)) {
return true;
}
Name resolvedName = target.getResolvedName();
return resolvedName != null && resolvedName.isLocal();
}
final void emitGetV(BindingProperty node, ValType type) {
// stack: [cx, value, propertyName] -> [v]
mv.lineInfo(node);
if (type == ValType.String) {
mv.invoke(Methods.AbstractOperations_GetV_String);
} else {
mv.invoke(Methods.AbstractOperations_GetV);
}
}
@Override
public void visit(BindingProperty node, PROPERTYNAME propertyName) {
Binding binding = node.getBinding();
Expression initializer = node.getInitializer();
if (binding instanceof BindingPattern) {
// stack: [] -> [cx, value]
mv.loadExecutionContext();
mv.load(value);
/* steps 1-2 (Runtime Semantics: BindingInitialization 13.3.3.5) */
// stack: [cx, value] -> [cx, value, propertyName]
ValType type = evaluatePropertyName(propertyName);
/* steps 1-2 */
// stack: [cx, value, propertyName] -> [v]
emitGetV(node, type);
/* step 3 */
// stack: [v] -> [v']
if (initializer != null) {
emitDefaultInitializer(initializer);
}
/* step 4 */
// stack: [v'] -> []
BindingInitialization((BindingPattern) binding);
return;
}
assert binding instanceof BindingIdentifier;
if (envRec == null) {
/* step 1 */
BindingIdentifier bindingId = (BindingIdentifier) binding;
IdReferenceOp op = IdReferenceOp.of(bindingId);
ValType type;
if (isSimplePropertyNameOrTarget(bindingId, propertyName)) {
/* steps 2-3 */
// stack: [] -> [ref]
op.resolveBinding(bindingId, mv);
// stack: [ref] -> [ref, cx, value]
mv.loadExecutionContext();
mv.load(value);
/* steps 1-2 (Runtime Semantics: BindingInitialization 13.3.3.5) */
// stack: [ref, cx, value] -> [ref, cx, value, propertyName]
type = evaluatePropertyName(propertyName);
} else {
/* steps 1-2 (Runtime Semantics: BindingInitialization 13.3.3.5) */
// stack: [] -> []
type = evaluatePropertyName(propertyName);
Variable<?> propertyNameVar = mv.newScratchVariable(type.toClass());
mv.store(propertyNameVar);
/* steps 2-3 */
// stack: [] -> [ref]
op.resolveBinding(bindingId, mv);
// stack: [ref] -> [ref, cx, value, propertyName]
mv.loadExecutionContext();
mv.load(value);
mv.load(propertyNameVar);
mv.freeVariable(propertyNameVar);
}
/* steps 4-5 */
// stack: [ref, cx, value, propertyName] -> [ref, v]
emitGetV(node, type);
/* step 6 */
// stack: [ref, v] -> [ref, v']
if (initializer != null) {
emitDefaultInitializer(initializer, bindingId);
}
/* steps 7-8 */
// stack: [ref, v'] -> []
op.putValue(bindingId, ValType.Any, mv);
} else {
/* step 1 */
BindingIdentifier bindingId = (BindingIdentifier) binding;
/* steps 2-3, 7-8 */
BindingOp<EnvironmentRecord> op = BindingOp.of(envRec, bindingId.getName());
op.initializeBinding(envRec, bindingId.getName(), asm -> {
// stack: [] -> [cx, value]
mv.loadExecutionContext();
mv.load(value);
/* steps 1-2 (Runtime Semantics: BindingInitialization 13.3.3.5) */
// stack: [cx, value] -> [cx, value, propertyName]
ValType type = evaluatePropertyName(propertyName);
/* steps 4-5 */
// stack: [cx, value, propertyName] -> [v]
emitGetV(node, type);
/* step 6 */
// stack: [v] -> [v']
if (initializer != null) {
emitDefaultInitializer(initializer, bindingId);
}
}, mv);
}
}
}
/**
* <h2>Runtime Semantics: KeyedBindingInitialization</h2>
* <ul>
* <li>13.3.3.7 Runtime Semantics: KeyedBindingInitialization
* </ul>
*/
private static final class LiteralKeyedBindingInitialization extends
KeyedBindingInitialization<String> {
private final Variable<HashSet<?>> propertyNames;
LiteralKeyedBindingInitialization(CodeGenerator codegen, CodeVisitor mv,
Variable<? extends EnvironmentRecord> envRec, Variable<Object> value,
Variable<HashSet<?>> propertyNames) {
super(codegen, mv, envRec, value);
this.propertyNames = propertyNames;
}
@Override
ValType evaluatePropertyName(String propertyName) {
if (propertyNames != null) {
mv.load(propertyNames);
mv.aconst(propertyName);
mv.invoke(Methods.HashSet_add);
mv.pop();
}
mv.aconst(propertyName);
return ValType.String;
}
@Override
boolean isSimplePropertyName(String propertyName) {
return true;
}
}
/**
* <h2>Runtime Semantics: KeyedBindingInitialization</h2>
* <ul>
* <li>13.3.3.7 Runtime Semantics: KeyedBindingInitialization
* </ul>
*/
private static final class ComputedKeyedBindingInitialization extends
KeyedBindingInitialization<ComputedPropertyName> {
private final Variable<HashSet<?>> propertyNames;
ComputedKeyedBindingInitialization(CodeGenerator codegen, CodeVisitor mv,
Variable<? extends EnvironmentRecord> envRec, Variable<Object> value,
Variable<HashSet<?>> propertyNames) {
super(codegen, mv, envRec, value);
this.propertyNames = propertyNames;
}
@Override
ValType evaluatePropertyName(ComputedPropertyName propertyName) {
if (propertyNames != null) {
mv.load(propertyNames);
}
// Runtime Semantics: Evaluation
// ComputedPropertyName : [ AssignmentExpression ]
ValType propType = expression(propertyName.getExpression(), mv);
ValType keyType = ToPropertyKey(propType, mv);
if (propertyNames != null) {
mv.dupX1();
mv.invoke(Methods.HashSet_add);
mv.pop();
}
return keyType;
}
@Override
boolean isSimplePropertyName(ComputedPropertyName propertyName) {
return propertyName.getExpression() instanceof Literal;
}
}
}