/** * 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.ArrayComprehensionGenerator.EvaluateArrayComprehension; import static com.github.anba.es6draft.compiler.BindingInitializationGenerator.InitializeBoundNameWithInitializer; import static com.github.anba.es6draft.compiler.BindingInitializationGenerator.InitializeBoundNameWithUndefined; import static com.github.anba.es6draft.semantics.StaticSemantics.*; import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import java.util.Objects; import com.github.anba.es6draft.ast.*; import com.github.anba.es6draft.ast.scope.BlockScope; import com.github.anba.es6draft.ast.scope.Name; import com.github.anba.es6draft.ast.scope.Scope; import com.github.anba.es6draft.ast.scope.TopLevelScope; import com.github.anba.es6draft.ast.scope.WithScope; import com.github.anba.es6draft.ast.synthetic.ExpressionMethod; import com.github.anba.es6draft.ast.synthetic.SpreadArrayLiteral; import com.github.anba.es6draft.ast.synthetic.SpreadElementMethod; import com.github.anba.es6draft.compiler.CodeVisitor.LabelState; import com.github.anba.es6draft.compiler.DefaultCodeGenerator.ValType; import com.github.anba.es6draft.compiler.assembler.FieldName; import com.github.anba.es6draft.compiler.assembler.InstructionAssembler; import com.github.anba.es6draft.compiler.assembler.Jump; import com.github.anba.es6draft.compiler.assembler.MethodName; import com.github.anba.es6draft.compiler.assembler.MethodTypeDescriptor; 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.parser.Parser; import com.github.anba.es6draft.runtime.DeclarativeEnvironmentRecord; import com.github.anba.es6draft.runtime.LexicalEnvironment; import com.github.anba.es6draft.runtime.internal.Bootstrap; import com.github.anba.es6draft.runtime.internal.CompatibilityOption; import com.github.anba.es6draft.runtime.internal.NativeCalls; import com.github.anba.es6draft.runtime.objects.Eval.EvalFlags; import com.github.anba.es6draft.runtime.objects.simd.SIMDType; import com.github.anba.es6draft.runtime.types.builtins.ArrayObject; /** * */ final class ExpressionGenerator extends DefaultCodeGenerator<ValType> { private static final class Fields { static final FieldName Intrinsics_ObjectPrototype = FieldName.findStatic(Types.Intrinsics, "ObjectPrototype", Types.Intrinsics); static final FieldName ScriptRuntime_EMPTY_ARRAY = FieldName.findStatic( Types.ScriptRuntime, "EMPTY_ARRAY", Types.Object_); } private static final class Methods { // class: Eval static final MethodName Eval_directEvalWithTranslate = MethodName .findStatic(Types.Eval, "directEval", Type.methodType(Types.Object, Types.Object_, Types.ExecutionContext, Type.INT_TYPE)); static final MethodName Eval_directEval = MethodName.findStatic(Types.Eval, "directEval", Type.methodType(Types.Object, Types.Object, Types.ExecutionContext, Type.INT_TYPE)); // class: ExecutionContext static final MethodName ExecutionContext_resolveThisBinding = MethodName.findVirtual( Types.ExecutionContext, "resolveThisBinding", Type.methodType(Types.Object)); // class: ArrayObject static final MethodName ArrayObject_ArrayCreate = MethodName.findStatic(Types.ArrayObject, "ArrayCreate", Type.methodType(Types.ArrayObject, Types.ExecutionContext, Type.LONG_TYPE)); static final MethodName ArrayObject_DenseArrayCreate = MethodName.findStatic( Types.ArrayObject, "DenseArrayCreate", Type.methodType(Types.ArrayObject, Types.ExecutionContext, Types.Object_)); static final MethodName ArrayObject_SparseArrayCreate = MethodName.findStatic( Types.ArrayObject, "SparseArrayCreate", Type.methodType(Types.ArrayObject, Types.ExecutionContext, Types.Object_)); // class: Math static final MethodName Math_pow = MethodName.findStatic(Types.Math, "pow", Type.methodType(Type.DOUBLE_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE)); // class: OrdinaryObject static final MethodName OrdinaryObject_ObjectCreate = MethodName.findStatic( Types.OrdinaryObject, "ObjectCreate", Type.methodType(Types.OrdinaryObject, Types.ExecutionContext, Types.Intrinsics)); // class: RegExpConstructor static final MethodName RegExpConstructor_RegExpCreate = MethodName.findStatic( Types.RegExpConstructor, "RegExpCreate", Type.methodType(Types.RegExpObject, Types.ExecutionContext, Types.Object, Types.Object)); // class: ScriptRuntime static final MethodName ScriptRuntime_add_str = MethodName.findStatic(Types.ScriptRuntime, "add", Type.methodType(Types.CharSequence, Types.CharSequence, Types.CharSequence, Types.ExecutionContext)); static final MethodName ScriptRuntime_in = MethodName.findStatic(Types.ScriptRuntime, "in", Type.methodType(Type.BOOLEAN_TYPE, Types.Object, Types.Object, Types.ExecutionContext)); static final MethodName ScriptRuntime_typeof = MethodName.findStatic(Types.ScriptRuntime, "typeof", Type.methodType(Types.String, Types.Object)); static final MethodName ScriptRuntime_typeof_Reference = MethodName.findStatic( Types.ScriptRuntime, "typeof", Type.methodType(Types.String, Types.Reference, Types.ExecutionContext)); static final MethodName ScriptRuntime_InstanceofOperator = MethodName.findStatic( Types.ScriptRuntime, "InstanceofOperator", Type.methodType(Type.BOOLEAN_TYPE, Types.Object, Types.Object, Types.ExecutionContext)); static final MethodName ScriptRuntime_ArrayAccumulationSpreadElement = MethodName .findStatic(Types.ScriptRuntime, "ArrayAccumulationSpreadElement", Type.methodType( Type.INT_TYPE, Types.ArrayObject, Type.INT_TYPE, Types.Object, Types.ExecutionContext)); static final MethodName ScriptRuntime_CheckCallable = MethodName.findStatic( Types.ScriptRuntime, "CheckCallable", Type.methodType(Types.Callable, Types.Object, Types.ExecutionContext)); static final MethodName ScriptRuntime_defineLength = MethodName.findStatic( Types.ScriptRuntime, "defineLength", Type.methodType(Type.VOID_TYPE, Types.ArrayObject, Type.INT_TYPE)); static final MethodName ScriptRuntime_defineProperty__int = MethodName.findStatic( Types.ScriptRuntime, "defineProperty", Type.methodType(Type.VOID_TYPE, Types.ArrayObject, Type.INT_TYPE, Types.Object)); static final MethodName ScriptRuntime_directEvalFallbackArguments = MethodName.findStatic( Types.ScriptRuntime, "directEvalFallbackArguments", Type.methodType(Types.Object_, Types.Callable, Types.ExecutionContext, Types.Object, Types.Object_)); static final MethodName ScriptRuntime_directEvalFallbackThisArgument = MethodName .findStatic(Types.ScriptRuntime, "directEvalFallbackThisArgument", Type.methodType(Types.Object, Types.ExecutionContext)); static final MethodName ScriptRuntime_directEvalFallbackHook = MethodName.findStatic( Types.ScriptRuntime, "directEvalFallbackHook", Type.methodType(Types.Callable, Types.ExecutionContext)); static final MethodName ScriptRuntime_EvaluateArrowFunction = MethodName .findStatic(Types.ScriptRuntime, "EvaluateArrowFunction", Type.methodType( Types.OrdinaryFunction, Types.RuntimeInfo$Function, Types.ExecutionContext)); static final MethodName ScriptRuntime_EvaluateAsyncArrowFunction = MethodName.findStatic( Types.ScriptRuntime, "EvaluateAsyncArrowFunction", Type.methodType( Types.OrdinaryAsyncFunction, Types.RuntimeInfo$Function, Types.ExecutionContext)); static final MethodName ScriptRuntime_EvaluateAsyncFunctionExpression = MethodName .findStatic(Types.ScriptRuntime, "EvaluateAsyncFunctionExpression", Type .methodType(Types.OrdinaryAsyncFunction, Types.RuntimeInfo$Function, Types.ExecutionContext)); static final MethodName ScriptRuntime_EvaluateAsyncGeneratorExpression = MethodName .findStatic(Types.ScriptRuntime, "EvaluateAsyncGeneratorExpression", Type .methodType(Types.OrdinaryAsyncGenerator, Types.RuntimeInfo$Function, Types.ExecutionContext)); static final MethodName ScriptRuntime_EvaluateFunctionExpression = MethodName.findStatic( Types.ScriptRuntime, "EvaluateFunctionExpression", Type.methodType( Types.OrdinaryConstructorFunction, Types.RuntimeInfo$Function, Types.ExecutionContext)); static final MethodName ScriptRuntime_EvaluateLegacyFunctionExpression = MethodName.findStatic( Types.ScriptRuntime, "EvaluateLegacyFunctionExpression", Type.methodType( Types.LegacyConstructorFunction, Types.RuntimeInfo$Function, Types.ExecutionContext)); static final MethodName ScriptRuntime_EvaluateConstructorGeneratorComprehension = MethodName.findStatic( Types.ScriptRuntime, "EvaluateConstructorGeneratorComprehension", Type.methodType(Types.GeneratorObject, Types.RuntimeInfo$Function, Types.ExecutionContext)); static final MethodName ScriptRuntime_EvaluateGeneratorComprehension = MethodName.findStatic( Types.ScriptRuntime, "EvaluateGeneratorComprehension", Type.methodType(Types.GeneratorObject, Types.RuntimeInfo$Function, Types.ExecutionContext)); static final MethodName ScriptRuntime_EvaluateLegacyGeneratorComprehension = MethodName .findStatic(Types.ScriptRuntime, "EvaluateLegacyGeneratorComprehension", Type .methodType(Types.GeneratorObject, Types.RuntimeInfo$Function, Types.ExecutionContext)); static final MethodName ScriptRuntime_EvaluateConstructorGeneratorExpression = MethodName.findStatic( Types.ScriptRuntime, "EvaluateConstructorGeneratorExpression", Type.methodType(Types.OrdinaryConstructorGenerator, Types.RuntimeInfo$Function, Types.ExecutionContext)); static final MethodName ScriptRuntime_EvaluateGeneratorExpression = MethodName.findStatic( Types.ScriptRuntime, "EvaluateGeneratorExpression", Type.methodType(Types.OrdinaryGenerator, Types.RuntimeInfo$Function, Types.ExecutionContext)); static final MethodName ScriptRuntime_EvaluateLegacyGeneratorExpression = MethodName .findStatic(Types.ScriptRuntime, "EvaluateLegacyGeneratorExpression", Type .methodType(Types.OrdinaryConstructorGenerator, Types.RuntimeInfo$Function, Types.ExecutionContext)); static final MethodName ScriptRuntime_EvaluateMethodDecorators = MethodName.findStatic( Types.ScriptRuntime, "EvaluateMethodDecorators", Type.methodType(Type.VOID_TYPE, Types.OrdinaryObject, Types.ArrayList, Types.ExecutionContext)); static final MethodName ScriptRuntime_BindThisValue = MethodName.findStatic( Types.ScriptRuntime, "BindThisValue", Type.methodType(Type.VOID_TYPE, Types.ScriptObject, Types.ExecutionContext)); static final MethodName ScriptRuntime_GetNewTargetOrUndefined = MethodName.findStatic( Types.ScriptRuntime, "GetNewTargetOrUndefined", Type.methodType(Types.Object, Types.ExecutionContext)); static final MethodName ScriptRuntime_GetNewTarget = MethodName.findStatic( Types.ScriptRuntime, "GetNewTarget", Type.methodType(Types.Constructor, Types.ExecutionContext)); static final MethodName ScriptRuntime_GetSuperConstructor = MethodName.findStatic( Types.ScriptRuntime, "GetSuperConstructor", Type.methodType(Types.Constructor, Types.ExecutionContext)); static final MethodName ScriptRuntime_IsBuiltinEval = MethodName.findStatic( Types.ScriptRuntime, "IsBuiltinEval", Type.methodType(Type.BOOLEAN_TYPE, Types.Callable, Types.ExecutionContext)); static final MethodName ScriptRuntime_PrepareForTailCall = MethodName.findStatic( Types.ScriptRuntime, "PrepareForTailCall", Type.methodType(Types.Object, Types.Callable, Types.ExecutionContext, Types.Object, Types.Object_)); static final MethodName ScriptRuntime_PrepareForTailCallUnchecked = MethodName.findStatic( Types.ScriptRuntime, "PrepareForTailCall", Type.methodType(Types.Object, Types.Object, Types.ExecutionContext, Types.Object, Types.Object_)); static final MethodName ScriptRuntime_SpreadArray = MethodName.findStatic( Types.ScriptRuntime, "SpreadArray", Type.methodType(Types.Object_, Types.Object, Types.ExecutionContext)); static final MethodName ScriptRuntime_NativeCallSpreadArray = MethodName.findStatic( Types.ScriptRuntime, "NativeCallSpreadArray", Type.methodType(Types.Object_, Types.Object, Types.ExecutionContext)); static final MethodName ScriptRuntime_toFlatArray = MethodName.findStatic( Types.ScriptRuntime, "toFlatArray", Type.methodType(Types.Object_, Types.Object_, Types.ExecutionContext)); static final MethodName ScriptRuntime_toStr = MethodName.findStatic(Types.ScriptRuntime, "toStr", Type.methodType(Types.CharSequence, Types.Object, Types.ExecutionContext)); static final MethodName ScriptRuntime_functionSent = MethodName.findStatic(Types.ScriptRuntime, "functionSent", Type.methodType(Types.Object, Types.ExecutionContext)); // class: StringBuilder static final MethodName StringBuilder_append_Charsequence = MethodName.findVirtual( Types.StringBuilder, "append", Type.methodType(Types.StringBuilder, Types.CharSequence)); static final MethodName StringBuilder_append_String = MethodName.findVirtual( Types.StringBuilder, "append", Type.methodType(Types.StringBuilder, Types.String)); static final MethodName StringBuilder_init = MethodName.findConstructor( Types.StringBuilder, Type.methodType(Type.VOID_TYPE)); static final MethodName StringBuilder_toString = MethodName.findVirtual( Types.StringBuilder, "toString", Type.methodType(Types.String)); } private static final int MAX_JVM_ARGUMENTS = 255; private static final int BOOTSTRAP_ARGUMENTS = 3; private static final int MAX_DYN_ARGUMENTS = MAX_JVM_ARGUMENTS - BOOTSTRAP_ARGUMENTS; public ExpressionGenerator(CodeGenerator codegen) { super(codegen); } private static void invokeDynamicCall(CodeVisitor mv) { // stack: [func(Callable), cx, thisValue, args] -> [result] mv.invokedynamic(Bootstrap.getCallName(), Bootstrap.getCallMethodDescriptor(), Bootstrap.getCallBootstrap()); } private static void invokeDynamicConstruct(CodeVisitor mv) { // stack: [constructor(Constructor), cx, args] -> [result] mv.invokedynamic(Bootstrap.getConstructName(), Bootstrap.getConstructMethodDescriptor(), Bootstrap.getConstructBootstrap()); } private static void invokeDynamicSuper(CodeVisitor mv) { // stack: [constructor(Constructor), cx, newTarget, args] -> [result] mv.invokedynamic(Bootstrap.getSuperName(), Bootstrap.getSuperMethodDescriptor(), Bootstrap.getSuperBootstrap()); } private static ValType invokeDynamicNativeCall(String name, Type[] arguments, CodeVisitor mv) { // stack: [cx, ...args] -> [result] MethodTypeDescriptor desc = MethodTypeDescriptor.methodType(Types.Object, arguments); mv.invokedynamic(NativeCalls.getNativeCallName(name), desc, NativeCalls.getNativeCallBootstrap()); return ValType.of(desc.returnType()); } private static void invokeDynamicOperator(BinaryExpression.Operator operator, CodeVisitor mv) { // stack: [lval, rval, cx?] -> [result] mv.invokedynamic(Bootstrap.getName(operator), Bootstrap.getMethodDescriptor(operator), Bootstrap.getBootstrap(operator)); } private static void invokeDynamicConcat(int numberOfStrings, CodeVisitor mv) { mv.invokedynamic(Bootstrap.getConcatName(), Bootstrap.getConcatMethodDescriptor(numberOfStrings), Bootstrap.getConcatBootstrap()); } private boolean isTailCall(Expression node, CodeVisitor mv) { return !codegen.isEnabled(Compiler.Option.NoTailCall) && mv.isTailCall(node); } /** * [12.3.3.1.1 Runtime Semantics: EvaluateNew(thisCall, constructProduction, arguments)] * * @param node * the <code>NewExpression</code> node * @param mv * the code visitor * @return the returned value type */ private ValType EvaluateNew(NewExpression node, CodeVisitor mv) { /* steps 1-2 (not applicable) */ /* steps 3-5 */ ValType type = node.getExpression().accept(this, mv); mv.toBoxed(type); mv.loadExecutionContext(); /* steps 6-7 */ ArgumentListEvaluation(node, node.getArguments(), mv); /* steps 8-9 */ mv.lineInfo(node); invokeDynamicConstruct(mv); return ValType.Object; } private boolean isEnclosedByWithStatement(Name name, Scope currentScope) { for (Scope scope : currentScope) { if (scope instanceof WithScope) { return true; } if (scope.isDeclared(name)) { return false; } } return codegen.isEnabled(Parser.Option.EnclosedByWithStatement); } private boolean isEnclosedByWithStatement(Scope currentScope) { for (Scope scope : currentScope) { if (scope instanceof WithScope) { return true; } } return codegen.isEnabled(Parser.Option.EnclosedByWithStatement); } private boolean isEnclosedByLexicalDeclaration(Scope currentScope) { final boolean catchVar = codegen.isEnabled(CompatibilityOption.CatchVarStatement); final boolean catchPattern = codegen.isEnabled(CompatibilityOption.CatchVarPattern); TopLevelScope top = currentScope.getTop(); for (Scope scope : currentScope) { if (scope instanceof BlockScope) { if (catchVar) { ScopedNode node = scope.getNode(); if (node instanceof CatchClause) { if (!catchPattern || ((CatchClause) node).getCatchParameter() instanceof BindingIdentifier) { continue; } } } if (!((BlockScope) scope).lexicallyDeclaredNames().isEmpty()) { return true; } } else if (scope == top) { break; } } if (!top.lexicallyDeclaredNames().isEmpty()) { TopLevelNode<?> topNode = top.getNode(); if (topNode instanceof Script) { return ((Script) topNode).isEvalScript(); } return true; } return codegen.isEnabled(Parser.Option.EnclosedByLexicalDeclaration); } private static boolean isGlobalScope(Scope currentScope) { ScopedNode node = currentScope.getNode(); if (node instanceof Script) { return ((Script) node).isGlobalScope(); } return false; } private static boolean isGlobalThis(Scope currentScope) { for (Scope scope = currentScope;;) { TopLevelScope top = scope.getTop(); TopLevelNode<?> node = top.getNode(); if (node instanceof Script) { return ((Script) node).isGlobalThis(); } if (node instanceof Module) { return true; } assert node instanceof FunctionNode : "class=" + node.getClass(); if (((FunctionNode) node).getThisMode() != FunctionNode.ThisMode.Lexical) { return false; } scope = top.getEnclosingScope(); } } private enum CallType { Identifier, IdentifierWith, Eval, EvalWith, Property, Value } private CallType callTypeOf(Expression call, Expression base, Scope scope) { if (base instanceof IdentifierReference) { IdentifierReference ident = (IdentifierReference) base; boolean directEval = call instanceof CallExpression && "eval".equals(ident.getName()); if (isEnclosedByWithStatement(ident.toName(), scope)) { return directEval ? CallType.EvalWith : CallType.IdentifierWith; } return directEval ? CallType.Eval : CallType.Identifier; } if (base instanceof ElementAccessor || base instanceof PropertyAccessor) { return CallType.Property; } if (base instanceof SuperElementAccessor || base instanceof SuperPropertyAccessor) { return CallType.Property; } return CallType.Value; } /** * [12.3.4.2 Runtime Semantics: EvaluateCall( ref, arguments, tailPosition )] * * @param call * the function call expression * @param base * the call expression's base node * @param arguments * the list of function call arguments * @param mv * the code visitor */ private ValType EvaluateCall(Expression call, Expression base, List<Expression> arguments, CodeVisitor mv) { switch (callTypeOf(call, base, mv.getScope())) { case Identifier: return EvaluateCallIdent(call, (IdentifierReference) base, arguments, mv); case IdentifierWith: return EvaluateCallIdentWith(call, (IdentifierReference) base, arguments, mv); case Eval: return EvaluateCallEval(call, (IdentifierReference) base, arguments, mv); case EvalWith: return EvaluateCallEvalWith(call, (IdentifierReference) base, arguments, mv); case Property: return EvaluateCallProperty(call, (LeftHandSideExpression) base, arguments, mv); case Value: return EvaluateCallValue(call, base, arguments, mv); default: throw new AssertionError(); } } private ValType EvaluateCallProperty(Expression call, LeftHandSideExpression base, List<Expression> arguments, CodeVisitor mv) { /* steps 1-4 */ // stack: [] -> [func, thisValue] ReferenceOp.propertyOp(base).referenceValueAndThis(base, mv, codegen); // stack: [func, thisValue] -> [func, cx, thisValue] mv.loadExecutionContext(); mv.swap(); /* step 5 */ // stack: [func, cx, thisValue] -> [result] return EvaluateDirectCall(call, arguments, mv); } private ValType EvaluateCallValue(Expression call, Expression base, List<Expression> arguments, CodeVisitor mv) { // stack: [] -> [func] ValType type = base.accept(this, mv); mv.toBoxed(type); /* steps 1-2 (not applicable) */ // GetValue(...) /* steps 3-4 */ // stack: [func] -> [func, cx, thisValue] mv.loadExecutionContext(); mv.loadUndefined(); /* step 5 */ // stack: [func, cx, thisValue] -> [result] return EvaluateDirectCall(call, arguments, mv); } private ValType EvaluateCallIdent(Expression call, IdentifierReference base, List<Expression> arguments, CodeVisitor mv) { /* steps 1-2 */ // stack: [] -> [func] ReferenceOp.of(base).referenceValue(base, mv, codegen); /* steps 3-4 */ // stack: [func] -> [func, cx, thisValue] mv.loadExecutionContext(); mv.loadUndefined(); /* step 5 */ // stack: [func, cx, thisValue] -> [result] return EvaluateDirectCall(call, arguments, mv); } private ValType EvaluateCallIdentWith(Expression call, IdentifierReference base, List<Expression> arguments, CodeVisitor mv) { /* steps 1-4 */ // stack: [] -> [func, thisValue] ReferenceOp.LOOKUP.referenceValueAndThis(base, mv, codegen); // stack: [func, thisValue] -> [func, cx, thisValue] mv.loadExecutionContext(); mv.swap(); /* step 5 */ // stack: [func, cx, thisValue] -> [result] return EvaluateDirectCall(call, arguments, mv); } private ValType EvaluateCallEval(Expression call, IdentifierReference base, List<Expression> arguments, CodeVisitor mv) { /* steps 1-2 */ // stack: [] -> [func] ReferenceOp.of(base).referenceValue(base, mv, codegen); /* steps 3-4 (omitted) */ /* step 5 */ // stack: [func] -> [result] return EvaluateDirectCallEval(call, arguments, false, mv); } private ValType EvaluateCallEvalWith(Expression call, IdentifierReference base, List<Expression> arguments, CodeVisitor mv) { /* steps 1-4 */ // stack: [] -> [func, thisValue] ReferenceOp.LOOKUP.referenceValueAndThis(base, mv, codegen); /* step 5 */ // stack: [func, thisValue] -> [result] return EvaluateDirectCallEval(call, arguments, true, mv); } /** * [12.3.4.3 Runtime Semantics: EvaluateDirectCall( func, thisValue, arguments, tailPosition )] * * @param call * the function call expression * @param arguments * the function arguments * @param mv * the code visitor */ private ValType EvaluateDirectCall(Expression call, List<Expression> arguments, CodeVisitor mv) { /* steps 1-2 */ // stack: [func, cx, thisValue] -> [func, cx, thisValue, args] ArgumentListEvaluation(call, arguments, mv); /* steps 3-9 */ mv.lineInfo(call); if (isTailCall(call, mv)) { // stack: [func, cx, thisValue, args] -> [<func(Callable), thisValue, args>] mv.invoke(Methods.ScriptRuntime_PrepareForTailCallUnchecked); } else { // stack: [func, cx, thisValue, args] -> [result] invokeDynamicCall(mv); } return ValType.Any; } /** * [12.3.4.3 Runtime Semantics: EvaluateDirectCall( func, thisValue, arguments, tailPosition )] * * @param call * the function call expression * @param arguments * the function arguments * @param hasThisValue * {@code true} if the thisValue is on the stack * @param mv * the code visitor */ private ValType EvaluateDirectCallEval(Expression call, List<Expression> arguments, boolean hasThisValue, CodeVisitor mv) { Jump afterCall = new Jump(), notEval = new Jump(); if (hasThisValue) { // stack: [func, thisValue] -> [thisValue, func] mv.swap(); } /* steps 1-2 (EvaluateDirectCall) */ // stack: [thisValue?, func] -> [thisValue?, args?, func] boolean constantArguments = hasConstantArguments(arguments); if (!constantArguments) { ArgumentListEvaluation(call, arguments, mv); mv.swap(); } // Emit line info after evaluating arguments. mv.lineInfo(call); /* steps 3-4 (EvaluateDirectCall) */ // stack: [thisValue?, args?, func] -> [thisValue?, args?, func(Callable)] mv.loadExecutionContext(); mv.invoke(Methods.ScriptRuntime_CheckCallable); // stack: [thisValue?, args?, func(Callable)] -> [thisValue?, args?, func(Callable)] mv.dup(); mv.loadExecutionContext(); mv.invoke(Methods.ScriptRuntime_IsBuiltinEval); mv.ifeq(notEval); { PerformEval(call, arguments, hasThisValue, afterCall, mv); } mv.mark(notEval); // stack: [thisValue?, args?, func(Callable)] -> [func(Callable), cx, thisValue, args] if (constantArguments) { if (hasThisValue) { // stack: [thisValue, func(Callable)] -> [...] mv.loadExecutionContext(); mv.swap1_2(); ArgumentListEvaluation(call, arguments, mv); } else { // stack: [func(Callable)] -> [...] mv.loadExecutionContext(); mv.loadUndefined(); ArgumentListEvaluation(call, arguments, mv); } } else { if (hasThisValue) { // stack: [thisValue, args, func(Callable)] -> [...] mv.loadExecutionContext(); mv.swap2(); } else { // stack: [args, func(Callable)] -> [...] mv.swap(); mv.loadExecutionContext(); mv.loadUndefined(); mv.swap1_2(); } } if (codegen.isEnabled(CompatibilityOption.Realm)) { // direct-eval fallback hook Jump noEvalHook = new Jump(); mv.loadExecutionContext(); mv.invoke(Methods.ScriptRuntime_directEvalFallbackHook); mv.ifnull(noEvalHook); { // stack: [func(Callable), cx, thisValue, args] -> [args'] mv.invoke(Methods.ScriptRuntime_directEvalFallbackArguments); // stack: [args'] -> [] Variable<Object[]> fallbackArguments = mv.newScratchVariable(Object[].class); mv.store(fallbackArguments); // stack: [] -> [func(Callable), cx, thisValue, args'] mv.loadExecutionContext(); mv.invoke(Methods.ScriptRuntime_directEvalFallbackHook); mv.loadExecutionContext(); mv.loadExecutionContext(); mv.invoke(Methods.ScriptRuntime_directEvalFallbackThisArgument); mv.load(fallbackArguments); mv.freeVariable(fallbackArguments); } mv.mark(noEvalHook); } /* steps 5-9 (EvaluateDirectCall) */ if (isTailCall(call, mv)) { // stack: [func(Callable), cx, thisValue, args'] -> [<func(Callable), thisValue, args>] mv.invoke(Methods.ScriptRuntime_PrepareForTailCall); } else { // stack: [func(Callable), cx, thisValue, args] -> [result] invokeDynamicCall(mv); } mv.mark(afterCall); return ValType.Any; } /** * [18.2.1.1] Direct Call to Eval * * @param call * the function call expression * @param arguments * the list of function call arguments * @param hasThisValue * {@code true} if the thisValue is on the stack * @param afterCall * the label after the call instruction * @param mv * the code visitor */ private void PerformEval(Expression call, List<Expression> arguments, boolean hasThisValue, Jump afterCall, CodeVisitor mv) { int evalFlags = EvalFlags.Direct.getValue(); if (mv.isStrict()) { evalFlags |= EvalFlags.Strict.getValue(); } if (mv.isGlobalCode()) { evalFlags |= EvalFlags.GlobalCode.getValue(); } if (isGlobalScope(mv.getScope())) { evalFlags |= EvalFlags.GlobalScope.getValue(); } if (isGlobalThis(mv.getScope())) { evalFlags |= EvalFlags.GlobalThis.getValue(); } if (isEnclosedByWithStatement(mv.getScope())) { evalFlags |= EvalFlags.EnclosedByWithStatement.getValue(); } if (!mv.isStrict() && isEnclosedByLexicalDeclaration(mv.getScope())) { evalFlags |= EvalFlags.EnclosedByLexicalDeclaration.getValue(); } // stack: [thisValue?, args?, func(Callable)] -> [thisValue?, args?] mv.pop(); // stack: [thisValue?, args?] -> [args?] boolean constantArguments = hasConstantArguments(arguments); if (hasThisValue) { if (constantArguments) { // stack: [thisValue] -> [] mv.pop(); } else { // stack: [thisValue, args] -> [args] mv.swap(); mv.pop(); } } if (codegen.isEnabled(CompatibilityOption.Realm)) { if (constantArguments) { // stack: [] -> [args] ArgumentListEvaluation(call, arguments, mv); } // stack: [args] -> [result] mv.loadExecutionContext(); mv.iconst(evalFlags); mv.invoke(Methods.Eval_directEvalWithTranslate); mv.goTo(afterCall); } else { if (arguments.isEmpty()) { assert constantArguments : "empty arguments list is constant"; // stack: [] -> [result] mv.loadUndefined(); mv.goTo(afterCall); } else if (hasArguments(arguments)) { if (constantArguments) { // stack: [] -> [arg_0] ValType type = arguments.get(0).accept(this, mv); mv.toBoxed(type); } else { // stack: [args] -> [arg_0] mv.iconst(0); mv.aaload(); } mv.loadExecutionContext(); mv.iconst(evalFlags); mv.invoke(Methods.Eval_directEval); mv.goTo(afterCall); } else { assert !constantArguments : "spread arguments list is not constant"; Jump emptyArguments = new Jump(); mv.dup(); mv.arraylength(); mv.ifeq(emptyArguments); { mv.iconst(0); mv.aaload(); mv.loadExecutionContext(); mv.iconst(evalFlags); mv.invoke(Methods.Eval_directEval); mv.goTo(afterCall); } mv.mark(emptyArguments); mv.pop(); mv.loadUndefined(); mv.goTo(afterCall); } } } private static boolean hasArguments(List<Expression> arguments) { for (Expression argument : arguments) { if (!(argument instanceof CallSpreadElement)) { return true; } } return false; } private static boolean hasConstantArguments(List<Expression> arguments) { for (Expression argument : arguments) { if (!(argument instanceof Literal)) { return false; } } return true; } /** * [12.3.6.1 ArgumentListEvaluation] * * @param call * the call expression node * @param arguments * the list of function call arguments * @param mv * the code visitor */ private void ArgumentListEvaluation(Expression call, List<Expression> arguments, CodeVisitor mv) { if (arguments.isEmpty()) { mv.get(Fields.ScriptRuntime_EMPTY_ARRAY); } else if (arguments.size() == 1 && arguments.get(0) instanceof CallSpreadElement) { ((CallSpreadElement) arguments.get(0)).accept(this, mv); } else { boolean hasSpread = false; mv.anewarray(arguments.size(), Types.Object); for (int i = 0, size = arguments.size(); i < size; ++i) { mv.dup(); mv.iconst(i); /* [12.3.5 Argument Lists] ArgumentListEvaluation */ Expression argument = arguments.get(i); hasSpread |= (argument instanceof CallSpreadElement); ValType argType = argument.accept(this, mv); mv.toBoxed(argType); mv.astore(Types.Object); } if (hasSpread) { mv.loadExecutionContext(); mv.lineInfo(call); mv.invoke(Methods.ScriptRuntime_toFlatArray); } } } /* ----------------------------------------------------------------------------------------- */ @Override protected ValType visit(Node node, CodeVisitor mv) { throw new IllegalStateException(String.format("node-class: %s", node.getClass())); } /** * 12.2.4.2.5 Runtime Semantics: Evaluation */ @Override public ValType visit(ArrayComprehension node, CodeVisitor mv) { return EvaluateArrayComprehension(codegen, node, mv); } /** * 12.2.5 Array Initializer * <p> * 12.2.5.3 Runtime Semantics: Evaluation<br> * 12.2.5.2 Runtime Semantics: ArrayAccumulation */ @Override public ValType visit(ArrayLiteral node, CodeVisitor mv) { int elision = 0; boolean hasSpread = false; for (Expression element : node.getElements()) { if (element instanceof Elision) { elision += 1; } else if (element instanceof SpreadElement) { hasSpread = true; } } if (!hasSpread) { // Try to initialize array with faster {Dense, Sparse}ArrayCreate methods int length = node.getElements().size(); float density = (float) (length - elision) / length; if ((density >= 0.25f && length < 0x10) || (density >= 0.75f && length < 0x1000)) { mv.loadExecutionContext(); mv.anewarray(length, Types.Object); int nextIndex = 0; for (Expression element : node.getElements()) { if (element instanceof Elision) { // Elision } else { mv.dup(); mv.iconst(nextIndex); ValType elementType = element.accept(this, mv); mv.toBoxed(elementType); mv.astore(Types.Object); } nextIndex += 1; } if (elision == 0) { mv.invoke(Methods.ArrayObject_DenseArrayCreate); } else { mv.invoke(Methods.ArrayObject_SparseArrayCreate); } return ValType.Object; } } if (!hasSpread) { int length = node.getElements().size(); mv.loadExecutionContext(); mv.lconst(length); // initialize with correct "length" mv.invoke(Methods.ArrayObject_ArrayCreate); int nextIndex = 0; for (Expression element : node.getElements()) { if (element instanceof Elision) { // Elision } else { mv.dup(); mv.iconst(nextIndex); ValType elementType = element.accept(this, mv); mv.toBoxed(elementType); mv.invoke(Methods.ScriptRuntime_defineProperty__int); } nextIndex += 1; } assert nextIndex == length; // Skip Put(array, "length", nextIndex, false), array is initialized with fixed length. } else { mv.loadExecutionContext(); mv.lconst(0); mv.invoke(Methods.ArrayObject_ArrayCreate); mv.dup(); // stack: [array, array, nextIndex] mv.iconst(0); // nextIndex arrayLiteralWithSpread(node, mv); // stack: [array, array, nextIndex] -> [array] mv.invoke(Methods.ScriptRuntime_defineLength); } return ValType.Object; } /** * 12.2.4.1.3 Runtime Semantics: Evaluation<br> * 12.2.4.1.2 Runtime Semantics: Array Accumulation */ @Override public ValType visit(SpreadArrayLiteral node, CodeVisitor mv) { // stack: [array, nextIndex] arrayLiteralWithSpread(node, mv); // stack: [array, nextIndex] -> [nextIndex] mv.swap(); mv.pop(); return ValType.Any; } /** * 12.2.4.1.3 Runtime Semantics: Evaluation<br> * 12.2.4.1.2 Runtime Semantics: Array Accumulation * * @param node * the array literal * @param mv * the expresion visitor */ private void arrayLiteralWithSpread(ArrayLiteral node, CodeVisitor mv) { // stack: [array, nextIndex] int elisionWidth = 0; for (Expression element : node.getElements()) { if (element instanceof Elision) { // Elision elisionWidth += 1; continue; } if (elisionWidth != 0) { mv.iconst(elisionWidth); mv.iadd(); elisionWidth = 0; } if (element instanceof SpreadElement) { element.accept(this, mv); } else { // stack: [array, nextIndex] -> [array, nextIndex, array, nextIndex] mv.dup2(); ValType elementType = element.accept(this, mv); mv.toBoxed(elementType); // stack: [array, nextIndex, array, nextIndex, value] -> [array, nextIndex] mv.invoke(Methods.ScriptRuntime_defineProperty__int); elisionWidth += 1; } } if (elisionWidth != 0) { mv.iconst(elisionWidth); mv.iadd(); } } /** * 12.2.4.1.2 Runtime Semantics: Array Accumulation */ @Override public ValType visit(SpreadElement node, CodeVisitor mv) { // stack: [array, nextIndex] -> [array, array, nextIndex] mv.swap(); mv.dupX1(); mv.swap(); // stack: [array, array, nextIndex] -> [array, array, nextIndex, value] Expression spread = node.getExpression(); ValType spreadType = spread.accept(this, mv); mv.toBoxed(spreadType); // stack: [array, array, nextIndex, value] -> [array, nextIndex'] mv.loadExecutionContext(); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_ArrayAccumulationSpreadElement); return ValType.Any; } /** * 12.2.4.1.3 Runtime Semantics: Evaluation<br> * 12.2.4.1.2 Runtime Semantics: Array Accumulation */ @Override public ValType visit(SpreadElementMethod node, CodeVisitor mv) { MethodName method = codegen.compile(node, mv); boolean hasResume = node.hasResumePoint(); mv.enterVariableScope(); Variable<ArrayObject> array = mv.newVariable("array", ArrayObject.class); Variable<Integer> nextIndex = mv.newVariable("nextIndex", int.class); // stack: [array, nextIndex] -> [array] mv.store(nextIndex); mv.dup(); mv.store(array); // stack: [array] -> [array, nextIndex'] mv.lineInfo(0); // 0 = hint for stacktraces to omit this frame if (hasResume) { mv.callWithSuspend(method, array, nextIndex); } else { mv.call(method, array, nextIndex); } mv.exitVariableScope(); return ValType.Any; } /** * 14.2.10 Runtime Semantics: Evaluation */ @Override public ValType visit(ArrowFunction node, CodeVisitor mv) { MethodName method = codegen.compile(node); /* steps 1-4 */ mv.invoke(method); mv.loadExecutionContext(); mv.invoke(Methods.ScriptRuntime_EvaluateArrowFunction); /* step 5 */ return ValType.Object; } /** * 12.14 Assignment Operators * <p> * 12.14.4 Runtime Semantics: Evaluation */ @Override public ValType visit(AssignmentExpression node, CodeVisitor mv) { return assignmentOp(node).emit(node, mv, this); } private static AssignmentOp assignmentOp(AssignmentExpression node) { switch (node.getOperator()) { case ASSIGN_ADD: return CompoundAssignmentOp.ADD; case ASSIGN_BITAND: return CompoundAssignmentOp.BITAND; case ASSIGN_BITOR: return CompoundAssignmentOp.BITOR; case ASSIGN_BITXOR: return CompoundAssignmentOp.BITXOR; case ASSIGN_DIV: return CompoundAssignmentOp.DIV; case ASSIGN_EXP: return CompoundAssignmentOp.EXP; case ASSIGN_MOD: return CompoundAssignmentOp.MOD; case ASSIGN_MUL: return CompoundAssignmentOp.MUL; case ASSIGN_SHL: return CompoundAssignmentOp.SHL; case ASSIGN_SHR: return CompoundAssignmentOp.SHR; case ASSIGN_SUB: return CompoundAssignmentOp.SUB; case ASSIGN_USHR: return CompoundAssignmentOp.USHR; case ASSIGN: if (node.getLeft() instanceof AssignmentPattern) { return AssignmentOp.DESTRUCTURING; } return AssignmentOp.ASSIGN; default: throw new AssertionError(Objects.toString(node.getOperator(), "<null>")); } } private static abstract class AssignmentOp { abstract ValType emit(AssignmentExpression node, CodeVisitor mv, ExpressionGenerator gen); static final AssignmentOp DESTRUCTURING = new AssignmentOp() { @Override ValType emit(AssignmentExpression node, CodeVisitor mv, ExpressionGenerator gen) { AssignmentPattern left = (AssignmentPattern) node.getLeft(); Expression right = node.getRight(); ValType rtype = right.accept(gen, mv); if (node.hasCompletion()) { mv.dup(rtype); } mv.toBoxed(rtype); DestructuringAssignmentGenerator.DestructuringAssignment(gen.codegen, left, mv); return node.hasCompletion() ? rtype : ValType.Empty; } }; static final AssignmentOp ASSIGN = new AssignmentOp() { @Override ValType emit(AssignmentExpression node, CodeVisitor mv, ExpressionGenerator gen) { LeftHandSideExpression left = node.getLeft(); Expression right = node.getRight(); ReferenceOp<LeftHandSideExpression> op = ReferenceOp.of(left); // stack: [] -> [lref, rval] ValType ltype = op.reference(left, mv, gen.codegen); ValType rtype = right.accept(gen, mv); if (IsAnonymousFunctionDefinition(right) && IsIdentifierRef(left)) { SetFunctionName(right, ((IdentifierReference) left).getName(), mv); } return op.putValue(left, ltype, rtype, node.hasCompletion(), mv); } }; } private static abstract class CompoundAssignmentOp extends AssignmentOp { abstract ValType convertLeft(ValType type, CodeVisitor mv); abstract ValType convertRight(ValType type, CodeVisitor mv); abstract ValType operation(CodeVisitor mv); @Override final ValType emit(AssignmentExpression node, CodeVisitor mv, ExpressionGenerator gen) { LeftHandSideExpression left = node.getLeft(); Expression right = node.getRight(); ReferenceOp<LeftHandSideExpression> op = ReferenceOp.of(left); ValType ltype = op.referenceForUpdate(left, mv, gen.codegen); ValType vtype = op.getValue(left, ltype, mv); // lref lval if (right instanceof Literal) { vtype = convertLeft(vtype, mv); } ValType rtype = right.accept(gen, mv); if (!(right instanceof Literal)) { mv.swap(vtype, rtype); vtype = convertLeft(vtype, mv); mv.swap(rtype, vtype); } convertRight(rtype, mv); // lref lval rval ValType result = operation(mv); // r lref r return op.putValue(left, ltype, result, node.hasCompletion(), mv); } static abstract class ArithmeticCompoundAssignmentOp extends CompoundAssignmentOp { @Override final ValType convertLeft(ValType type, CodeVisitor mv) { ToNumber(type, mv); return ValType.Number; } @Override final ValType convertRight(ValType type, CodeVisitor mv) { ToNumber(type, mv); return ValType.Number; } } static abstract class IntegerCompoundAssignmentOp extends CompoundAssignmentOp { @Override final ValType convertLeft(ValType type, CodeVisitor mv) { ToInt32(type, mv); return ValType.Number_int; } @Override final ValType convertRight(ValType type, CodeVisitor mv) { ToInt32(type, mv); return ValType.Number_int; } } // Extension: Exponentiation Operator static final CompoundAssignmentOp EXP = new ArithmeticCompoundAssignmentOp() { @Override ValType operation(CodeVisitor mv) { mv.invoke(Methods.Math_pow); return ValType.Number; } }; // 12.6 Multiplicative Operators static final CompoundAssignmentOp MUL = new ArithmeticCompoundAssignmentOp() { @Override ValType operation(CodeVisitor mv) { mv.dmul(); return ValType.Number; } }; // 12.6 Multiplicative Operators static final CompoundAssignmentOp DIV = new ArithmeticCompoundAssignmentOp() { @Override ValType operation(CodeVisitor mv) { mv.ddiv(); return ValType.Number; } }; // 12.6 Multiplicative Operators static final CompoundAssignmentOp MOD = new ArithmeticCompoundAssignmentOp() { @Override ValType operation(CodeVisitor mv) { mv.drem(); return ValType.Number; } }; // 12.7.3 The Addition operator ( + ) static final AssignmentOp ADD = new AssignmentOp() { @Override ValType emit(AssignmentExpression node, CodeVisitor mv, ExpressionGenerator gen) { LeftHandSideExpression left = node.getLeft(); Expression right = node.getRight(); ReferenceOp<LeftHandSideExpression> op = ReferenceOp.of(left); ValType ltype = op.referenceForUpdate(left, mv, gen.codegen); ValType vtype = op.getValue(left, ltype, mv); ValType result; if (right instanceof StringLiteral || right instanceof TemplateLiteral) { assert !(right instanceof TemplateLiteral && ((TemplateLiteral) right) .isTagged()); // x += "..." toStringForConcat(left, vtype, mv); // lref lval(string) if (!(right instanceof StringLiteral && ((StringLiteral) right).getValue() .isEmpty())) { ValType rtype = right.accept(gen, mv); addStrings(ValType.String, rtype, mv); } result = ValType.String; } else { // lref lval ValType rtype = right.accept(gen, mv); mv.toBoxed(rtype); // lref lval rval mv.loadExecutionContext(); invokeDynamicOperator(BinaryExpression.Operator.ADD, mv); result = ValType.Any; } // r lref r return op.putValue(left, ltype, result, node.hasCompletion(), mv); } }; // 12.7.4 The Subtraction Operator ( - ) static final CompoundAssignmentOp SUB = new ArithmeticCompoundAssignmentOp() { @Override ValType operation(CodeVisitor mv) { mv.dsub(); return ValType.Number; } }; // 12.8.3 The Left Shift Operator ( << ) static final CompoundAssignmentOp SHL = new IntegerCompoundAssignmentOp() { @Override ValType operation(CodeVisitor mv) { mv.iconst(0x1F); mv.iand(); mv.ishl(); return ValType.Number_int; } }; // 12.8.4 The Signed Right Shift Operator ( >> ) static final CompoundAssignmentOp SHR = new IntegerCompoundAssignmentOp() { @Override ValType operation(CodeVisitor mv) { mv.iconst(0x1F); mv.iand(); mv.ishr(); return ValType.Number_int; } }; // 12.8.5 The Unsigned Right Shift Operator ( >>> ) static final CompoundAssignmentOp USHR = new CompoundAssignmentOp() { @Override ValType convertLeft(ValType type, CodeVisitor mv) { ToUint32(type, mv); return ValType.Number_uint; } @Override ValType convertRight(ValType type, CodeVisitor mv) { ToInt32(type, mv); return ValType.Number_int; } @Override ValType operation(CodeVisitor mv) { mv.iconst(0x1F); mv.iand(); mv.lushr(); return ValType.Number_uint; } }; // 12.11 Binary Bitwise Operators ( & ) static final CompoundAssignmentOp BITAND = new IntegerCompoundAssignmentOp() { @Override ValType operation(CodeVisitor mv) { mv.iand(); return ValType.Number_int; } }; // 12.11 Binary Bitwise Operators ( ^ ) static final CompoundAssignmentOp BITXOR = new IntegerCompoundAssignmentOp() { @Override ValType operation(CodeVisitor mv) { mv.ixor(); return ValType.Number_int; } }; // 12.11 Binary Bitwise Operators ( | ) static final CompoundAssignmentOp BITOR = new IntegerCompoundAssignmentOp() { @Override ValType operation(CodeVisitor mv) { mv.ior(); return ValType.Number_int; } }; } @Override public ValType visit(AsyncArrowFunction node, CodeVisitor mv) { MethodName method = codegen.compile(node); /* steps 1-4 */ mv.invoke(method); mv.loadExecutionContext(); mv.invoke(Methods.ScriptRuntime_EvaluateAsyncArrowFunction); /* step 5 */ return ValType.Object; } /** * Extension: Async Function Definitions */ @Override public ValType visit(AsyncFunctionExpression node, CodeVisitor mv) { MethodName method = codegen.compile(node); /* steps 1-5/10 */ mv.invoke(method); mv.loadExecutionContext(); mv.invoke(Methods.ScriptRuntime_EvaluateAsyncFunctionExpression); /* step 6/11 */ return ValType.Object; } /** * Extension: Async Generator Function Definitions */ @Override public ValType visit(AsyncGeneratorExpression node, CodeVisitor mv) { MethodName method = codegen.compile(node); /* steps 1-5/10 */ mv.invoke(method); mv.loadExecutionContext(); mv.invoke(Methods.ScriptRuntime_EvaluateAsyncGeneratorExpression); /* step 6/11 */ return ValType.Object; } /** * Extension: Async Function Definitions */ @Override public ValType visit(AwaitExpression node, CodeVisitor mv) { ValType type = node.getExpression().accept(this, mv); mv.toBoxed(type); await(node, mv); return ValType.Any; } /** * 12.6.3 Runtime Semantics: Evaluation<br> * 12.7.3.1 Runtime Semantics: Evaluation<br> * 12.7.4.1 Runtime Semantics: Evaluation<br> * 12.8.3.1 Runtime Semantics: Evaluation<br> * 12.8.4.1 Runtime Semantics: Evaluation<br> * 12.8.5.1 Runtime Semantics: Evaluation<br> * 12.9.3 Runtime Semantics: Evaluation<br> * 12.10.3 Runtime Semantics: Evaluation<br> * 12.11.3 Runtime Semantics: Evaluation<br> * 12.12.3 Runtime Semantics: Evaluation<br> */ @Override public ValType visit(BinaryExpression node, CodeVisitor mv) { return binaryOp(node).emit(node, mv, this); } private static BinaryOp binaryOp(BinaryExpression node) { switch (node.getOperator()) { case ADD: return BinaryOp.ADD; case AND: return BinaryOp.AND; case BITAND: return BinaryOp.BITAND; case BITOR: return BinaryOp.BITOR; case BITXOR: return BinaryOp.BITXOR; case DIV: return BinaryOp.DIV; case EQ: return BinaryOp.EQ; case EXP: return BinaryOp.EXP; case GE: return BinaryOp.GE; case GT: return BinaryOp.GT; case IN: return BinaryOp.IN; case INSTANCEOF: return BinaryOp.INSTANCEOF; case LE: return BinaryOp.LE; case LT: return BinaryOp.LT; case MOD: return BinaryOp.MOD; case MUL: return BinaryOp.MUL; case NE: return BinaryOp.NE; case OR: return BinaryOp.OR; case SHEQ: return BinaryOp.SHEQ; case SHL: return BinaryOp.SHL; case SHNE: return BinaryOp.SHNE; case SHR: return BinaryOp.SHR; case SUB: return BinaryOp.SUB; case USHR: return BinaryOp.USHR; default: throw new AssertionError(Objects.toString(node.getOperator(), "<null>")); } } private static abstract class BinaryOp { abstract ValType emit(BinaryExpression node, CodeVisitor mv, ExpressionGenerator gen); static abstract class ConvertOp extends BinaryOp { abstract ValType operation(CodeVisitor mv); abstract ValType convertLeft(ValType type, CodeVisitor mv); abstract ValType convertRight(ValType type, CodeVisitor mv); @Override final ValType emit(BinaryExpression node, CodeVisitor mv, ExpressionGenerator gen) { Expression left = node.getLeft(); Expression right = node.getRight(); ValType ltype = left.accept(gen, mv); if (ltype.isPrimitive() || right instanceof Literal) { ltype = convertLeft(ltype, mv); } ValType rtype = right.accept(gen, mv); if (!(ltype.isPrimitive() || right instanceof Literal)) { mv.swap(ltype, rtype); ltype = convertLeft(ltype, mv); mv.swap(rtype, ltype); } convertRight(rtype, mv); return operation(mv); } } static abstract class ArithmeticOp extends ConvertOp { @Override final ValType convertLeft(ValType type, CodeVisitor mv) { ToNumber(type, mv); return ValType.Number; } @Override final ValType convertRight(ValType type, CodeVisitor mv) { ToNumber(type, mv); return ValType.Number; } } static abstract class IntegerOp extends ConvertOp { @Override final ValType convertLeft(ValType type, CodeVisitor mv) { ToInt32(type, mv); return ValType.Number_int; } @Override final ValType convertRight(ValType type, CodeVisitor mv) { ToInt32(type, mv); return ValType.Number_int; } } static abstract class RelationalOp extends BinaryOp { abstract void operation(CodeVisitor mv); @Override final ValType emit(BinaryExpression node, CodeVisitor mv, ExpressionGenerator gen) { mv.toBoxed(node.getLeft().accept(gen, mv)); mv.toBoxed(node.getRight().accept(gen, mv)); mv.lineInfo(node); operation(mv); return ValType.Boolean; } } static final class EqMethods { // class: AbstractOperations static final MethodName AbstractOperations_IsCallable = MethodName.findStatic(Types.AbstractOperations, "IsCallable", Type.methodType(Type.BOOLEAN_TYPE, Types.Object)); // class: Type static final MethodName Type_isBoolean = MethodName.findStatic(Types._Type, "isBoolean", Type.methodType(Type.BOOLEAN_TYPE, Types.Object)); static final MethodName Type_isNull = MethodName.findStatic(Types._Type, "isNull", Type.methodType(Type.BOOLEAN_TYPE, Types.Object)); static final MethodName Type_isNumber = MethodName.findStatic(Types._Type, "isNumber", Type.methodType(Type.BOOLEAN_TYPE, Types.Object)); static final MethodName Type_isString = MethodName.findStatic(Types._Type, "isString", Type.methodType(Type.BOOLEAN_TYPE, Types.Object)); static final MethodName Type_isSymbol = MethodName.findStatic(Types._Type, "isSymbol", Type.methodType(Type.BOOLEAN_TYPE, Types.Object)); static final MethodName Type_isUndefinedOrNull = MethodName.findStatic(Types._Type, "isUndefinedOrNull", Type.methodType(Type.BOOLEAN_TYPE, Types.Object)); static final MethodName Type_isUndefined = MethodName.findStatic(Types._Type, "isUndefined", Type.methodType(Type.BOOLEAN_TYPE, Types.Object)); // class: Reference static final MethodName Reference_isUnresolvableReference = MethodName.findVirtual(Types.Reference, "isUnresolvableReference", Type.methodType(Type.BOOLEAN_TYPE)); // class: ScriptRuntime static final MethodName ScriptRuntime_compare = MethodName.findStatic(Types.ScriptRuntime, "compare", Type.methodType(Type.BOOLEAN_TYPE, Types.CharSequence, Types.CharSequence)); static final MethodName ScriptRuntime_isNonCallableObjectOrNull = MethodName.findStatic(Types.ScriptRuntime, "isNonCallableObjectOrNull", Type.methodType(Type.BOOLEAN_TYPE, Types.Object)); static final MethodName ScriptRuntime_isSIMDType = MethodName.findStatic(Types.ScriptRuntime, "isSIMDType", Type.methodType(Type.BOOLEAN_TYPE, Types.Object, Types.SIMDType)); } static abstract class EqualityOp extends BinaryOp { @Override final ValType emit(BinaryExpression node, CodeVisitor mv, ExpressionGenerator gen) { Expression left = node.getLeft(); Expression right = node.getRight(); if (left instanceof Literal) { emitLiteral(node, (Literal) left, right, mv, gen); } else if (right instanceof Literal) { emitLiteral(node, (Literal) right, left, mv, gen); } else { ValType ltype = left.accept(gen, mv); if (ltype.isPrimitive()) { emitPrimitive(node, ltype, right, mv, gen); } else { emitGeneric(node, ltype, right, mv, gen); } } return ValType.Boolean; } protected abstract void emitGeneric(BinaryExpression node, ValType ltype, Expression right, CodeVisitor mv, ExpressionGenerator gen); protected abstract void emitLiteral(BinaryExpression node, ValType type, NullLiteral literal, CodeVisitor mv, ExpressionGenerator gen); protected abstract void emitLiteral(BinaryExpression node, ValType type, BooleanLiteral literal, CodeVisitor mv, ExpressionGenerator gen); protected abstract void emitLiteral(BinaryExpression node, ValType type, NumericLiteral literal, CodeVisitor mv, ExpressionGenerator gen); protected abstract void emitLiteral(BinaryExpression node, ValType type, StringLiteral literal, CodeVisitor mv, ExpressionGenerator gen); private void emitLiteral(BinaryExpression node, Literal literal, Expression expr, CodeVisitor mv, ExpressionGenerator gen) { if (literal instanceof NullLiteral) { ValType type = expr.accept(gen, mv); emitLiteral(node, type, (NullLiteral) literal, mv, gen); } else if (literal instanceof BooleanLiteral) { BooleanLiteral bool = (BooleanLiteral) literal; ValType type = expr.accept(gen, mv); if (type == ValType.Boolean) { if (bool.getValue() == false) { mv.not(); } } else { emitLiteral(node, type, bool, mv, gen); } } else if (literal instanceof NumericLiteral) { NumericLiteral numeric = (NumericLiteral) literal; ValType type = expr.accept(gen, mv); if (type.isNumeric()) { if (type == ValType.Number_int && numeric.isInt()) { if (numeric.intValue() == 0) { compareInt0(mv); } else { mv.iconst(numeric.intValue()); compareInt(mv); } } else if (type == ValType.Number_uint && numeric.isInt()) { mv.lconst(numeric.intValue()); compareLong(mv); } else { ToNumber(type, mv); mv.dconst(numeric.doubleValue()); compareDouble(mv); } } else { emitLiteral(node, type, numeric, mv, gen); } } else { assert literal instanceof StringLiteral; StringLiteral string = (StringLiteral) literal; if (isTypeof(expr) && isTypeString(string.getValue())) { emitTypeCheck(string.getValue(), ((UnaryExpression) expr).getOperand(), mv, gen); } else { ValType type = expr.accept(gen, mv); if (type == ValType.String) { mv.aconst(string.getValue()); compareString(mv); } else { emitLiteral(node, type, string, mv, gen); } } } } private void emitPrimitive(BinaryExpression node, ValType ltype, Expression right, CodeVisitor mv, ExpressionGenerator gen) { ValType expected = expressionType(right); if (ltype == expected) { ValType rtype = right.accept(gen, mv); assert rtype == expected; switch (ltype) { case Boolean: case Number_int: compareInt(mv); break; case Number_uint: compareLong(mv); break; case Number: compareDouble(mv); break; case String: compareString(mv); break; case Undefined: case Null: compareReference(mv); break; default: throw new AssertionError(); } } else if (ltype.isNumeric() && expected.isNumeric()) { ToNumber(ltype, mv); ValType rtype = right.accept(gen, mv); assert rtype == expected; ToNumber(rtype, mv); compareDouble(mv); } else { emitGeneric(node, ltype, right, mv, gen); } } private void compareString(CodeVisitor mv) { mv.invoke(EqMethods.ScriptRuntime_compare); } private void compareReference(CodeVisitor mv) { Jump notSame = new Jump(), end = new Jump(); mv.ifacmpne(notSame); compareTail(end, notSame, mv); } private void compareInt0(CodeVisitor mv) { Jump notSame = new Jump(), end = new Jump(); mv.ifne(notSame); compareTail(end, notSame, mv); } private void compareInt(CodeVisitor mv) { Jump notSame = new Jump(), end = new Jump(); mv.ificmpne(notSame); compareTail(end, notSame, mv); } private void compareLong(CodeVisitor mv) { Jump notSame = new Jump(), end = new Jump(); mv.lcmp(); mv.ifne(notSame); compareTail(end, notSame, mv); } private void compareDouble(CodeVisitor mv) { Jump notSame = new Jump(), end = new Jump(); mv.dcmpl(); mv.ifne(notSame); compareTail(end, notSame, mv); } private void compareTail(Jump end, Jump notSame, CodeVisitor mv) { mv.iconst(true); mv.goTo(end); mv.mark(notSame); mv.iconst(false); mv.mark(end); } private void emitTypeCheck(String name, Expression operand, CodeVisitor mv, ExpressionGenerator gen) { if (operand instanceof IdentifierReference) { IdentifierReference ident = (IdentifierReference) operand; Name resolvedName = ident.getResolvedName(); if (resolvedName == null || !resolvedName.isLocal()) { // stack: [] -> [ref, ref] ValType reference = ReferenceOp.LOOKUP.reference(ident, mv, gen.codegen); mv.dup(reference); // stack: [ref, ref] -> [ref, unresolvable] Jump unresolvable = new Jump(), end = new Jump(); mv.invoke(EqMethods.Reference_isUnresolvableReference); mv.ifeq(unresolvable); { mv.pop(reference); mv.iconst("undefined".equals(name)); mv.goTo(end); } mv.mark(unresolvable); // stack: [ref] -> [val] ReferenceOp.LOOKUP.getValue(ident, reference, mv); emitTypeCheckGeneric(name, mv); mv.mark(end); return; } } ValType type = operand.accept(gen, mv); if (type != ValType.Any) { emitTypeCheckConst(name, type, mv); } else { emitTypeCheckGeneric(name, mv); } } private void emitTypeCheckConst(String name, ValType type, CodeVisitor mv) { assert type != ValType.Any; boolean result; switch (name) { case "undefined": result = type == ValType.Undefined; break; case "object": if (type == ValType.Object) { emitTypeCheckGeneric(name, mv); return; } result = type == ValType.Null; break; case "boolean": result = type == ValType.Boolean; break; case "number": result = type.isNumeric(); break; case "string": result = type == ValType.String; break; case "function": { if (type == ValType.Object) { emitTypeCheckGeneric(name, mv); return; } result = false; break; } default: result = false; break; } mv.pop(type); mv.iconst(result); } private void emitTypeCheckGeneric(String name, CodeVisitor mv) { MethodName typeCheck = typeCheck(name); if (typeCheck == EqMethods.ScriptRuntime_isSIMDType) { mv.getstatic(Types.SIMDType, SIMDType.from(name).name(), Types.SIMDType); } mv.invoke(typeCheck); } private MethodName typeCheck(String name) { switch (name) { case "undefined": return EqMethods.Type_isUndefined; case "object": return EqMethods.ScriptRuntime_isNonCallableObjectOrNull; case "boolean": return EqMethods.Type_isBoolean; case "number": return EqMethods.Type_isNumber; case "string": return EqMethods.Type_isString; case "symbol": return EqMethods.Type_isSymbol; case "function": return EqMethods.AbstractOperations_IsCallable; default: return EqMethods.ScriptRuntime_isSIMDType; } } private boolean isTypeof(Expression expr) { if (!(expr instanceof UnaryExpression)) { return false; } return ((UnaryExpression) expr).getOperator() == UnaryExpression.Operator.TYPEOF; } private boolean isTypeString(String s) { switch (s) { case "undefined": case "object": case "boolean": case "number": case "string": case "symbol": case "function": return true; case "float64x2": case "float32x4": case "int32x4": case "int16x8": case "int8x16": case "uint32x4": case "uint16x8": case "uint8x16": case "bool64x2": case "bool32x4": case "bool16x8": case "bool8x16": return true; default: return false; } } } static abstract class LogicalOp extends BinaryOp { abstract void operation(Jump jump, CodeVisitor mv); @Override final ValType emit(BinaryExpression node, CodeVisitor mv, ExpressionGenerator gen) { Expression left = node.getLeft(); Expression right = node.getRight(); Jump after = new Jump(); ValType ltype = left.accept(gen, mv); if (!node.hasCompletion()) { ToBoolean(ltype, mv); operation(after, mv); ValType rtype = right.emptyCompletion().accept(gen, mv); mv.pop(rtype); mv.mark(after); return ValType.Empty; } if (ltype == ValType.Boolean && expressionType(right) == ValType.Boolean) { mv.dup(); operation(after, mv); mv.pop(); ValType rtype = right.accept(gen, mv); assert rtype == ValType.Boolean; mv.mark(after); return ValType.Boolean; } mv.toBoxed(ltype); mv.dup(); ToBoolean(ValType.Any, mv); operation(after, mv); mv.pop(); ValType rtype = right.accept(gen, mv); mv.toBoxed(rtype); mv.mark(after); return ValType.Any; } } // Extension: Exponentiation Operator static final ArithmeticOp EXP = new ArithmeticOp() { @Override ValType operation(CodeVisitor mv) { mv.invoke(Methods.Math_pow); return ValType.Number; } }; // 12.6 Multiplicative Operators static final ArithmeticOp MUL = new ArithmeticOp() { @Override ValType operation(CodeVisitor mv) { mv.dmul(); return ValType.Number; } }; // 12.6 Multiplicative Operators static final ArithmeticOp DIV = new ArithmeticOp() { @Override ValType operation(CodeVisitor mv) { mv.ddiv(); return ValType.Number; } }; // 12.6 Multiplicative Operators static final ArithmeticOp MOD = new ArithmeticOp() { @Override ValType operation(CodeVisitor mv) { mv.drem(); return ValType.Number; } }; // 12.7.3 The Addition operator ( + ) static final BinaryOp ADD = new BinaryOp() { @Override ValType emit(BinaryExpression node, CodeVisitor mv, ExpressionGenerator gen) { Expression left = node.getLeft(); Expression right = node.getRight(); // Handle 'a' + b + c + ... if (left instanceof BinaryExpression && isStringConcat((BinaryExpression) left) && stringConcat(node, mv, gen)) { return ValType.String; } // Handle 'a' + b if (left instanceof StringLiteral) { String leftValue = ((StringLiteral) left).getValue(); if (right instanceof StringLiteral) { // 'a' + 'b' mv.aconst(leftValue + ((StringLiteral) right).getValue()); return ValType.String; } if (leftValue.isEmpty()) { // "" + x return evalToString(right, mv, gen); } // "..." + x ValType ltype = left.accept(gen, mv); ValType rtype = evalToString(right, mv, gen); return addStrings(ltype, rtype, mv); } else if (right instanceof StringLiteral) { if (((StringLiteral) right).getValue().isEmpty()) { // x + "" return evalToString(left, mv, gen); } // x + "..." ValType ltype = evalToString(left, mv, gen); ValType rtype = right.accept(gen, mv); return addStrings(ltype, rtype, mv); } else if (left instanceof TemplateLiteral) { // `...` + x assert !((TemplateLiteral) left).isTagged(); ValType ltype = left.accept(gen, mv); ValType rtype = evalToString(right, mv, gen); return addStrings(ltype, rtype, mv); } else if (right instanceof TemplateLiteral) { // x + `...` assert !((TemplateLiteral) right).isTagged(); ValType ltype = left.accept(gen, mv); if (ltype.isPrimitive()) { ToString(ltype, mv); } ValType rtype = right.accept(gen, mv); if (!ltype.isPrimitive()) { mv.swap(ltype, rtype); toStringForConcat(left, ltype, mv); mv.swap(rtype, ValType.String); } return addStrings(ValType.String, rtype, mv); } ValType ltype = left.accept(gen, mv); if (ltype == ValType.String) { ValType rtype = evalToString(right, mv, gen); return addStrings(ltype, rtype, mv); } if (ltype.isNumeric()) { ValType expected = expressionType(right); if (expected.isPrimitive() && expected != ValType.String) { ToNumber(ltype, mv); ValType rtype = right.accept(gen, mv); assert rtype.isPrimitive() && rtype != ValType.String : String.format( "expected=%s, actual=%s", expected, rtype); ToNumber(rtype, mv); mv.dadd(); return ValType.Number; } } if (right instanceof BinaryExpression && isStringConcat((BinaryExpression) right)) { if (ltype.isPrimitive()) { ToString(ltype, mv); } ValType rtype = right.accept(gen, mv); assert rtype == ValType.String; if (!ltype.isPrimitive()) { mv.swap(ltype, rtype); toStringForConcat(left, ltype, mv); mv.swap(rtype, ValType.String); } return addStrings(ValType.String, rtype, mv); } mv.toBoxed(ltype); ValType rtype = right.accept(gen, mv); if (rtype == ValType.String) { mv.swap(ValType.Any, rtype); if (ltype.isPrimitive()) { mv.toUnboxed(ltype); } toStringForConcat(left, ltype, mv); mv.swap(rtype, ValType.String); return addStrings(ValType.String, rtype, mv); } mv.toBoxed(rtype); mv.loadExecutionContext(); invokeDynamicOperator(node.getOperator(), mv); return ValType.Any; } private boolean isStringConcat(BinaryExpression binary) { for (;;) { if (binary.getOperator() != BinaryExpression.Operator.ADD) { return false; } Expression left = binary.getLeft(); if (isString(left) || isString(binary.getRight())) { return true; } if (!(left instanceof BinaryExpression)) { return false; } binary = (BinaryExpression) left; } } private boolean isString(Expression node) { return node instanceof StringLiteral || node instanceof TemplateLiteral; } private boolean stringConcat(BinaryExpression binary, CodeVisitor mv, ExpressionGenerator gen) { if (isConstantStringConcat(binary)) { StringBuilder sb = new StringBuilder(); stringConcat(binary, sb); mv.aconst(sb.toString()); return true; } int strings = countStringConcats(binary); if (strings > MAX_DYN_ARGUMENTS) { return false; } if (strings == 0) { mv.aconst(""); } else if (strings == 1) { stringConcatWith(binary, mv, gen); } else { mv.loadExecutionContext(); stringConcatWith(binary, mv, gen); invokeDynamicConcat(strings, mv); } return true; } private boolean isConstantStringConcat(BinaryExpression node) { for (;;) { if (node.getOperator() != BinaryExpression.Operator.ADD) { return false; } Expression left = node.getLeft(); Expression right = node.getRight(); if (left instanceof StringLiteral && right instanceof StringLiteral) { return true; } else if (left instanceof BinaryExpression && right instanceof StringLiteral) { node = (BinaryExpression) left; } else if (left instanceof StringLiteral && right instanceof BinaryExpression) { node = (BinaryExpression) right; } else if (left instanceof BinaryExpression && right instanceof BinaryExpression && isConstantStringConcat((BinaryExpression) right)) { node = (BinaryExpression) left; } else { return false; } } } private void stringConcat(Expression node, StringBuilder sb) { if (node instanceof StringLiteral) { sb.append(((StringLiteral) node).getValue()); } else { assert node instanceof BinaryExpression; stringConcat(((BinaryExpression) node).getLeft(), sb); stringConcat(((BinaryExpression) node).getRight(), sb); } } private ValType stringConcatWith(BinaryExpression node, CodeVisitor mv, ExpressionGenerator gen) { ArrayList<Expression> list = new ArrayList<>(); for (;;) { Expression left = node.getLeft(); Expression right = node.getRight(); if (isString(left) || isString(right)) { ValType ltype = stringConcatWith(left, mv, gen); if (ltype.isPrimitive() || right instanceof Literal) { toStringForConcat(left, ltype, mv); } ValType rtype = stringConcatWith(right, mv, gen); assert ltype == ValType.String || rtype == ValType.String; if (!(ltype.isPrimitive() || right instanceof Literal)) { mv.swap(ltype, rtype); toStringForConcat(left, ltype, mv); mv.swap(rtype, ValType.String); } toStringForConcat(right, rtype, mv); break; } node = (BinaryExpression) left; list.add(right); } for (int i = list.size() - 1; i >= 0; --i) { Expression e = list.get(i); ValType rtype = stringConcatWith(e, mv, gen); toStringForConcat(e, rtype, mv); } return ValType.String; } private ValType stringConcatWith(Expression node, CodeVisitor mv, ExpressionGenerator gen) { if (node instanceof StringLiteral) { if (!((StringLiteral) node).getValue().isEmpty()) { node.accept(gen, mv); } return ValType.String; } if (node instanceof TemplateLiteral) { node.accept(gen, mv); return ValType.String; } if (node instanceof BinaryExpression && isStringConcat((BinaryExpression) node)) { return stringConcatWith((BinaryExpression) node, mv, gen); } return node.accept(gen, mv); } private int countStringConcats(BinaryExpression node) { int c = 0; for (BinaryExpression binary = (BinaryExpression) node;;) { Expression left = binary.getLeft(); Expression right = binary.getRight(); if (isString(left) || isString(right)) { return c + countStringConcats(left) + countStringConcats(right); } binary = (BinaryExpression) left; c += countStringConcats(right); } } private int countStringConcats(Expression node) { if (node instanceof BinaryExpression && isStringConcat((BinaryExpression) node)) { return countStringConcats((BinaryExpression) node); } // Empty string literals are omitted. if (node instanceof StringLiteral && ((StringLiteral) node).getValue().isEmpty()) { return 0; } return 1; } private ValType evalToString(Expression node, CodeVisitor mv, ExpressionGenerator gen) { ValType type = node.accept(gen, mv); return toStringForConcat(node, type, mv); } }; // 12.7.4 The Subtraction Operator ( - ) static final ArithmeticOp SUB = new ArithmeticOp() { @Override ValType operation(CodeVisitor mv) { mv.dsub(); return ValType.Number; } }; // 12.8.3 The Left Shift Operator ( << ) static final IntegerOp SHL = new IntegerOp() { @Override ValType operation(CodeVisitor mv) { mv.iconst(0x1F); mv.iand(); mv.ishl(); return ValType.Number_int; } }; // 12.8.4 The Signed Right Shift Operator ( >> ) static final IntegerOp SHR = new IntegerOp() { @Override ValType operation(CodeVisitor mv) { mv.iconst(0x1F); mv.iand(); mv.ishr(); return ValType.Number_int; } }; // 12.8.5 The Unsigned Right Shift Operator ( >>> ) static final ConvertOp USHR = new ConvertOp() { @Override ValType convertLeft(ValType type, CodeVisitor mv) { ToUint32(type, mv); return ValType.Number_uint; } @Override ValType convertRight(ValType type, CodeVisitor mv) { ToInt32(type, mv); // ToUint32() return ValType.Number_int; } @Override ValType operation(CodeVisitor mv) { mv.iconst(0x1F); mv.iand(); mv.lushr(); return ValType.Number_uint; } }; // 12.9 Relational Operators ( < ) static final RelationalOp LT = new RelationalOp() { @Override void operation(CodeVisitor mv) { mv.loadExecutionContext(); invokeDynamicOperator(BinaryExpression.Operator.LT, mv); } }; // 12.9 Relational Operators ( > ) static final RelationalOp GT = new RelationalOp() { @Override void operation(CodeVisitor mv) { mv.swap(); mv.loadExecutionContext(); invokeDynamicOperator(BinaryExpression.Operator.GT, mv); } }; // 12.9 Relational Operators ( <= ) static final RelationalOp LE = new RelationalOp() { @Override void operation(CodeVisitor mv) { mv.swap(); mv.loadExecutionContext(); invokeDynamicOperator(BinaryExpression.Operator.LE, mv); } }; // 12.9 Relational Operators ( >= ) static final RelationalOp GE = new RelationalOp() { @Override void operation(CodeVisitor mv) { mv.loadExecutionContext(); invokeDynamicOperator(BinaryExpression.Operator.GE, mv); } }; // 12.9 Relational Operators ( instanceof ) static final RelationalOp INSTANCEOF = new RelationalOp() { @Override void operation(CodeVisitor mv) { mv.loadExecutionContext(); mv.invoke(Methods.ScriptRuntime_InstanceofOperator); } }; // 12.9 Relational Operators ( in ) static final RelationalOp IN = new RelationalOp() { @Override void operation(CodeVisitor mv) { mv.loadExecutionContext(); mv.invoke(Methods.ScriptRuntime_in); } }; // 12.10 Equality Operators ( == ) static final EqualityOp EQ = new EqualityOp() { @Override protected void emitGeneric(BinaryExpression node, ValType ltype, Expression right, CodeVisitor mv, ExpressionGenerator gen) { mv.toBoxed(ltype); mv.toBoxed(right.accept(gen, mv)); mv.lineInfo(node); mv.loadExecutionContext(); invokeDynamicOperator(BinaryExpression.Operator.EQ, mv); } @Override protected void emitLiteral(BinaryExpression node, ValType type, NullLiteral literal, CodeVisitor mv, ExpressionGenerator gen) { if (type == ValType.Any) { mv.invoke(EqMethods.Type_isUndefinedOrNull); } else { mv.pop(type); mv.iconst(type == ValType.Undefined || type == ValType.Null); } } @Override protected void emitLiteral(BinaryExpression node, ValType type, BooleanLiteral literal, CodeVisitor mv, ExpressionGenerator gen) { assert type != ValType.Boolean; emitGeneric(node, type, literal, mv, gen); } @Override protected void emitLiteral(BinaryExpression node, ValType type, NumericLiteral literal, CodeVisitor mv, ExpressionGenerator gen) { assert !type.isNumeric(); emitGeneric(node, type, literal, mv, gen); } @Override protected void emitLiteral(BinaryExpression node, ValType type, StringLiteral literal, CodeVisitor mv, ExpressionGenerator gen) { assert type != ValType.String; emitGeneric(node, type, literal, mv, gen); } }; // 12.10 Equality Operators ( != ) static final BinaryOp NE = new BinaryOp() { @Override ValType emit(BinaryExpression node, CodeVisitor mv, ExpressionGenerator gen) { BinaryOp.EQ.emit(node, mv, gen); mv.not(); return ValType.Boolean; } }; // 12.10 Equality Operators ( === ) static final EqualityOp SHEQ = new EqualityOp() { @Override protected void emitGeneric(BinaryExpression node, ValType ltype, Expression right, CodeVisitor mv, ExpressionGenerator gen) { mv.toBoxed(ltype); mv.toBoxed(right.accept(gen, mv)); mv.lineInfo(node); invokeDynamicOperator(BinaryExpression.Operator.SHEQ, mv); } @Override protected void emitLiteral(BinaryExpression node, ValType type, NullLiteral literal, CodeVisitor mv, ExpressionGenerator gen) { if (type == ValType.Any) { mv.invoke(EqMethods.Type_isNull); } else { mv.pop(type); mv.iconst(type == ValType.Null); } } @Override protected void emitLiteral(BinaryExpression node, ValType type, BooleanLiteral literal, CodeVisitor mv, ExpressionGenerator gen) { assert type != ValType.Boolean; if (type == ValType.Any) { emitGeneric(node, type, literal, mv, gen); } else { mv.pop(type); mv.iconst(false); } } @Override protected void emitLiteral(BinaryExpression node, ValType type, NumericLiteral literal, CodeVisitor mv, ExpressionGenerator gen) { assert !type.isNumeric(); if (type == ValType.Any) { emitGeneric(node, type, literal, mv, gen); } else { mv.pop(type); mv.iconst(false); } } @Override protected void emitLiteral(BinaryExpression node, ValType type, StringLiteral literal, CodeVisitor mv, ExpressionGenerator gen) { assert type != ValType.String; if (type == ValType.Any) { emitGeneric(node, type, literal, mv, gen); } else { mv.pop(type); mv.iconst(false); } } }; // 12.10 Equality Operators ( !== ) static final BinaryOp SHNE = new BinaryOp() { @Override ValType emit(BinaryExpression node, CodeVisitor mv, ExpressionGenerator gen) { BinaryOp.SHEQ.emit(node, mv, gen); mv.not(); return ValType.Boolean; } }; // 12.11 Binary Bitwise Operators ( & ) static final IntegerOp BITAND = new IntegerOp() { @Override ValType operation(CodeVisitor mv) { mv.iand(); return ValType.Number_int; } }; // 12.11 Binary Bitwise Operators ( ^ ) static final IntegerOp BITXOR = new IntegerOp() { @Override ValType operation(CodeVisitor mv) { mv.ixor(); return ValType.Number_int; } }; // 12.11 Binary Bitwise Operators ( | ) static final IntegerOp BITOR = new IntegerOp() { @Override ValType operation(CodeVisitor mv) { mv.ior(); return ValType.Number_int; } }; // 12.12 Binary Logical Operators static final LogicalOp AND = new LogicalOp() { @Override void operation(Jump jump, CodeVisitor mv) { mv.ifeq(jump); } }; // 12.12 Binary Logical Operators static final LogicalOp OR = new LogicalOp() { @Override void operation(Jump jump, CodeVisitor mv) { mv.ifne(jump); } }; } private static ValType toStringForConcat(Expression node, ValType type, CodeVisitor mv) { // ToString(ToPrimitive(type, mv), mv); if (type.isPrimitive()) { ToString(type, mv); } else { mv.loadExecutionContext(); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_toStr); } return ValType.String; } private static ValType addStrings(ValType left, ValType right, CodeVisitor mv) { assert left == ValType.String && right == ValType.String; mv.loadExecutionContext(); mv.invoke(Methods.ScriptRuntime_add_str); return ValType.String; } private static ValType expressionType(Expression node) { return node.accept(ExpressionTypeVisitor.INSTANCE, null); } private static final class ExpressionTypeVisitor extends DefaultNodeVisitor<ValType, Void> { static final ExpressionTypeVisitor INSTANCE = new ExpressionTypeVisitor(); @Override protected ValType visit(Node node, Void value) { return ValType.Any; } @Override public ValType visit(ArrayComprehension node, Void value) { return ValType.Object; } @Override public ValType visit(ArrayLiteral node, Void value) { return ValType.Object; } @Override public ValType visit(ArrowFunction node, Void value) { return ValType.Object; } @Override public ValType visit(AssignmentExpression node, Void value) { switch (node.getOperator()) { case ASSIGN_BITAND: case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_SHL: case ASSIGN_SHR: return ValType.Number_int; case ASSIGN_USHR: return ValType.Number_uint; case ASSIGN_DIV: case ASSIGN_EXP: case ASSIGN_MOD: case ASSIGN_MUL: case ASSIGN_SUB: return ValType.Number; case ASSIGN_ADD: { ValType rtype = expressionType(node.getRight()); if (rtype == ValType.String) { return ValType.String; } // Pessimistically assume any-type return ValType.Any; } case ASSIGN: return expressionType(node.getRight()); default: throw new AssertionError(); } } @Override public ValType visit(AsyncArrowFunction node, Void value) { return ValType.Object; } @Override public ValType visit(AsyncFunctionExpression node, Void value) { return ValType.Object; } @Override public ValType visit(BinaryExpression node, Void value) { switch (node.getOperator()) { case BITAND: case BITOR: case BITXOR: case SHL: case SHR: return ValType.Number_int; case USHR: return ValType.Number_uint; case DIV: case EXP: case MOD: case MUL: case SUB: return ValType.Number; case EQ: case GE: case GT: case IN: case INSTANCEOF: case LE: case LT: case NE: case SHEQ: case SHNE: return ValType.Boolean; case ADD: { ValType ltype = expressionType(node.getLeft()); ValType rtype = expressionType(node.getRight()); if (ltype == ValType.String || rtype == ValType.String) { return ValType.String; } else if (ltype.isNumeric() && rtype.isPrimitive()) { return ValType.Number; } // Pessimistically assume any-type return ValType.Any; } case AND: case OR: return expressionType(node.getLeft()) == ValType.Boolean && expressionType(node.getRight()) == ValType.Boolean ? ValType.Boolean : ValType.Any; default: throw new AssertionError(); } } @Override public ValType visit(BooleanLiteral node, Void value) { return ValType.Boolean; } @Override public ValType visit(ClassExpression node, Void value) { return ValType.Object; } @Override public ValType visit(CommaExpression node, Void value) { List<Expression> operands = node.getOperands(); return expressionType(operands.get(operands.size() - 1)); } @Override public ValType visit(ConditionalExpression node, Void value) { ValType ltype = expressionType(node.getThen()); ValType rtype = expressionType(node.getOtherwise()); if (ltype != rtype && ltype.isNumeric() && rtype.isNumeric()) { return ValType.Number; } return ltype == rtype ? ltype : ValType.Any; } @Override public ValType visit(FunctionExpression node, Void value) { return ValType.Object; } @Override public ValType visit(GeneratorComprehension node, Void value) { return ValType.Object; } @Override public ValType visit(GeneratorExpression node, Void value) { return ValType.Object; } @Override public ValType visit(LetExpression node, Void value) { return expressionType(node.getExpression()); } @Override public ValType visit(NewExpression node, Void value) { return ValType.Object; } @Override public ValType visit(NullLiteral node, Void value) { return ValType.Null; } @Override public ValType visit(NumericLiteral node, Void value) { return node.isInt() ? ValType.Number_int : ValType.Number; } @Override public ValType visit(ObjectLiteral node, Void value) { return ValType.Object; } @Override public ValType visit(RegularExpressionLiteral node, Void value) { return ValType.Object; } @Override public ValType visit(StringLiteral node, Void value) { return ValType.String; } @Override public ValType visit(SuperCallExpression node, Void value) { return ValType.Object; } @Override public ValType visit(SuperNewExpression node, Void value) { return ValType.Object; } @Override public ValType visit(TemplateLiteral node, Void value) { return ValType.String; } @Override public ValType visit(UnaryExpression node, Void value) { switch (node.getOperator()) { case BITNOT: return ValType.Number_int; case NEG: case POS: case POST_DEC: case POST_INC: case PRE_DEC: case PRE_INC: return ValType.Number; case DELETE: case NOT: return ValType.Boolean; case VOID: return ValType.Undefined; case TYPEOF: return ValType.String; default: throw new AssertionError(); } } } /** * 12.2.4 Literals * <p> * 12.2.4.1 Runtime Semantics: Evaluation */ @Override public ValType visit(BooleanLiteral node, CodeVisitor mv) { /* steps 1-2 */ mv.iconst(node.getValue()); return ValType.Boolean; } /** * 12.3.4 Function Calls * <p> * 12.3.4.1 Runtime Semantics: Evaluation */ @Override public ValType visit(CallExpression node, CodeVisitor mv) { return EvaluateCall(node, node.getBase(), node.getArguments(), mv); } /** * 12.3.6.1 Runtime Semantics: ArgumentListEvaluation */ @Override public ValType visit(CallSpreadElement node, CodeVisitor mv) { ValType type = node.getExpression().accept(this, mv); mv.toBoxed(type); mv.loadExecutionContext(); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_SpreadArray); return ValType.Any; // actually Object[] } /** * 14.5.16 Runtime Semantics: Evaluation */ @Override public ValType visit(ClassExpression node, CodeVisitor mv) { /* steps 1-2 */ Name className = node.getIdentifier() != null ? node.getIdentifier().getName() : null; /* steps 3-4 */ ClassDefinitionEvaluation(node, className, mv); /* step 5 */ if (className != null) { SetFunctionName(node, className, mv); } /* step 6 */ return ValType.Object; } /** * 12.15.3 Runtime Semantics: Evaluation */ @Override public ValType visit(CommaExpression node, CodeVisitor mv) { assert !node.getOperands().isEmpty() : "empty comma expression"; int count = node.getOperands().size(); for (Expression e : node.getOperands()) { if (--count == 0) { return e.accept(this, mv); } ValType type = e.emptyCompletion().accept(this, mv); mv.pop(type); } return null; } /** * 12.13 Conditional Operator ( ? : ) * <p> * 12.13.3 Runtime Semantics: Evaluation */ @Override public ValType visit(ConditionalExpression node, CodeVisitor mv) { Jump l0 = new Jump(), l1 = new Jump(); /* steps 1-2 */ ValType typeTest = node.getTest().accept(this, mv); /* step 2 */ ToBoolean(typeTest, mv); /* step 3 */ mv.ifeq(l0); ValType typeThen = node.getThen().accept(this, mv); if (typeThen.isJavaPrimitive()) { // Try to avoid boxing if then-and-otherwise are both compatible primitive types. ValType expected = expressionType(node.getOtherwise()); boolean sameType = typeThen == expected; if (sameType || (typeThen.isNumeric() && expected.isNumeric())) { if (!sameType) { ToNumber(typeThen, mv); } mv.goTo(l1); mv.mark(l0); ValType typeOtherwise = node.getOtherwise().accept(this, mv); assert expected == typeOtherwise : String.format("expected=%s, got=%s", expected, typeOtherwise); if (!sameType) { ToNumber(typeOtherwise, mv); } mv.mark(l1); return sameType ? typeThen : ValType.Number; } } mv.toBoxed(typeThen); mv.goTo(l1); /* step 4 */ mv.mark(l0); ValType typeOtherwise = node.getOtherwise().accept(this, mv); mv.toBoxed(typeOtherwise); mv.mark(l1); return typeThen == typeOtherwise ? typeThen : ValType.Any; } /** * Extension: 'do' expression */ @Override public ValType visit(DoExpression node, CodeVisitor mv) { Entry<MethodName, LabelState> entry = codegen.compile(node, mv); MethodName method = entry.getKey(); LabelState labelState = entry.getValue(); boolean hasCompletion = labelState.hasReturn() || node.hasCompletion(); boolean hasResume = node.hasYieldOrAwait(); boolean hasTarget = hasResume || labelState.size() > 0; mv.enterVariableScope(); Value<Object[]> completion; if (hasCompletion) { Variable<Object[]> completionVar = mv.newVariable("completion", Object[].class); mv.anewarray(1, Types.Object); mv.store(completionVar); if (node.hasCompletion()) { mv.astore(completionVar, 0, mv.undefinedValue()); } completion = completionVar; } else { completion = mv.anullValue(); } MutableValue<Integer> target = hasTarget ? mv.newVariable("target", int.class) : new PopStoreValue<>(); // stack: [] -> [] mv.lineInfo(0); // 0 = hint for stacktraces to omit this frame if (hasResume) { mv.callWithSuspendInt(method, target, completion); } else { mv.callWithResult(method, target, completion); } Value<Object> completionValue = mv.arrayElement(completion, 0, Object.class); mv.labelSwitch(labelState, target, completionValue, true); if (node.hasCompletion()) { mv.load(completionValue); } mv.exitVariableScope(); return node.hasCompletion() ? ValType.Any : ValType.Empty; } private static final class PopStoreValue<V> implements MutableValue<V> { @Override public void load(InstructionAssembler assembler) { throw new AssertionError(); } @Override public void store(InstructionAssembler assembler) { assembler.pop(); } } /** * 12.3.2 Property Accessors * <p> * 12.3.2.1 Runtime Semantics: Evaluation */ @Override public ValType visit(ElementAccessor node, CodeVisitor mv) { ReferenceOp<ElementAccessor> op = ReferenceOp.propertyOp(node); return op.referenceValue(node, mv, codegen); } @Override public ValType visit(EmptyExpression node, CodeVisitor mv) { return ValType.Empty; } @Override public ValType visit(ExpressionMethod node, CodeVisitor mv) { MethodName method = codegen.compile(node, mv); boolean hasResume = node.hasResumePoint(); // stack: [] -> [result] mv.lineInfo(0); // 0 = hint for stacktraces to omit this frame if (hasResume) { mv.callWithSuspend(method); } else { mv.call(method); } return ValType.Any; } /** * 14.1.17 Runtime Semantics: Evaluation */ @Override public ValType visit(FunctionExpression node, CodeVisitor mv) { MethodName method = codegen.compile(node); /* steps 1-5/10 */ mv.invoke(method); mv.loadExecutionContext(); if (isLegacy(node)) { mv.invoke(Methods.ScriptRuntime_EvaluateLegacyFunctionExpression); } else { mv.invoke(Methods.ScriptRuntime_EvaluateFunctionExpression); } /* step 6/11 */ return ValType.Object; } private boolean isLegacy(FunctionExpression node) { if (IsStrict(node)) { return false; } return codegen.isEnabled(CompatibilityOption.FunctionArguments) || codegen.isEnabled(CompatibilityOption.FunctionCaller); } /** * Extension: 'function.sent' meta property */ @Override public ValType visit(FunctionSent node, CodeVisitor mv) { mv.loadExecutionContext(); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_functionSent); return ValType.Any; } /** * 12.2.7.2 Runtime Semantics: Evaluation */ @Override public ValType visit(GeneratorComprehension node, CodeVisitor mv) { MethodName method = codegen.compile(node); /* steps 1-8 */ mv.invoke(method); mv.loadExecutionContext(); if (node.getComprehension() instanceof LegacyComprehension) { mv.invoke(Methods.ScriptRuntime_EvaluateLegacyGeneratorComprehension); } else if (node.isConstructor()) { mv.invoke(Methods.ScriptRuntime_EvaluateConstructorGeneratorComprehension); } else { mv.invoke(Methods.ScriptRuntime_EvaluateGeneratorComprehension); } /* step 9 */ return ValType.Object; } /** * 14.4.14 Runtime Semantics: Evaluation */ @Override public ValType visit(GeneratorExpression node, CodeVisitor mv) { MethodName method = codegen.compile(node); /* steps 1-7/11 */ mv.invoke(method); mv.loadExecutionContext(); if (node.isConstructor()) { mv.invoke(Methods.ScriptRuntime_EvaluateConstructorGeneratorExpression); } else { mv.invoke(Methods.ScriptRuntime_EvaluateGeneratorExpression); } /* step 8/12 */ return ValType.Object; } /** * 14.4.14 Runtime Semantics: Evaluation */ @Override public ValType visit(LegacyGeneratorExpression node, CodeVisitor mv) { MethodName method = codegen.compile(node); /* steps 1-7 */ mv.invoke(method); mv.loadExecutionContext(); mv.invoke(Methods.ScriptRuntime_EvaluateLegacyGeneratorExpression); /* step 8 */ return ValType.Object; } /** * 12.1 Identifiers * <p> * 12.1.6 Runtime Semantics: Evaluation */ @Override public ValType visit(IdentifierReference node, CodeVisitor mv) { /* steps 1-2 */ ReferenceOp<IdentifierReference> op = ReferenceOp.of(node); return op.referenceValue(node, mv, codegen); } /** * Extension: 'let' expression */ @Override public ValType visit(LetExpression 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 (LexicalBinding lexical : node.getBindings()) { Binding binding = lexical.getBinding(); Expression initializer = lexical.getInitializer(); for (Name name : BoundNames(binding)) { BindingOp<DeclarativeEnvironmentRecord> op = BindingOp.of(envRec, name); op.createMutableBinding(envRec, name, false, mv); } if (initializer == null) { // LexicalBinding : BindingIdentifier assert binding instanceof BindingIdentifier; Name name = ((BindingIdentifier) binding).getName(); /* steps 1-2 */ // stack: [] -> [] InitializeBoundNameWithUndefined(envRec, name, mv); } else if (binding instanceof BindingIdentifier) { // LexicalBinding : BindingIdentifier Initializer Name name = ((BindingIdentifier) binding).getName(); /* steps 1-7 */ InitializeBoundNameWithInitializer(codegen, envRec, name, initializer, mv); } else { // LexicalBinding : BindingPattern Initializer assert binding instanceof BindingPattern; /* steps 1-3 */ expressionBoxed(initializer, mv); /* steps 4-5 */ BindingInitializationGenerator.BindingInitialization(codegen, envRec, (BindingPattern) binding, mv); } } mv.load(env); pushLexicalEnvironment(mv); mv.exitVariableScope(); } mv.enterScope(node); ValType type = node.getExpression().accept(this, mv); mv.exitScope(); if (scope.isPresent()) { popLexicalEnvironment(mv); } return type; } @Override public ValType visit(NativeCallExpression node, CodeVisitor mv) { String nativeName = node.getBase().getName(); List<Expression> arguments = node.getArguments(); Type[] parameters = new Type[1 + arguments.size()]; mv.loadExecutionContext(); parameters[0] = Types.ExecutionContext; for (int i = 0; i < arguments.size(); ++i) { parameters[i + 1] = nativeCallArgument(arguments.get(i), mv); } mv.lineInfo(node); return invokeDynamicNativeCall(nativeName, parameters, mv); } private Type nativeCallArgument(Expression argument, CodeVisitor mv) { if (argument instanceof CallSpreadElement) { CallSpreadElement spread = (CallSpreadElement) argument; ValType type = spread.getExpression().accept(this, mv); mv.toBoxed(type); mv.loadExecutionContext(); mv.lineInfo(spread); mv.invoke(Methods.ScriptRuntime_NativeCallSpreadArray); return Types.Object_; } return argument.accept(this, mv).toType(); } /** * 12.3.3 The new Operator * <p> * 12.3.3.1 Runtime Semantics: Evaluation */ @Override public ValType visit(NewExpression node, CodeVisitor mv) { /* step 1 */ return EvaluateNew(node, mv); } /** * 12.3.8 Meta Properties * <p> * 12.3.8.1 Runtime Semantics: Evaluation */ @Override public ValType visit(NewTarget node, CodeVisitor mv) { mv.loadExecutionContext(); mv.invoke(Methods.ScriptRuntime_GetNewTargetOrUndefined); return ValType.Any; } /** * 12.2.4 Literals * <p> * 12.2.4.1 Runtime Semantics: Evaluation */ @Override public ValType visit(NullLiteral node, CodeVisitor mv) { /* step 1 */ mv.loadNull(); return ValType.Null; } /** * 12.2.4 Literals * <p> * 12.2.4.1 Runtime Semantics: Evaluation */ @Override public ValType visit(NumericLiteral node, CodeVisitor mv) { if (node.isInt()) { /* step 1 */ mv.iconst(node.intValue()); return ValType.Number_int; } else { /* step 1 */ mv.dconst(node.doubleValue()); return ValType.Number; } } /** * 12.2.6 Object Initializer * <p> * 12.2.6.8 Runtime Semantics: Evaluation */ @Override public ValType visit(ObjectLiteral node, CodeVisitor mv) { Variable<ArrayList<Object>> decorators = null; boolean hasDecorators = HasDecorators(node); if (hasDecorators) { mv.enterVariableScope(); decorators = newDecoratorVariable("decorators", mv); } PropertyGenerator propgen = codegen.propertyGenerator(decorators); /* step 1 */ mv.loadExecutionContext(); mv.get(Fields.Intrinsics_ObjectPrototype); mv.invoke(Methods.OrdinaryObject_ObjectCreate); /* steps 2-3 */ for (PropertyDefinition property : node.getProperties()) { mv.dup(); property.accept(propgen, mv); } if (hasDecorators) { mv.dup(); mv.load(decorators); mv.loadExecutionContext(); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_EvaluateMethodDecorators); mv.exitVariableScope(); } /* step 4 */ return ValType.Object; } /** * 12.3.2 Property Accessors * <p> * 12.3.2.1 Runtime Semantics: Evaluation */ @Override public ValType visit(PropertyAccessor node, CodeVisitor mv) { ReferenceOp<PropertyAccessor> op = ReferenceOp.propertyOp(node); return op.referenceValue(node, mv, codegen); } /** * 12.2.8 Regular Expression Literals * <p> * 12.2.8.2 Runtime Semantics: Evaluation */ @Override public ValType visit(RegularExpressionLiteral node, CodeVisitor mv) { mv.loadExecutionContext(); /* step 1 */ mv.aconst(node.getRegexp()); /* step 2 */ mv.aconst(node.getFlags()); /* step 3 */ mv.lineInfo(node); mv.invoke(Methods.RegExpConstructor_RegExpCreate); return ValType.Object; } /** * 12.2.4 Literals * <p> * 12.2.4.1 Runtime Semantics: Evaluation */ @Override public ValType visit(StringLiteral node, CodeVisitor mv) { /* step 1 */ mv.aconst(node.getValue()); return ValType.String; } /** * 12.3.5.1 Runtime Semantics: Evaluation */ @Override public ValType visit(SuperCallExpression node, CodeVisitor mv) { /* steps 1-2 */ // stack: [] -> [newTarget] mv.loadExecutionContext(); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_GetNewTarget); /* steps 3-4 */ // stack: [newTarget] -> [constructor, newTarget] mv.loadExecutionContext(); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_GetSuperConstructor); mv.swap(); // stack: [constructor, newTarget] -> [constructor, cx, newTarget] mv.loadExecutionContext(); mv.swap(); /* steps 5-6 */ // stack: [constructor, cx, newTarget] -> [constructor, cx, newTarget, argList] ArgumentListEvaluation(node, node.getArguments(), mv); /* steps 7-8 */ // stack: [constructor, cx, newTarget, argList] -> [result] mv.lineInfo(node); invokeDynamicSuper(mv); /* steps 9-10 */ // stack: [result] -> [result] mv.dup(); mv.loadExecutionContext(); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_BindThisValue); return ValType.Object; } /** * 12.3.5.1 Runtime Semantics: Evaluation */ @Override public ValType visit(SuperElementAccessor node, CodeVisitor mv) { ReferenceOp<SuperElementAccessor> op = ReferenceOp.propertyOp(node); return op.referenceValue(node, mv, codegen); } /** * 12.3.5.1 Runtime Semantics: Evaluation */ @Override public ValType visit(SuperNewExpression node, CodeVisitor mv) { /* steps 1-2 */ // stack: [] -> [newTarget] mv.loadExecutionContext(); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_GetNewTarget); /* steps 3-4 */ // stack: [newTarget] -> [constructor, newTarget] mv.loadExecutionContext(); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_GetSuperConstructor); mv.swap(); // stack: [constructor, newTarget] -> [constructor, cx, newTarget] mv.loadExecutionContext(); mv.swap(); /* steps 5-6 */ // stack: [constructor, cx, newTarget] -> [constructor, cx, newTarget, argList] ArgumentListEvaluation(node, node.getArguments(), mv); /* steps 7-13 */ // stack: [constructor, cx, newTarget, argList] -> [result] mv.lineInfo(node); invokeDynamicSuper(mv); return ValType.Object; } /** * 12.3.5.1 Runtime Semantics: Evaluation */ @Override public ValType visit(SuperPropertyAccessor node, CodeVisitor mv) { ReferenceOp<SuperPropertyAccessor> op = ReferenceOp.propertyOp(node); return op.referenceValue(node, mv, codegen); } /** * 12.3.7 Tagged Templates * <p> * 12.3.7.1 Runtime Semantics: Evaluation */ @Override public ValType visit(TemplateCallExpression node, CodeVisitor mv) { codegen.compile(node.getTemplate()); // 12.2.9.2.1 Runtime Semantics: ArgumentListEvaluation // 12.2.8.2.2 Runtime Semantics: GetTemplateObject // 12.2.9.2.3 Runtime Semantics: SubstitutionEvaluation TemplateLiteral template = node.getTemplate(); List<Expression> substitutions = Substitutions(template); ArrayList<Expression> arguments = new ArrayList<>(substitutions.size() + 1); arguments.add(template); arguments.addAll(substitutions); /* steps 1-4 */ return EvaluateCall(node, node.getBase(), arguments, mv); } /** * 12.2.9 Template Literals * <p> * 12.2.9.5 Runtime Semantics: Evaluation */ @Override public ValType visit(TemplateLiteral node, CodeVisitor mv) { if (node.isTagged()) { codegen.GetTemplateObject(node, mv); return ValType.Object; } List<Expression> elements = node.getElements(); if (elements.size() == 1) { assert elements.get(0) instanceof TemplateCharacters; TemplateCharacters chars = (TemplateCharacters) elements.get(0); mv.aconst(chars.getValue()); } else { // TODO: change to expression::concat? mv.anew(Types.StringBuilder, Methods.StringBuilder_init); for (Expression expr : elements) { if (expr instanceof TemplateCharacters) { String value = ((TemplateCharacters) expr).getValue(); if (!value.isEmpty()) { mv.aconst(value); mv.invoke(Methods.StringBuilder_append_String); } } else { ValType type = expr.accept(this, mv); ToString(type, mv); mv.invoke(Methods.StringBuilder_append_Charsequence); } } mv.invoke(Methods.StringBuilder_toString); } return ValType.String; } /** * 12.2.2 The this Keyword * <p> * 12.2.2.1 Runtime Semantics: Evaluation */ @Override public ValType visit(ThisExpression node, CodeVisitor mv) { /* step 1 */ mv.loadExecutionContext(); mv.lineInfo(node); mv.invoke(Methods.ExecutionContext_resolveThisBinding); return ValType.Any; } /** * 12.4.4.1 Runtime Semantics: Evaluation<br> * 12.4.5.1 Runtime Semantics: Evaluation<br> * 12.5.4.2 Runtime Semantics: Evaluation<br> * 12.5.5.1 Runtime Semantics: Evaluation<br> * 12.5.6.1 Runtime Semantics: Evaluation<br> * 12.5.7.1 Runtime Semantics: Evaluation<br> * 12.5.8.1 Runtime Semantics: Evaluation<br> * 12.5.9.1 Runtime Semantics: Evaluation<br> * 12.5.10.1 Runtime Semantics: Evaluation<br> * 12.5.11.1 Runtime Semantics: Evaluation<br> * 12.5.12.1 Runtime Semantics: Evaluation<br> */ @Override public ValType visit(UnaryExpression node, CodeVisitor mv) { switch (node.getOperator()) { case POST_INC: case POST_DEC: case PRE_INC: case PRE_DEC: return unaryUpdateOp(node).emit(node, mv, this); case DELETE: { // 12.5.4 The delete Operator Expression operand = node.getOperand(); if (operand instanceof LeftHandSideExpression) { LeftHandSideExpression lhs = (LeftHandSideExpression) operand; return ReferenceOp.of(lhs).delete(lhs, mv, codegen); } ValType type = operand.emptyCompletion().accept(this, mv); assert type != ValType.Reference; mv.pop(type); mv.iconst(true); return ValType.Boolean; } case VOID: { // 12.5.5 The void Operator ValType type = node.getOperand().emptyCompletion().accept(this, mv); mv.pop(type); if (node.hasCompletion()) { mv.loadUndefined(); return ValType.Undefined; } return ValType.Empty; } case TYPEOF: { // 12.5.6 The typeof Operator Expression operand = node.getOperand(); if (operand instanceof IdentifierReference) { IdentifierReference ident = (IdentifierReference) operand; Name resolvedName = ident.getResolvedName(); if (resolvedName == null || !resolvedName.isLocal()) { // TODO: Add referenceValueOrUndefined() method ReferenceOp.LOOKUP.reference(ident, mv, codegen); mv.loadExecutionContext(); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_typeof_Reference); return ValType.String; } } ValType type = operand.accept(this, mv); mv.toBoxed(type); mv.invoke(Methods.ScriptRuntime_typeof); return ValType.String; } case POS: { // 12.5.9 Unary + Operator ValType type = node.getOperand().accept(this, mv); ToNumber(type, mv); return ValType.Number; } case NEG: { // 12.5.10 Unary - Operator ValType type = node.getOperand().accept(this, mv); ToNumber(type, mv); mv.dneg(); return ValType.Number; } case BITNOT: { // 12.5.11 Bitwise NOT Operator ( ~ ) ValType type = node.getOperand().accept(this, mv); ToInt32(type, mv); mv.bitnot(); return ValType.Number_int; } case NOT: { // 12.5.12 Logical NOT Operator ( ! ) if (!node.hasCompletion()) { ValType type = node.getOperand().emptyCompletion().accept(this, mv); mv.pop(type); return ValType.Empty; } ValType type = node.getOperand().accept(this, mv); ToBoolean(type, mv); mv.not(); return ValType.Boolean; } default: throw new AssertionError(Objects.toString(node.getOperator(), "<null>")); } } private static UnaryUpdateOp unaryUpdateOp(UnaryExpression node) { switch (node.getOperator()) { case PRE_INC: case POST_INC: return UnaryUpdateOp.INCREMENT; case PRE_DEC: case POST_DEC: return UnaryUpdateOp.DECREMENT; default: throw new AssertionError(Objects.toString(node.getOperator(), "<null>")); } } private static abstract class UnaryUpdateOp { abstract void operation(CodeVisitor mv); final ValType emit(UnaryExpression node, CodeVisitor mv, ExpressionGenerator gen) { LeftHandSideExpression expr = (LeftHandSideExpression) node.getOperand(); ReferenceOp<LeftHandSideExpression> op = ReferenceOp.of(expr); ValType type = op.referenceForUpdate(expr, mv, gen.codegen); ValType vtype = op.getValue(expr, type, mv); ToNumber(vtype, mv); if (!node.getOperator().isPostfix()) { operation(mv); return op.putValue(expr, type, ValType.Number, node.hasCompletion(), mv); } if (node.hasCompletion()) { Variable<?> saved = op.saveValue(type, ValType.Number, mv); operation(mv); op.putValue(expr, type, ValType.Number, mv); op.restoreValue(saved, mv); return ValType.Number; } operation(mv); op.putValue(expr, type, ValType.Number, mv); return ValType.Empty; } // 12.4.4 Postfix Increment Operator // 12.5.7 Prefix Increment Operator static final UnaryUpdateOp INCREMENT = new UnaryUpdateOp() { @Override void operation(CodeVisitor mv) { mv.dconst(1d); mv.dadd(); } }; // 12.4.5 Postfix Decrement Operator // 12.5.8 Prefix Decrement Operator static final UnaryUpdateOp DECREMENT = new UnaryUpdateOp() { @Override void operation(CodeVisitor mv) { mv.dconst(1d); mv.dsub(); } }; } /** * 14.4 Generator Function Definitions * <p> * 14.4.14 Runtime Semantics: Evaluation */ @Override public ValType visit(YieldExpression node, CodeVisitor mv) { Expression expr = node.getExpression(); if (expr != null) { ValType type = expr.accept(this, mv); mv.toBoxed(type); } else { mv.loadUndefined(); } if (node.isDelegatedYield()) { delegatedYield(node, mv); } else { yield(node, mv); } return ValType.Any; } }