/**
* 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.interpreter;
import static com.github.anba.es6draft.runtime.AbstractOperations.*;
import static com.github.anba.es6draft.runtime.internal.ScriptRuntime.CheckCallable;
import static com.github.anba.es6draft.runtime.internal.ScriptRuntime.CheckConstructor;
import static com.github.anba.es6draft.runtime.internal.ScriptRuntime.IsBuiltinEval;
import static com.github.anba.es6draft.runtime.types.Null.NULL;
import static com.github.anba.es6draft.runtime.types.Reference.GetValue;
import static com.github.anba.es6draft.runtime.types.Reference.PutValue;
import static com.github.anba.es6draft.runtime.types.Undefined.UNDEFINED;
import static com.github.anba.es6draft.runtime.types.builtins.ArrayObject.ArrayCreate;
import static com.github.anba.es6draft.runtime.types.builtins.OrdinaryObject.ObjectCreate;
import static com.github.anba.es6draft.semantics.StaticSemantics.PropName;
import java.util.EnumSet;
import java.util.List;
import com.github.anba.es6draft.ast.*;
import com.github.anba.es6draft.ast.BinaryExpression.Operator;
import com.github.anba.es6draft.parser.Parser;
import com.github.anba.es6draft.runtime.ExecutionContext;
import com.github.anba.es6draft.runtime.internal.CompatibilityOption;
import com.github.anba.es6draft.runtime.internal.IndexedMap;
import com.github.anba.es6draft.runtime.internal.ScriptRuntime;
import com.github.anba.es6draft.runtime.objects.Eval;
import com.github.anba.es6draft.runtime.objects.Eval.EvalFlags;
import com.github.anba.es6draft.runtime.objects.text.RegExpConstructor;
import com.github.anba.es6draft.runtime.types.Callable;
import com.github.anba.es6draft.runtime.types.Constructor;
import com.github.anba.es6draft.runtime.types.Intrinsics;
import com.github.anba.es6draft.runtime.types.Reference;
import com.github.anba.es6draft.runtime.types.ScriptObject;
import com.github.anba.es6draft.runtime.types.Undefined;
import com.github.anba.es6draft.runtime.types.builtins.ArrayObject;
import com.github.anba.es6draft.runtime.types.builtins.OrdinaryObject;
/**
* Basic interpreter to speed-up evaluation of simple eval-scripts.
*/
public final class Interpreter extends DefaultNodeVisitor<Object, ExecutionContext> {
/**
* Returns a new {@link InterpretedScript} if {@code parsedScript} can be interpreted, otherwise
* returns {@code null}.
*
* @param parsedScript
* the script node
* @return the interpreted script or {@code null}
*/
public static InterpretedScript script(Script parsedScript) {
if (!parsedScript.accept(InterpreterTest.INSTANCE, null)) {
return null;
}
return new InterpretedScript(parsedScript);
}
private final EnumSet<Parser.Option> parserOptions;
private final boolean strict;
public Interpreter(Script parsedScript) {
this.parserOptions = EnumSet.copyOf(parsedScript.getParserOptions());
this.strict = parsedScript.isStrict();
}
/* ----------------------------------------------------------------------------------------- */
/**
* 12.4.4 Postfix Increment Operator.
*
* @param lhs
* the left-hand side expression
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Double postIncrement(Reference<?, ?> lhs, ExecutionContext cx) {
double oldValue = ToNumber(cx, GetValue(lhs, cx));
double newValue = oldValue + 1;
PutValue(lhs, newValue, cx);
return oldValue;
}
/**
* 12.4.5 Postfix Decrement Operator
*
* @param lhs
* the left-hand side expression
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Double postDecrement(Reference<?, ?> lhs, ExecutionContext cx) {
double oldValue = ToNumber(cx, GetValue(lhs, cx));
double newValue = oldValue - 1;
PutValue(lhs, newValue, cx);
return oldValue;
}
/**
* 12.5.4 The delete Operator
*
* @param expr
* the expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static boolean delete(Object expr, ExecutionContext cx) {
if (!(expr instanceof Reference)) {
return true;
}
return ((Reference<?, ?>) expr).delete(cx);
}
/**
* 12.5.5 The void Operator
*
* @param value
* the expression value
* @return the return value after applying the operation
*/
private static Undefined _void(Object value) {
assert !(value instanceof Reference);
return UNDEFINED;
}
/**
* 12.5.7 Prefix Increment Operator
*
* @param expr
* the expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Double preIncrement(Reference<?, ?> expr, ExecutionContext cx) {
double oldValue = ToNumber(cx, GetValue(expr, cx));
double newValue = oldValue + 1;
PutValue(expr, newValue, cx);
return newValue;
}
/**
* 12.5.8 Prefix Decrement Operator
*
* @param expr
* the expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Double preDecrement(Reference<?, ?> expr, ExecutionContext cx) {
double oldValue = ToNumber(cx, GetValue(expr, cx));
double newValue = oldValue - 1;
PutValue(expr, newValue, cx);
return newValue;
}
/**
* 12.5.9 Unary + Operator
*
* @param value
* the expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Double pos(Object value, ExecutionContext cx) {
return ToNumber(cx, value);
}
/**
* 12.5.10 Unary - Operator
*
* @param value
* the expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Double neg(Object value, ExecutionContext cx) {
return -ToNumber(cx, value);
}
/**
* 12.5.11 Bitwise NOT Operator ( ~ )
*
* @param value
* the expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Integer bitnot(Object value, ExecutionContext cx) {
return ~ToInt32(cx, value);
}
/**
* 12.5.12 Logical NOT Operator ( ! )
*
* @param value
* the expression value
* @return the return value after applying the operation
*/
private static Boolean not(Object value) {
return !ToBoolean(value);
}
/**
* Extension: Exponentiation Operator
*
* @param leftValue
* the left-hand side expression value
* @param rightValue
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Double exp(Object leftValue, Object rightValue, ExecutionContext cx) {
double lnum = ToNumber(cx, leftValue);
double rnum = ToNumber(cx, rightValue);
return Math.pow(lnum, rnum);
}
/**
* 12.6 Multiplicative Operators
*
* @param leftValue
* the left-hand side expression value
* @param rightValue
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Double mul(Object leftValue, Object rightValue, ExecutionContext cx) {
double lnum = ToNumber(cx, leftValue);
double rnum = ToNumber(cx, rightValue);
return lnum * rnum;
}
/**
* 12.6 Multiplicative Operators
*
* @param leftValue
* the left-hand side expression value
* @param rightValue
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Double div(Object leftValue, Object rightValue, ExecutionContext cx) {
double lnum = ToNumber(cx, leftValue);
double rnum = ToNumber(cx, rightValue);
return lnum / rnum;
}
/**
* 12.6 Multiplicative Operators
*
* @param leftValue
* the left-hand side expression value
* @param rightValue
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Double mod(Object leftValue, Object rightValue, ExecutionContext cx) {
double lnum = ToNumber(cx, leftValue);
double rnum = ToNumber(cx, rightValue);
return lnum % rnum;
}
/**
* 12.7.2 The Subtraction Operator ( - )
*
* @param lval
* the left-hand side expression value
* @param rval
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Double sub(Object lval, Object rval, ExecutionContext cx) {
double lnum = ToNumber(cx, lval);
double rnum = ToNumber(cx, rval);
return lnum - rnum;
}
/**
* 12.8.1 The Left Shift Operator ( {@literal <<} )
*
* @param lval
* the left-hand side expression value
* @param rval
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Integer leftShift(Object lval, Object rval, ExecutionContext cx) {
int lnum = ToInt32(cx, lval);
long rnum = ToUint32(cx, rval);
int shiftCount = (int) (rnum & 0x1F);
return lnum << shiftCount;
}
/**
* 12.8.2 The Signed Right Shift Operator ( {@literal >>} )
*
* @param lval
* the left-hand side expression value
* @param rval
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Integer rightShift(Object lval, Object rval, ExecutionContext cx) {
int lnum = ToInt32(cx, lval);
long rnum = ToUint32(cx, rval);
int shiftCount = (int) (rnum & 0x1F);
return lnum >> shiftCount;
}
/**
* 12.8.3 The Unsigned Right Shift Operator ( {@literal >>>} )
*
* @param lval
* the left-hand side expression value
* @param rval
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Long unsignedRightShift(Object lval, Object rval, ExecutionContext cx) {
long lnum = ToUint32(cx, lval);
long rnum = ToUint32(cx, rval);
int shiftCount = (int) (rnum & 0x1F);
return lnum >>> shiftCount;
}
/**
* 12.9 Relational Operators
*
* @param lval
* the left-hand side expression value
* @param rval
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Boolean _instanceof(Object lval, Object rval, ExecutionContext cx) {
return ScriptRuntime.InstanceofOperator(lval, rval, cx);
}
/**
* 12.9 Relational Operators
*
* @param lval
* the left-hand side expression value
* @param rval
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Boolean lessThan(Object lval, Object rval, ExecutionContext cx) {
return RelationalComparison(cx, lval, rval, true) == 1;
}
/**
* 12.9 Relational Operators
*
* @param lval
* the left-hand side expression value
* @param rval
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Boolean lessThanEqual(Object lval, Object rval, ExecutionContext cx) {
return RelationalComparison(cx, rval, lval, false) == 0;
}
/**
* 12.9 Relational Operators
*
* @param lval
* the left-hand side expression value
* @param rval
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Boolean greaterThan(Object lval, Object rval, ExecutionContext cx) {
return RelationalComparison(cx, rval, lval, false) == 1;
}
/**
* 12.9 Relational Operators
*
* @param lval
* the left-hand side expression value
* @param rval
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Boolean greaterThanEqual(Object lval, Object rval, ExecutionContext cx) {
return RelationalComparison(cx, lval, rval, true) == 0;
}
/**
* 12.10 Equality Operators
*
* @param lval
* the left-hand side expression value
* @param rval
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Boolean equals(Object lval, Object rval, ExecutionContext cx) {
return EqualityComparison(cx, rval, lval);
}
/**
* 12.10 Equality Operators
*
* @param lval
* the left-hand side expression value
* @param rval
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Boolean notEquals(Object lval, Object rval, ExecutionContext cx) {
return !EqualityComparison(cx, rval, lval);
}
/**
* 12.10 Equality Operators
*
* @param lval
* the left-hand side expression value
* @param rval
* the right-hand side expression value
* @return the return value after applying the operation
*/
private static Boolean strictEquals(Object lval, Object rval) {
return StrictEqualityComparison(rval, lval);
}
/**
* 12.10 Equality Operators
*
* @param lval
* the left-hand side expression value
* @param rval
* the right-hand side expression value
* @return the return value after applying the operation
*/
private static Boolean strictNotEquals(Object lval, Object rval) {
return !StrictEqualityComparison(rval, lval);
}
/**
* 12.11 Binary Bitwise Operators
*
* @param lval
* the left-hand side expression value
* @param rval
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Integer bitand(Object lval, Object rval, ExecutionContext cx) {
int lnum = ToInt32(cx, lval);
int rnum = ToInt32(cx, rval);
return lnum & rnum;
}
/**
* 12.11 Binary Bitwise Operators
*
* @param lval
* the left-hand side expression value
* @param rval
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Integer bitxor(Object lval, Object rval, ExecutionContext cx) {
int lnum = ToInt32(cx, lval);
int rnum = ToInt32(cx, rval);
return lnum ^ rnum;
}
/**
* 12.11 Binary Bitwise Operators
*
* @param lval
* the left-hand side expression value
* @param rval
* the right-hand side expression value
* @param cx
* the execution context
* @return the return value after applying the operation
*/
private static Integer bitor(Object lval, Object rval, ExecutionContext cx) {
int lnum = ToInt32(cx, lval);
int rnum = ToInt32(cx, rval);
return lnum | rnum;
}
/* ----------------------------------------------------------------------------------------- */
@Override
protected Object visit(Node node, ExecutionContext cx) {
throw new IllegalStateException();
}
@Override
public Object visit(Script node, ExecutionContext cx) {
Object completionValue = UNDEFINED;
for (StatementListItem stmt : node.getStatements()) {
Object val = stmt.accept(this, cx);
if (val != null) {
completionValue = val;
}
}
return completionValue;
}
@Override
public Object visit(VariableStatement node, ExecutionContext cx) {
for (VariableDeclaration decl : node.getElements()) {
decl.accept(this, cx);
}
return null;
}
@Override
public Object visit(VariableDeclaration node, ExecutionContext cx) {
BindingIdentifier binding = (BindingIdentifier) node.getBinding();
Expression initializer = node.getInitializer();
if (initializer != null) {
Reference<?, String> lhs = cx.resolveBinding(binding.getName().getIdentifier(), strict);
Object val = GetValue(initializer.accept(this, cx), cx);
lhs.putValue(val, cx);
}
return null;
}
@Override
public Object visit(ExpressionStatement node, ExecutionContext cx) {
return GetValue(node.getExpression().accept(this, cx), cx);
}
@Override
public Object visit(AssignmentExpression node, ExecutionContext cx) {
if (node.getOperator() == AssignmentExpression.Operator.ASSIGN) {
Reference<?, ?> lref = (Reference<?, ?>) node.getLeft().accept(this, cx);
Object rval = GetValue(node.getRight().accept(this, cx), cx);
PutValue(lref, rval, cx);
return rval;
} else {
Reference<?, ?> lref = (Reference<?, ?>) node.getLeft().accept(this, cx);
Object lval = GetValue(lref, cx);
Object rval = GetValue(node.getRight().accept(this, cx), cx);
Object r;
switch (node.getOperator()) {
case ASSIGN_ADD:
r = ScriptRuntime.add(lval, rval, cx);
break;
case ASSIGN_BITAND:
r = bitand(lval, rval, cx);
break;
case ASSIGN_BITOR:
r = bitor(lval, rval, cx);
break;
case ASSIGN_BITXOR:
r = bitxor(lval, rval, cx);
break;
case ASSIGN_DIV:
r = div(lval, rval, cx);
break;
case ASSIGN_EXP:
r = exp(lval, rval, cx);
break;
case ASSIGN_MOD:
r = mod(lval, rval, cx);
break;
case ASSIGN_MUL:
r = mul(lval, rval, cx);
break;
case ASSIGN_SHL:
r = leftShift(lval, rval, cx);
break;
case ASSIGN_SHR:
r = rightShift(lval, rval, cx);
break;
case ASSIGN_SUB:
r = sub(lval, rval, cx);
break;
case ASSIGN_USHR:
r = unsignedRightShift(lval, rval, cx);
break;
case ASSIGN:
default:
throw new AssertionError();
}
PutValue(lref, r, cx);
return r;
}
}
@Override
public Object visit(BinaryExpression node, ExecutionContext cx) {
if (node.getOperator() == BinaryExpression.Operator.AND
|| node.getOperator() == BinaryExpression.Operator.OR) {
return visitAndOr(node, cx);
}
/* steps 1-? */
Object lval = GetValue(node.getLeft().accept(this, cx), cx);
Object rval = GetValue(node.getRight().accept(this, cx), cx);
switch (node.getOperator()) {
case ADD:
return ScriptRuntime.add(lval, rval, cx);
case BITAND:
return bitand(lval, rval, cx);
case BITOR:
return bitor(lval, rval, cx);
case BITXOR:
return bitxor(lval, rval, cx);
case DIV:
return div(lval, rval, cx);
case EQ:
return equals(lval, rval, cx);
case EXP:
return exp(lval, rval, cx);
case GE:
return greaterThanEqual(lval, rval, cx);
case GT:
return greaterThan(lval, rval, cx);
case IN:
return ScriptRuntime.in(lval, rval, cx);
case INSTANCEOF:
return _instanceof(lval, rval, cx);
case LE:
return lessThanEqual(lval, rval, cx);
case LT:
return lessThan(lval, rval, cx);
case MOD:
return mod(lval, rval, cx);
case MUL:
return mul(lval, rval, cx);
case NE:
return notEquals(lval, rval, cx);
case SHEQ:
return strictEquals(lval, rval);
case SHL:
return leftShift(lval, rval, cx);
case SHNE:
return strictNotEquals(lval, rval);
case SHR:
return rightShift(lval, rval, cx);
case SUB:
return sub(lval, rval, cx);
case USHR:
return unsignedRightShift(lval, rval, cx);
case AND:
case OR:
default:
throw new AssertionError();
}
}
private Object visitAndOr(BinaryExpression node, ExecutionContext cx) {
Object lval = GetValue(node.getLeft().accept(this, cx), cx);
if (ToBoolean(lval) ^ node.getOperator() == Operator.AND) {
return lval;
}
return GetValue(node.getRight().accept(this, cx), cx);
}
@Override
public Object visit(UnaryExpression node, ExecutionContext cx) {
Object val = node.getOperand().accept(this, cx);
switch (node.getOperator()) {
case BITNOT:
return bitnot(GetValue(val, cx), cx);
case DELETE:
return delete(val, cx);
case NEG:
return neg(GetValue(val, cx), cx);
case NOT:
return not(GetValue(val, cx));
case POS:
return pos(GetValue(val, cx), cx);
case POST_DEC:
return postDecrement((Reference<?, ?>) val, cx);
case POST_INC:
return postIncrement((Reference<?, ?>) val, cx);
case PRE_DEC:
return preDecrement((Reference<?, ?>) val, cx);
case PRE_INC:
return preIncrement((Reference<?, ?>) val, cx);
case TYPEOF:
return ScriptRuntime.typeof(val, cx);
case VOID:
return _void(GetValue(val, cx));
default:
throw new AssertionError();
}
}
@Override
public Object visit(CommaExpression node, ExecutionContext cx) {
assert !node.getOperands().isEmpty();
Object val = null;
for (Expression expression : node.getOperands()) {
val = GetValue(expression.accept(this, cx), cx);
}
return val;
}
@Override
public Object visit(ConditionalExpression node, ExecutionContext cx) {
Object test = GetValue(node.getTest().accept(this, cx), cx);
Object val;
if (ToBoolean(test)) {
val = node.getThen().accept(this, cx);
} else {
val = node.getOtherwise().accept(this, cx);
}
return GetValue(val, cx);
}
@Override
public Object visit(NullLiteral node, ExecutionContext cx) {
return NULL;
}
@Override
public Object visit(BooleanLiteral node, ExecutionContext cx) {
return node.getValue();
}
@Override
public Object visit(NumericLiteral node, ExecutionContext cx) {
return node.getValue();
}
@Override
public Object visit(StringLiteral node, ExecutionContext cx) {
return node.getValue();
}
@Override
public Object visit(RegularExpressionLiteral node, ExecutionContext cx) {
return RegExpConstructor.RegExpCreate(cx, node.getRegexp(), node.getFlags());
}
@Override
public Object visit(ObjectLiteral node, ExecutionContext cx) {
OrdinaryObject obj = ObjectCreate(cx, Intrinsics.ObjectPrototype);
for (PropertyDefinition propertyDefinition : node.getProperties()) {
assert propertyDefinition instanceof PropertyValueDefinition;
PropertyValueDefinition propValDef = (PropertyValueDefinition) propertyDefinition;
PropertyName propertyName = propValDef.getPropertyName();
Expression propertyValue = propValDef.getPropertyValue();
String propName = PropName(propertyName);
long propIndex = propName != null ? IndexedMap.toIndex(propName) : -1;
assert propName != null && !(propertyName instanceof ComputedPropertyName);
Object value = GetValue(propertyValue.accept(this, cx), cx);
if ("__proto__".equals(propName)
&& cx.getRealm().isEnabled(CompatibilityOption.ProtoInitializer)) {
ScriptRuntime.defineProtoProperty(obj, value, cx);
} else if (IndexedMap.isIndex(propIndex)) {
ScriptRuntime.defineProperty(obj, propIndex, value, cx);
} else {
ScriptRuntime.defineProperty(obj, propName, value, cx);
}
}
return obj;
}
@Override
public Object visit(ArrayLiteral node, ExecutionContext cx) {
ArrayObject array = ArrayCreate(cx, 0);
int nextIndex = 0;
for (Expression element : node.getElements()) {
if (element instanceof Elision) {
// Elision
} else {
Object value = GetValue(element.accept(this, cx), cx);
ScriptRuntime.defineProperty(array, nextIndex, value);
}
nextIndex += 1;
}
Set(cx, array, "length", nextIndex, false);
return array;
}
@Override
public Object visit(CallExpression node, ExecutionContext cx) {
Object ref = node.getBase().accept(this, cx);
return EvaluateCall(ref, node.getArguments(), directEval(node), cx);
}
/**
* 12.3.4.2 Runtime Semantics: EvaluateCall( ref, arguments, tailPosition )<br>
* 12.3.4.3 Runtime Semantics: EvaluateDirectCall( func, thisValue, arguments, tailPosition )
*
* @param ref
* the call base reference
* @param arguments
* the function call arguments
* @param directEval
* the direct eval flag
* @param cx
* the execution context
* @return the return value after applying the call operation
*/
private Object EvaluateCall(Object ref, List<Expression> arguments, boolean directEval,
ExecutionContext cx) {
/* steps 1-2 (EvaluateCall) */
Object func = GetValue(ref, cx);
/* steps 3-4 (EvaluateCall) */
Object thisValue = UNDEFINED;
if (ref instanceof Reference) {
Reference<?, ?> rref = (Reference<?, ?>) ref;
if (rref.isPropertyReference()) {
thisValue = rref.getThisValue();
} else if (!(rref instanceof Reference.BindingReference)) {
assert rref instanceof Reference.IdentifierReference;
Reference.IdentifierReference<?> idref = (Reference.IdentifierReference<?>) rref;
ScriptObject newThisValue = idref.getBase().withBaseObject();
if (newThisValue != null) {
thisValue = newThisValue;
}
}
}
/* steps 1-2 (EvaluateDirectCall) */
Object[] argList = ArgumentListEvaluation(arguments, cx);
/* steps 3-4 (EvaluateDirectCall) */
Callable f = CheckCallable(func, cx);
/* [12.3.4.1 Runtime Semantics: Evaluation - step 3] */
if (directEval && IsBuiltinEval(ref, f, cx)) {
int evalFlags = EvalFlags.Direct.getValue();
if (strict) {
evalFlags |= EvalFlags.Strict.getValue();
}
evalFlags |= EvalFlags.toFlags(parserOptions);
return Eval.directEval(argList, cx, evalFlags);
}
if (directEval && ScriptRuntime.directEvalFallbackHook(cx) != null) {
argList = ScriptRuntime.directEvalFallbackArguments(f, cx, thisValue, argList);
thisValue = ScriptRuntime.directEvalFallbackThisArgument(cx);
f = ScriptRuntime.directEvalFallbackHook(cx);
}
/* steps 5, 7-8 (EvaluateDirectCall) (not applicable) */
/* steps 6, 9 (EvaluateDirectCall) */
return f.call(cx, thisValue, argList);
}
private Object[] ArgumentListEvaluation(List<Expression> arguments, ExecutionContext cx) {
int size = arguments.size();
Object[] args = new Object[size];
for (int i = 0; i < size; ++i) {
args[i] = GetValue(arguments.get(i).accept(this, cx), cx);
}
return args;
}
private static boolean directEval(CallExpression node) {
Expression base = node.getBase();
if (base instanceof IdentifierReference
&& "eval".equals(((IdentifierReference) base).getName())) {
return true;
}
return false;
}
@Override
public Object visit(NewExpression node, ExecutionContext cx) {
Object constructor = node.getExpression().accept(this, cx);
constructor = GetValue(constructor, cx);
Object[] args = ArgumentListEvaluation(node.getArguments(), cx);
return CheckConstructor(constructor, cx).construct(cx, (Constructor) constructor, args);
}
@Override
public Object visit(ElementAccessor node, ExecutionContext cx) {
Object base = GetValue(node.getBase().accept(this, cx), cx);
Object element = GetValue(node.getElement().accept(this, cx), cx);
return ScriptRuntime.getElement(base, element, cx, strict);
}
@Override
public Object visit(PropertyAccessor node, ExecutionContext cx) {
Object base = GetValue(node.getBase().accept(this, cx), cx);
return ScriptRuntime.getProperty(base, node.getName(), cx, strict);
}
@Override
public Object visit(IdentifierReference node, ExecutionContext cx) {
return cx.resolveBinding(node.getName(), strict);
}
@Override
public Object visit(ThisExpression node, ExecutionContext cx) {
return cx.resolveThisBinding();
}
/**
* {@link NodeVisitor} to test whether or not the script can be executed by the interpreter.
*/
private static final class InterpreterTest extends DefaultNodeVisitor<Boolean, Void> {
static final DefaultNodeVisitor<Boolean, Void> INSTANCE = new InterpreterTest();
@Override
protected Boolean visit(Node node, Void value) {
return false;
}
@Override
public Boolean visit(Script node, Void value) {
for (StatementListItem stmt : node.getStatements()) {
if (!stmt.accept(this, value)) {
return false;
}
}
return true;
}
@Override
public Boolean visit(VariableStatement node, Void value) {
for (VariableDeclaration decl : node.getElements()) {
if (!decl.accept(this, value)) {
return false;
}
}
return true;
}
@Override
public Boolean visit(VariableDeclaration node, Void value) {
Binding binding = node.getBinding();
if (!(binding instanceof BindingIdentifier)) {
return false;
}
Expression initializer = node.getInitializer();
return initializer == null || initializer.accept(this, value);
}
@Override
public Boolean visit(ExpressionStatement node, Void value) {
return node.getExpression().accept(this, value);
}
@Override
public Boolean visit(CallExpression node, Void value) {
if (!node.getBase().accept(this, value)) {
return false;
}
for (Expression expression : node.getArguments()) {
if (!expression.accept(this, value)) {
return false;
}
}
return true;
}
@Override
public Boolean visit(NewExpression node, Void value) {
if (!node.getExpression().accept(this, value)) {
return false;
}
for (Expression expression : node.getArguments()) {
if (!expression.accept(this, value)) {
return false;
}
}
return true;
}
@Override
public Boolean visit(ConditionalExpression node, Void value) {
return node.getTest().accept(this, value) && node.getThen().accept(this, value)
&& node.getOtherwise().accept(this, value);
}
@Override
public Boolean visit(CommaExpression node, Void value) {
for (Expression expression : node.getOperands()) {
if (!expression.accept(this, value)) {
return false;
}
}
return true;
}
@Override
public Boolean visit(ElementAccessor node, Void value) {
return node.getBase().accept(this, value) && node.getElement().accept(this, value);
}
@Override
public Boolean visit(PropertyAccessor node, Void value) {
return node.getBase().accept(this, value);
}
@Override
public Boolean visit(IdentifierName node, Void value) {
return true;
}
@Override
public Boolean visit(IdentifierReference node, Void value) {
return true;
}
@Override
public Boolean visit(ThisExpression node, Void value) {
return true;
}
@Override
public Boolean visit(RegularExpressionLiteral node, Void value) {
return true;
}
@Override
public Boolean visit(ObjectLiteral node, Void value) {
for (PropertyDefinition propertyDefinition : node.getProperties()) {
if (!propertyDefinition.accept(this, value)) {
return false;
}
}
return true;
}
@Override
public Boolean visit(PropertyValueDefinition node, Void value) {
return node.getPropertyName().accept(this, value)
&& node.getPropertyValue().accept(this, value);
}
@Override
public Boolean visit(ArrayLiteral node, Void value) {
for (Expression expression : node.getElements()) {
if (!expression.accept(this, value)) {
return false;
}
}
return true;
}
@Override
protected Boolean visit(Literal node, Void value) {
return true;
}
@Override
public Boolean visit(AssignmentExpression node, Void value) {
return node.getLeft().accept(this, value) && node.getRight().accept(this, value);
}
@Override
public Boolean visit(BinaryExpression node, Void value) {
return node.getLeft().accept(this, value) && node.getRight().accept(this, value);
}
@Override
public Boolean visit(UnaryExpression node, Void value) {
return node.getOperand().accept(this, value);
}
}
}