/**
* 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.ToPropertyKey;
import com.github.anba.es6draft.ast.ElementAccessor;
import com.github.anba.es6draft.ast.Expression;
import com.github.anba.es6draft.ast.IdentifierReference;
import com.github.anba.es6draft.ast.LeftHandSideExpression;
import com.github.anba.es6draft.ast.Literal;
import com.github.anba.es6draft.ast.PropertyAccessor;
import com.github.anba.es6draft.ast.StringLiteral;
import com.github.anba.es6draft.ast.SuperElementAccessor;
import com.github.anba.es6draft.ast.SuperPropertyAccessor;
import com.github.anba.es6draft.compiler.DefaultCodeGenerator.ValType;
import com.github.anba.es6draft.compiler.assembler.Jump;
import com.github.anba.es6draft.compiler.assembler.MethodName;
import com.github.anba.es6draft.compiler.assembler.Type;
import com.github.anba.es6draft.compiler.assembler.Variable;
/**
*
*/
abstract class ReferenceOp<NODE extends LeftHandSideExpression> {
/**
* Evaluates {@code node} and pushes the resolved reference object on the stack.
* <p>
* stack: [] -> [{@literal <reference>}]
*
* @param node
* the reference node
* @param mv
* the code visitor
* @param gen
* the code generator
* @return the reference value type
*/
final ValType reference(NODE node, CodeVisitor mv, CodeGenerator gen) {
return reference(node, false, mv, gen);
}
/**
* Evaluates {@code node} and pushes two copies of the resolved reference object on the stack.
* <p>
* stack: [] -> [{@literal <reference>}, {@literal <reference>}]
*
* @param node
* the reference node
* @param mv
* the code visitor
* @param gen
* the code generator
* @return the reference value type
*/
final ValType referenceForUpdate(NODE node, CodeVisitor mv, CodeGenerator gen) {
return reference(node, true, mv, gen);
}
/**
* Evaluates {@code node} and pushes the resolved reference object on the stack.
* <p>
* stack: [] -> [{@literal <reference>}]<br>
* or: [] -> [{@literal <reference>}, {@literal <reference>}]
*
* @param node
* the reference node
* @param update
* if {@code true} duplicate reference
* @param mv
* the code visitor
* @param gen
* the code generator
* @return the reference value type
*/
protected abstract ValType reference(NODE node, boolean update, CodeVisitor mv, CodeGenerator gen);
/**
* Retrieves the reference's value.
* <p>
* stack: [{@literal <reference>}] -> [{@literal <value>}]
*
* @param node
* the reference node
* @param ref
* the reference value type
* @param mv
* the code visitor
* @return the stack top value or empty
*/
abstract ValType getValue(NODE node, ValType ref, CodeVisitor mv);
/**
* Assigns a new value to the reference.
* <p>
* stack: [{@literal <reference>}, {@literal <value>}] -> []
*
* @param node
* the reference node
* @param ref
* the reference value type
* @param value
* the top stack value type
* @param mv
* the code visitor
*/
abstract void putValue(NODE node, ValType ref, ValType value, CodeVisitor mv);
/**
* Assigns a new value to the reference.
* <p>
* stack: [{@literal <reference>}, {@literal <value>}] -> []<br>
* or: [{@literal <reference>}, {@literal <value>}] -> [{@literal <value>}]
*
* @param node
* the reference node
* @param ref
* the reference value type
* @param value
* the top stack value type
* @param completion
* if {@code true} keep a copy of the new value on the stack
* @param mv
* the code visitor
*/
final ValType putValue(NODE node, ValType ref, ValType value, boolean completion, CodeVisitor mv) {
if (completion) {
Variable<?> saved = saveValue(ref, value, mv);
putValue(node, ref, value, mv);
restoreValue(saved, mv);
return value;
}
putValue(node, ref, value, mv);
return ValType.Empty;
}
/**
* Evaluates {@code node} and pushes the resolved reference object on the stack.
* <p>
* stack: [] -> []
*
* @param node
* the reference node
* @param mv
* the code visitor
* @param gen
* the code generator
* @return the stack top value or empty
*/
abstract ValType delete(NODE node, CodeVisitor mv, CodeGenerator gen);
/**
* Evaluates {@code node} and pushes the reference value on the stack.
* <p>
* stack: [] -> [{@literal <value>}]
*
* @param node
* the reference node
* @param mode
* the reference operation mode
* @param mv
* the code visitor
* @param gen
* the code generator
* @return the reference value type
*/
final ValType referenceValue(NODE node, CodeVisitor mv, CodeGenerator gen) {
return referenceValue(node, false, mv, gen);
}
/**
* Evaluates {@code node} and pushes the reference value on the stack.
* <p>
* stack: [] -> [{@literal <value>}, {@literal <thisValue>}]
*
* @param node
* the reference node
* @param mode
* the reference operation mode
* @param mv
* the code visitor
* @param gen
* the code generator
* @return the reference value type
*/
final ValType referenceValueAndThis(NODE node, CodeVisitor mv, CodeGenerator gen) {
return referenceValue(node, true, mv, gen);
}
/**
* Evaluates {@code node} and pushes the reference value on the stack.
* <p>
* stack: [] -> [{@literal <value>}]<br>
* or: [] -> [{@literal <value>}, {@literal <thisValue>}]
*
* @param node
* the reference node
* @param withThis
* if {@code true} pushes this-value
* @param mv
* the code visitor
* @param gen
* the code generator
* @return the reference value type
*/
protected abstract ValType referenceValue(NODE node, boolean withThis, CodeVisitor mv, CodeGenerator gen);
/**
* Saves the top stack value.
* <p>
* stack: [{@literal <reference>}, {@literal <value>}] -> [{@literal <value>}, {@literal
* <reference>}, {@literal <value>}]<br>
* or: [{@literal <reference>}, {@literal <value>}] -> [{@literal <reference>}, {@literal
* <value>}] and returns a non-null {@link Variable} object.
*
* @param ref
* the reference value type
* @param value
* the top stack value type
* @param mv
* the code visitor
* @return the variable to hold the value or {@code null} if saved on stack
*/
abstract Variable<?> saveValue(ValType ref, ValType value, CodeVisitor mv);
/**
* Restores the top stack value. This operation is a no-op if <var>variable</var> is
* {@code null}.
* <p>
* stack: [] -> [{@literal <value>}]
*
* @param variable
* the variable or {@code null}
* @param mv
* the code visitor
*/
abstract void restoreValue(Variable<?> variable, CodeVisitor mv);
/**
* Returns the {@code ReferenceOp} implementation for the left-hand side expression.
*
* @param lhs
* the left-hand side expression
* @return the {@code ReferenceOp}
*/
@SuppressWarnings("unchecked")
public static <NODE extends LeftHandSideExpression> ReferenceOp<NODE> of(NODE lhs) {
if (lhs instanceof IdentifierReference) {
return (ReferenceOp<NODE>) of((IdentifierReference) lhs);
}
return propertyOp(lhs);
}
/**
* Returns the {@code ReferenceOp} implementation for the identifier reference.
*
* @param lhs
* the identifier reference
* @return the {@code ReferenceOp}
*/
public static ReferenceOp<IdentifierReference> of(IdentifierReference lhs) {
return ReferenceOp.LOOKUP;
}
/**
* Returns the {@code ReferenceOp} implementation for the property accessor expression.
*
* @param lhs
* the property accessor expression
* @return the {@code ReferenceOp}
*/
@SuppressWarnings("unchecked")
public static <NODE extends LeftHandSideExpression> ReferenceOp<NODE> propertyOp(NODE lhs) {
if (lhs instanceof ElementAccessor) {
return (ReferenceOp<NODE>) ReferenceOp.ELEMENT;
}
if (lhs instanceof PropertyAccessor) {
return (ReferenceOp<NODE>) ReferenceOp.PROPERTY;
}
if (lhs instanceof SuperElementAccessor) {
return (ReferenceOp<NODE>) ReferenceOp.SUPER_ELEMENT;
}
if (lhs instanceof SuperPropertyAccessor) {
return (ReferenceOp<NODE>) ReferenceOp.SUPER_PROPERTY;
}
throw new AssertionError();
}
private static final class Methods {
// EnvironmentRecord
static final MethodName EnvironmentRecord_withBaseObject = MethodName.findInterface(
Types.EnvironmentRecord, "withBaseObject", Type.methodType(Types.ScriptObject));
// class: Reference
static final MethodName Reference_getValue = MethodName.findVirtual(Types.Reference,
"getValue", Type.methodType(Types.Object, Types.ExecutionContext));
static final MethodName Reference_putValue = MethodName.findVirtual(Types.Reference,
"putValue", Type.methodType(Type.VOID_TYPE, Types.Object, Types.ExecutionContext));
static final MethodName Reference_delete = MethodName.findVirtual(Types.Reference, "delete",
Type.methodType(Type.BOOLEAN_TYPE, Types.ExecutionContext));
static final MethodName Reference_getBase = MethodName.findVirtual(Types.Reference,
"getBase", Type.methodType(Types.Object));
// class: ScriptRuntime
static final MethodName ScriptRuntime_GetSuperEnvironmentRecord = MethodName.findStatic(Types.ScriptRuntime,
"GetSuperEnvironmentRecord", Type.methodType(Types.FunctionEnvironmentRecord, Types.ExecutionContext));
static final MethodName ScriptRuntime_GetSuperThis = MethodName.findStatic(Types.ScriptRuntime, "GetSuperThis",
Type.methodType(Types.Object, Types.FunctionEnvironmentRecord, Types.ExecutionContext));
static final MethodName ScriptRuntime_GetSuperBase = MethodName.findStatic(Types.ScriptRuntime, "GetSuperBase",
Type.methodType(Types.ScriptObject, Types.FunctionEnvironmentRecord, Types.ExecutionContext));
static final MethodName ScriptRuntime_getSuperProperty = MethodName.findStatic(Types.ScriptRuntime,
"getSuperProperty",
Type.methodType(Types.Object, Types.Object, Types.Object, Types.ScriptObject, Types.ExecutionContext));
static final MethodName ScriptRuntime_getSuperProperty_String = MethodName.findStatic(Types.ScriptRuntime,
"getSuperProperty",
Type.methodType(Types.Object, Types.String, Types.Object, Types.ScriptObject, Types.ExecutionContext));
static final MethodName ScriptRuntime_setSuperProperty = MethodName.findStatic(Types.ScriptRuntime,
"setSuperProperty", Type.methodType(Type.VOID_TYPE, Types.Object, Types.Object, Types.ScriptObject,
Types.Object, Types.ExecutionContext, Type.BOOLEAN_TYPE));
static final MethodName ScriptRuntime_setSuperProperty_String = MethodName.findStatic(Types.ScriptRuntime,
"setSuperProperty", Type.methodType(Type.VOID_TYPE, Types.String, Types.Object, Types.ScriptObject,
Types.Object, Types.ExecutionContext, Type.BOOLEAN_TYPE));
static final MethodName ScriptRuntime_deleteSuperProperty = MethodName.findStatic(Types.ScriptRuntime,
"deleteSuperProperty", Type.methodType(Type.BOOLEAN_TYPE, Types.ExecutionContext));
// ScriptRuntime#checkAccessProperty
static final MethodName ScriptRuntime_checkAccessElement = MethodName.findStatic(
Types.ScriptRuntime, "checkAccessElement",
Type.methodType(Types.Object, Types.Object, Types.Object, Types.ExecutionContext));
static final MethodName ScriptRuntime_checkAccessProperty = MethodName.findStatic(
Types.ScriptRuntime, "checkAccessProperty",
Type.methodType(Types.Object, Types.Object, Types.ExecutionContext));
static final MethodName ScriptRuntime_checkAccessProperty_String = MethodName.findStatic(
Types.ScriptRuntime, "checkAccessProperty",
Type.methodType(Types.String, Types.Object, Types.String, Types.ExecutionContext));
static final MethodName ScriptRuntime_checkAccessProperty_int = MethodName.findStatic(
Types.ScriptRuntime, "checkAccessProperty", Type.methodType(Type.INT_TYPE,
Types.Object, Type.INT_TYPE, Types.ExecutionContext));
static final MethodName ScriptRuntime_checkAccessProperty_long = MethodName.findStatic(
Types.ScriptRuntime, "checkAccessProperty", Type.methodType(Type.LONG_TYPE,
Types.Object, Type.LONG_TYPE, Types.ExecutionContext));
static final MethodName ScriptRuntime_checkAccessProperty_double = MethodName.findStatic(
Types.ScriptRuntime, "checkAccessProperty", Type.methodType(Type.DOUBLE_TYPE,
Types.Object, Type.DOUBLE_TYPE, Types.ExecutionContext));
// ScriptRuntime#getPropertyValue
static final MethodName ScriptRuntime_getElementValue = MethodName.findStatic(
Types.ScriptRuntime, "getElementValue",
Type.methodType(Types.Object, Types.Object, Types.Object, Types.ExecutionContext));
static final MethodName ScriptRuntime_getPropertyValue_String = MethodName.findStatic(
Types.ScriptRuntime, "getPropertyValue",
Type.methodType(Types.Object, Types.Object, Types.String, Types.ExecutionContext));
static final MethodName ScriptRuntime_getPropertyValue_int = MethodName.findStatic(
Types.ScriptRuntime, "getPropertyValue",
Type.methodType(Types.Object, Types.Object, Type.INT_TYPE, Types.ExecutionContext));
static final MethodName ScriptRuntime_getPropertyValue_long = MethodName
.findStatic(Types.ScriptRuntime, "getPropertyValue", Type.methodType(Types.Object,
Types.Object, Type.LONG_TYPE, Types.ExecutionContext));
static final MethodName ScriptRuntime_getPropertyValue_double = MethodName
.findStatic(Types.ScriptRuntime, "getPropertyValue", Type.methodType(Types.Object,
Types.Object, Type.DOUBLE_TYPE, Types.ExecutionContext));
// ScriptRuntime#setPropertyValue
static final MethodName ScriptRuntime_setElementValue = MethodName.findStatic(
Types.ScriptRuntime, "setElementValue",
Type.methodType(Type.VOID_TYPE, Types.Object, Types.Object, Types.Object,
Types.ExecutionContext, Type.BOOLEAN_TYPE));
static final MethodName ScriptRuntime_setPropertyValue_String = MethodName.findStatic(
Types.ScriptRuntime, "setPropertyValue",
Type.methodType(Type.VOID_TYPE, Types.Object, Types.String, Types.Object,
Types.ExecutionContext, Type.BOOLEAN_TYPE));
static final MethodName ScriptRuntime_setPropertyValue_int = MethodName.findStatic(
Types.ScriptRuntime, "setPropertyValue",
Type.methodType(Type.VOID_TYPE, Types.Object, Type.INT_TYPE, Types.Object,
Types.ExecutionContext, Type.BOOLEAN_TYPE));
static final MethodName ScriptRuntime_setPropertyValue_long = MethodName.findStatic(
Types.ScriptRuntime, "setPropertyValue",
Type.methodType(Type.VOID_TYPE, Types.Object, Type.LONG_TYPE, Types.Object,
Types.ExecutionContext, Type.BOOLEAN_TYPE));
static final MethodName ScriptRuntime_setPropertyValue_double = MethodName.findStatic(
Types.ScriptRuntime, "setPropertyValue",
Type.methodType(Type.VOID_TYPE, Types.Object, Type.DOUBLE_TYPE, Types.Object,
Types.ExecutionContext, Type.BOOLEAN_TYPE));
// ScriptRuntime#deleteProperty
static final MethodName ScriptRuntime_deleteElement = MethodName
.findStatic(Types.ScriptRuntime, "deleteElement", Type.methodType(Type.BOOLEAN_TYPE,
Types.Object, Types.Object, Types.ExecutionContext, Type.BOOLEAN_TYPE));
static final MethodName ScriptRuntime_deleteProperty_String = MethodName.findStatic(
Types.ScriptRuntime, "deleteProperty", Type.methodType(Type.BOOLEAN_TYPE,
Types.Object, Types.String, Types.ExecutionContext, Type.BOOLEAN_TYPE));
static final MethodName ScriptRuntime_deleteProperty_int = MethodName.findStatic(
Types.ScriptRuntime, "deleteProperty", Type.methodType(Type.BOOLEAN_TYPE,
Types.Object, Type.INT_TYPE, Types.ExecutionContext, Type.BOOLEAN_TYPE));
static final MethodName ScriptRuntime_deleteProperty_long = MethodName.findStatic(
Types.ScriptRuntime, "deleteProperty", Type.methodType(Type.BOOLEAN_TYPE,
Types.Object, Type.LONG_TYPE, Types.ExecutionContext, Type.BOOLEAN_TYPE));
static final MethodName ScriptRuntime_deleteProperty_double = MethodName.findStatic(
Types.ScriptRuntime, "deleteProperty", Type.methodType(Type.BOOLEAN_TYPE,
Types.Object, Type.DOUBLE_TYPE, Types.ExecutionContext, Type.BOOLEAN_TYPE));
}
private static ValType GetValue(LeftHandSideExpression node, ValType type, CodeVisitor mv) {
assert type == ValType.Reference : "type is not reference: " + type;
mv.loadExecutionContext();
mv.lineInfo(node);
mv.invoke(Methods.Reference_getValue);
return ValType.Any;
}
private static void PutValue(LeftHandSideExpression node, ValType type, ValType value, CodeVisitor mv) {
assert type == ValType.Reference : "type is not reference: " + type;
mv.toBoxed(value);
mv.loadExecutionContext();
mv.lineInfo(node);
mv.invoke(Methods.Reference_putValue);
}
private static ValType Delete(LeftHandSideExpression node, ValType type, CodeVisitor mv) {
assert type == ValType.Reference : "type is not reference: " + type;
mv.loadExecutionContext();
mv.lineInfo(node);
mv.invoke(Methods.Reference_delete);
return ValType.Boolean;
}
private static ValType getElement(LeftHandSideExpression node, ValType elementType, CodeVisitor mv) {
// stack: [base, key] -> [value]
mv.loadExecutionContext();
mv.lineInfo(node);
mv.invoke(elementGetMethod(elementType));
return ValType.Any;
}
private static ValType setElement(LeftHandSideExpression node, ValType elementType, ValType value, CodeVisitor mv) {
// stack: [base, key, value] -> []
mv.toBoxed(value);
mv.loadExecutionContext();
mv.iconst(mv.isStrict());
mv.lineInfo(node);
mv.invoke(elementSetMethod(elementType));
return ValType.Empty;
}
private static ValType deleteElement(LeftHandSideExpression node, ValType elementType, CodeVisitor mv) {
// stack: [base, key] -> [result]
mv.loadExecutionContext();
mv.iconst(mv.isStrict());
mv.lineInfo(node);
mv.invoke(elementDeleteMethod(elementType));
return ValType.Boolean;
}
private static MethodName checkAccessMethod(ValType elementType) {
switch (elementType) {
case Empty:
return Methods.ScriptRuntime_checkAccessProperty;
case Number:
return Methods.ScriptRuntime_checkAccessProperty_double;
case Number_int:
return Methods.ScriptRuntime_checkAccessProperty_int;
case Number_uint:
return Methods.ScriptRuntime_checkAccessProperty_long;
case String:
return Methods.ScriptRuntime_checkAccessProperty_String;
case Any:
case Object:
return Methods.ScriptRuntime_checkAccessElement;
default:
throw new AssertionError();
}
}
private static MethodName elementGetMethod(ValType elementType) {
switch (elementType) {
case Number:
return Methods.ScriptRuntime_getPropertyValue_double;
case Number_int:
return Methods.ScriptRuntime_getPropertyValue_int;
case Number_uint:
return Methods.ScriptRuntime_getPropertyValue_long;
case String:
return Methods.ScriptRuntime_getPropertyValue_String;
case Any:
case Object:
return Methods.ScriptRuntime_getElementValue;
default:
throw new AssertionError();
}
}
private static MethodName elementSetMethod(ValType elementType) {
switch (elementType) {
case Number:
return Methods.ScriptRuntime_setPropertyValue_double;
case Number_int:
return Methods.ScriptRuntime_setPropertyValue_int;
case Number_uint:
return Methods.ScriptRuntime_setPropertyValue_long;
case String:
return Methods.ScriptRuntime_setPropertyValue_String;
case Any:
case Object:
return Methods.ScriptRuntime_setElementValue;
default:
throw new AssertionError();
}
}
private static MethodName elementDeleteMethod(ValType elementType) {
switch (elementType) {
case Number:
return Methods.ScriptRuntime_deleteProperty_double;
case Number_int:
return Methods.ScriptRuntime_deleteProperty_int;
case Number_uint:
return Methods.ScriptRuntime_deleteProperty_long;
case String:
return Methods.ScriptRuntime_deleteProperty_String;
case Any:
case Object:
return Methods.ScriptRuntime_deleteElement;
default:
throw new AssertionError();
}
}
private static void GetSuperEnvironment(LeftHandSideExpression node, CodeVisitor mv) {
mv.loadExecutionContext();
mv.lineInfo(node);
mv.invoke(Methods.ScriptRuntime_GetSuperEnvironmentRecord);
mv.dup();
}
private static void GetSuperThis(CodeVisitor mv) {
// stack: [env] -> [thisValue]
mv.loadExecutionContext();
mv.invoke(Methods.ScriptRuntime_GetSuperThis);
}
private static void GetSuperBase(CodeVisitor mv) {
// stack: [env, thisValue] -> [thisValue, baseValue]
mv.swap();
mv.loadExecutionContext();
mv.invoke(Methods.ScriptRuntime_GetSuperBase);
}
private static ValType GetSuperElement(LeftHandSideExpression node, ValType elementType, CodeVisitor mv) {
// stack: [pk, thisValue, baseValue] -> [value]
mv.loadExecutionContext();
mv.lineInfo(node);
if (elementType == ValType.String) {
mv.invoke(Methods.ScriptRuntime_getSuperProperty_String);
} else {
assert elementType == ValType.Any;
mv.invoke(Methods.ScriptRuntime_getSuperProperty);
}
return ValType.Any;
}
private static ValType SetSuperElement(LeftHandSideExpression node, ValType elementType, ValType value,
CodeVisitor mv) {
// stack: [pk, thisValue, baseValue, value] -> []
mv.toBoxed(value);
mv.loadExecutionContext();
mv.iconst(mv.isStrict());
mv.lineInfo(node);
if (elementType == ValType.String) {
mv.invoke(Methods.ScriptRuntime_setSuperProperty_String);
} else {
assert elementType == ValType.Any;
mv.invoke(Methods.ScriptRuntime_setSuperProperty);
}
return ValType.Empty;
}
private static ValType DeleteSuperElement(LeftHandSideExpression node, ValType elementType, CodeVisitor mv) {
mv.pop(elementType);
mv.loadExecutionContext();
mv.lineInfo(node);
mv.invoke(Methods.ScriptRuntime_deleteSuperProperty);
return ValType.Boolean;
}
private static Variable<?> saveToVariable(ValType value, CodeVisitor mv) {
Variable<?> result = mv.newScratchVariable(value.toClass());
mv.dup(value);
mv.store(result);
return result;
}
private static void loadFromVariable(Variable<?> variable, CodeVisitor mv) {
mv.load(variable);
mv.freeVariable(variable);
}
/**
* 12.1 Identifiers
* <p>
* 12.1.6 Runtime Semantics: Evaluation
*/
static final ReferenceOp<IdentifierReference> LOOKUP = new ReferenceOp<IdentifierReference>() {
@Override
protected ValType reference(IdentifierReference node, boolean update, CodeVisitor mv, CodeGenerator gen) {
// stack: [] -> [ref]
ValType ref = IdentifierResolution.resolve(node, mv);
assert ref == ValType.Reference : "type is not reference: " + ref;
if (update) {
mv.dup();
}
return ref;
}
@Override
ValType getValue(IdentifierReference node, ValType ref, CodeVisitor mv) {
// stack: [ref] -> [value]
return GetValue(node, ref, mv);
}
@Override
void putValue(IdentifierReference node, ValType ref, ValType value, CodeVisitor mv) {
// stack: [ref, value] -> []
PutValue(node, ref, value, mv);
}
@Override
ValType delete(IdentifierReference node, CodeVisitor mv, CodeGenerator gen) {
ValType ref = reference(node, false, mv, gen);
return Delete(node, ref, mv);
}
@Override
protected ValType referenceValue(IdentifierReference node, boolean withThis, CodeVisitor mv,
CodeGenerator gen) {
if (withThis) {
// stack: [] -> [ref, ref]
ValType ref = reference(node, mv, gen);
mv.dup();
// stack: [ref, ref] -> [value, ref]
getValue(node, ref, mv);
mv.swap();
// stack: [value, ref] -> [value, baseObj?]
mv.invoke(Methods.Reference_getBase);
mv.checkcast(Types.EnvironmentRecord);
mv.invoke(Methods.EnvironmentRecord_withBaseObject);
// stack: [value, baseObj?] -> [value, thisValue]
Jump baseObjNotNull = new Jump();
mv.dup();
mv.ifnonnull(baseObjNotNull);
{
mv.pop();
mv.loadUndefined();
}
mv.mark(baseObjNotNull);
return ValType.Any;
}
return IdentifierResolution.resolveValue(node, mv);
}
@Override
Variable<?> saveValue(ValType ref, ValType value, CodeVisitor mv) {
// stack: [ref, value] -> [value, ref, value]
mv.dupX(ref, value);
return null;
}
@Override
void restoreValue(Variable<?> result, CodeVisitor mv) {
// stack: [] -> []
}
};
/**
* 12.3.2 Property Accessors
* <p>
* 12.3.2.1 Runtime Semantics: Evaluation
*/
static final ReferenceOp<PropertyAccessor> PROPERTY = new ReferenceOp<PropertyAccessor>() {
@Override
protected ValType reference(PropertyAccessor node, boolean update, CodeVisitor mv, CodeGenerator gen) {
// stack: [] -> [base, key]
/* steps 1-3 */
gen.expressionBoxed(node.getBase(), mv);
/* steps 4-6 (not applicable) */
/* steps 7-8 */
mv.loadExecutionContext();
mv.lineInfo(node);
mv.invoke(checkAccessMethod(ValType.Empty));
/* steps 9-10 */
mv.aconst(node.getName());
/* steps 11-12 (not applicable) */
if (update) {
// stack: [base, key] -> [base, key, base, key]
mv.dup2();
}
return ValType.String;
}
@Override
ValType getValue(PropertyAccessor node, ValType ref, CodeVisitor mv) {
// stack: [base, key] -> [value]
return getElement(node, ValType.String, mv);
}
@Override
void putValue(PropertyAccessor node, ValType ref, ValType value, CodeVisitor mv) {
// stack: [base, key, value] -> []
setElement(node, ValType.String, value, mv);
}
@Override
ValType delete(PropertyAccessor node, CodeVisitor mv, CodeGenerator gen) {
// stack: [] -> [base]
gen.expressionBoxed(node.getBase(), mv);
// stack: [base] -> [base, key]
mv.aconst(node.getName());
// stack: [base, key] -> [result]
return deleteElement(node, ValType.String, mv);
}
@Override
protected ValType referenceValue(PropertyAccessor node, boolean withThis, CodeVisitor mv, CodeGenerator gen) {
gen.expressionBoxed(node.getBase(), mv);
if (withThis) {
mv.dup();
}
mv.aconst(node.getName());
mv.loadExecutionContext();
mv.lineInfo(node);
mv.invoke(elementGetMethod(ValType.String));
if (withThis) {
// stack: [thisValue, func] -> [func, thisValue]
mv.swap();
}
return ValType.Any;
}
@Override
Variable<?> saveValue(ValType ref, ValType value, CodeVisitor mv) {
// stack: [base, key, value] -> [value, base, key, value]
// ValType.Number to represent (base, key) tuple
mv.dupX(ValType.Number, value);
return null;
}
@Override
void restoreValue(Variable<?> variable, CodeVisitor mv) {
// stack: [] -> []
}
};
/**
* 12.3.2 Property Accessors
* <p>
* 12.3.2.1 Runtime Semantics: Evaluation
*/
static final ReferenceOp<ElementAccessor> ELEMENT = new ReferenceOp<ElementAccessor>() {
private ValType evalPropertyKey(Expression propertyKey, CodeVisitor mv, CodeGenerator gen) {
ValType elementType = gen.expression(propertyKey, mv);
switch (elementType) {
case String:
if (propertyKey instanceof StringLiteral) {
return elementType;
}
// fall-thru if string is not flat
case Boolean:
case Null:
case Undefined:
DefaultCodeGenerator.ToFlatString(elementType, mv);
return ValType.String;
default:
return elementType;
}
}
@Override
protected ValType reference(ElementAccessor node, boolean update, CodeVisitor mv, CodeGenerator gen) {
// stack: [] -> [base, base?, key]
/* steps 1-3 */
gen.expressionBoxed(node.getBase(), mv);
boolean isLiteral = node.getElement() instanceof Literal;
if (isLiteral) {
/* steps 7-10 */
mv.loadExecutionContext();
mv.lineInfo(node);
mv.invoke(checkAccessMethod(ValType.Empty));
} else {
mv.dup();
}
if (update) {
mv.dup();
}
/* steps 4-6 */
ValType elementType = evalPropertyKey(node.getElement(), mv, gen);
if (!isLiteral) {
/* steps 7-10 */
mv.loadExecutionContext();
mv.lineInfo(node);
mv.invoke(checkAccessMethod(elementType));
}
/* steps 11-12 (not applicable) */
if (update) {
// stack: [base, base, key] -> [base, key, base, key]
mv.dupX(ValType.Any, elementType);
}
return elementType;
}
@Override
ValType getValue(ElementAccessor node, ValType ref, CodeVisitor mv) {
// stack: [base, key] -> [value]
return getElement(node, ref, mv);
}
@Override
void putValue(ElementAccessor node, ValType ref, ValType value, CodeVisitor mv) {
// stack: [base, key, value] -> []
setElement(node, ref, value, mv);
}
@Override
ValType delete(ElementAccessor node, CodeVisitor mv, CodeGenerator gen) {
// stack: [] -> [base]
gen.expressionBoxed(node.getBase(), mv);
// stack: [base] -> [base, key]
ValType elementType = evalPropertyKey(node.getElement(), mv, gen);
// stack: [base, key] -> [result]
return deleteElement(node, elementType, mv);
}
@Override
protected ValType referenceValue(ElementAccessor node, boolean withThis, CodeVisitor mv, CodeGenerator gen) {
gen.expressionBoxed(node.getBase(), mv);
if (withThis) {
mv.dup();
}
ValType elementType = evalPropertyKey(node.getElement(), mv, gen);
mv.loadExecutionContext();
mv.lineInfo(node);
mv.invoke(elementGetMethod(elementType));
if (withThis) {
mv.swap();
}
return ValType.Any;
}
@Override
Variable<?> saveValue(ValType ref, ValType value, CodeVisitor mv) {
// stack: [base, key, value] -> [value, base, key, value]
if (ref.size() == 1) {
// ValType.Number to represent (base, key) tuple
mv.dupX(ValType.Number, value);
return null;
}
return saveToVariable(value, mv);
}
@Override
void restoreValue(Variable<?> variable, CodeVisitor mv) {
if (variable != null) {
loadFromVariable(variable, mv);
}
}
};
/**
* 12.3.5.1 Runtime Semantics: Evaluation
*/
static final ReferenceOp<SuperPropertyAccessor> SUPER_PROPERTY = new ReferenceOp<SuperPropertyAccessor>() {
@Override
protected ValType reference(SuperPropertyAccessor node, boolean update, CodeVisitor mv, CodeGenerator gen) {
// stack: [] -> [pk, pk?]
mv.aconst(node.getName());
if (update) {
mv.dup();
}
// stack: [pk, pk?] -> [pk, pk?, env, env]
GetSuperEnvironment(node, mv);
// stack: [pk, pk?, env, env] -> [pk, pk?, thisValue, baseValue]
GetSuperThis(mv);
GetSuperBase(mv);
if (update) {
// stack: [pk, pk, thisValue, baseValue] -> [pk, thisValue, baseValue, pk, thisValue, baseValue]
mv.dup2X1();
}
return ValType.String;
}
@Override
ValType getValue(SuperPropertyAccessor node, ValType ref, CodeVisitor mv) {
// stack: [pk, thisValue, baseValue] -> [value]
return GetSuperElement(node, ref, mv);
}
@Override
void putValue(SuperPropertyAccessor node, ValType ref, ValType value, CodeVisitor mv) {
// stack: [pk, thisValue, baseValue, value] -> []
SetSuperElement(node, ref, value, mv);
}
@Override
ValType delete(SuperPropertyAccessor node, CodeVisitor mv, CodeGenerator gen) {
return DeleteSuperElement(node, ValType.Empty, mv);
}
@Override
protected ValType referenceValue(SuperPropertyAccessor node, boolean withThis, CodeVisitor mv,
CodeGenerator gen) {
// stack: [] -> [pk]
mv.aconst(node.getName());
// stack: [pk] -> [pk, env, env]
GetSuperEnvironment(node, mv);
// stack: [pk, env, env] -> [thisValue?, pk, thisValue, baseValue]
GetSuperThis(mv);
if (withThis) {
mv.dupX2();
}
GetSuperBase(mv);
// stack: [thisValue?, pk, thisValue, baseValue] -> [thisValue?, value]
GetSuperElement(node, ValType.String, mv);
if (withThis) {
// stack: [thisValue, value] -> [value, thisValue]
mv.swap();
}
return ValType.Any;
}
@Override
Variable<?> saveValue(ValType ref, ValType value, CodeVisitor mv) {
// stack: [pk, thisValue, baseValue, value] -> [pk, thisValue, baseValue, value]
return saveToVariable(value, mv);
}
@Override
void restoreValue(Variable<?> variable, CodeVisitor mv) {
// stack: [] -> [value]
loadFromVariable(variable, mv);
}
};
/**
* 12.3.5.1 Runtime Semantics: Evaluation
*/
static final ReferenceOp<SuperElementAccessor> SUPER_ELEMENT = new ReferenceOp<SuperElementAccessor>() {
@Override
protected ValType reference(SuperElementAccessor node, boolean update, CodeVisitor mv, CodeGenerator gen) {
// stack: [] -> [pk, pk?]
ValType type = gen.expression(node.getElement(), mv);
type = ToPropertyKey(type, mv);
if (update) {
mv.dup();
}
// stack: [pk, pk?] -> [pk, pk?, env, env]
GetSuperEnvironment(node, mv);
// stack: [pk, pk?, env, env] -> [pk, pk?, thisValue, baseValue]
GetSuperThis(mv);
GetSuperBase(mv);
if (update) {
// stack: [pk, pk, thisValue, baseValue] -> [pk, thisValue, baseValue, pk, thisValue, baseValue]
mv.dup2X1();
}
return type;
}
@Override
ValType getValue(SuperElementAccessor node, ValType ref, CodeVisitor mv) {
// stack: [pk, thisValue, baseValue] -> [value]
return GetSuperElement(node, ref, mv);
}
@Override
void putValue(SuperElementAccessor node, ValType ref, ValType value, CodeVisitor mv) {
// stack: [pk, thisValue, baseValue, value] -> []
SetSuperElement(node, ref, value, mv);
}
@Override
ValType delete(SuperElementAccessor node, CodeVisitor mv, CodeGenerator gen) {
ValType type = gen.expression(node.getElement().emptyCompletion(), mv);
return DeleteSuperElement(node, type, mv);
}
@Override
protected ValType referenceValue(SuperElementAccessor node, boolean withThis, CodeVisitor mv,
CodeGenerator gen) {
// stack: [] -> [pk]
ValType type = gen.expression(node.getElement(), mv);
type = ToPropertyKey(type, mv);
// stack: [pk] -> [pk, env, env]
GetSuperEnvironment(node, mv);
// stack: [pk, env, env] -> [thisValue?, pk, thisValue, baseValue]
GetSuperThis(mv);
if (withThis) {
mv.dupX2();
}
GetSuperBase(mv);
// stack: [thisValue?, pk, thisValue, baseValue] -> [thisValue?, value]
GetSuperElement(node, type, mv);
if (withThis) {
// stack: [thisValue, value] -> [value, thisValue]
mv.swap();
}
return ValType.Any;
}
@Override
Variable<?> saveValue(ValType ref, ValType value, CodeVisitor mv) {
// stack: [pk, thisValue, baseValue, value] -> [pk, thisValue, baseValue, value]
return saveToVariable(value, mv);
}
@Override
void restoreValue(Variable<?> variable, CodeVisitor mv) {
// stack: [] -> [value]
loadFromVariable(variable, mv);
}
};
}