/** * 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.ClassPropertyGenerator.ClassPropertyEvaluation; import static com.github.anba.es6draft.semantics.StaticSemantics.ConstructorMethod; import static com.github.anba.es6draft.semantics.StaticSemantics.HasDecorators; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; import com.github.anba.es6draft.ast.*; import com.github.anba.es6draft.ast.AbruptNode.Abrupt; import com.github.anba.es6draft.ast.scope.BlockScope; import com.github.anba.es6draft.ast.scope.ModuleScope; import com.github.anba.es6draft.ast.scope.Name; import com.github.anba.es6draft.ast.scope.Scope; import com.github.anba.es6draft.ast.scope.ScriptScope; import com.github.anba.es6draft.ast.scope.WithScope; import com.github.anba.es6draft.compiler.assembler.FieldName; import com.github.anba.es6draft.compiler.assembler.Jump; import com.github.anba.es6draft.compiler.assembler.MethodName; import com.github.anba.es6draft.compiler.assembler.TryCatchLabel; import com.github.anba.es6draft.compiler.assembler.Type; import com.github.anba.es6draft.compiler.assembler.Value; import com.github.anba.es6draft.compiler.assembler.Variable; import com.github.anba.es6draft.runtime.DeclarativeEnvironmentRecord; import com.github.anba.es6draft.runtime.EnvironmentRecord; import com.github.anba.es6draft.runtime.GlobalEnvironmentRecord; import com.github.anba.es6draft.runtime.LexicalEnvironment; import com.github.anba.es6draft.runtime.ModuleEnvironmentRecord; import com.github.anba.es6draft.runtime.ObjectEnvironmentRecord; import com.github.anba.es6draft.runtime.internal.Bootstrap; import com.github.anba.es6draft.runtime.internal.ScriptException; import com.github.anba.es6draft.runtime.types.Callable; import com.github.anba.es6draft.runtime.types.Null; import com.github.anba.es6draft.runtime.types.Reference; import com.github.anba.es6draft.runtime.types.ScriptObject; import com.github.anba.es6draft.runtime.types.Undefined; import com.github.anba.es6draft.runtime.types.builtins.OrdinaryConstructorFunction; import com.github.anba.es6draft.runtime.types.builtins.OrdinaryObject; /** * Abstract base class for specialised generators */ abstract class DefaultCodeGenerator<RETURN> extends DefaultNodeVisitor<RETURN, CodeVisitor> { private static final class Fields { static final FieldName Double_NaN = FieldName.findStatic(Types.Double, "NaN", Type.DOUBLE_TYPE); static final FieldName ScriptRuntime_EMPTY_ARRAY = FieldName.findStatic(Types.ScriptRuntime, "EMPTY_ARRAY", Types.Object_); } private static final class Methods { // class: AbstractOperations static final MethodName AbstractOperations_CreateIterResultObject = MethodName.findStatic( Types.AbstractOperations, "CreateIterResultObject", Type.methodType( Types.OrdinaryObject, Types.ExecutionContext, Types.Object, Type.BOOLEAN_TYPE)); static final MethodName AbstractOperations_HasOwnProperty = MethodName.findStatic( Types.AbstractOperations, "HasOwnProperty", Type.methodType(Type.BOOLEAN_TYPE, Types.ExecutionContext, Types.ScriptObject, Types.String)); static final MethodName AbstractOperations_GetIterator = MethodName.findStatic( Types.AbstractOperations, "GetIterator", Type.methodType(Types.ScriptObject, Types.ExecutionContext, Types.Object)); static final MethodName AbstractOperations_GetMethod = MethodName.findStatic(Types.AbstractOperations, "GetMethod", Type.methodType(Types.Callable, Types.ExecutionContext, Types.ScriptObject, Types.String)); static final MethodName AbstractOperations_IteratorComplete = MethodName.findStatic( Types.AbstractOperations, "IteratorComplete", Type.methodType(Type.BOOLEAN_TYPE, Types.ExecutionContext, Types.ScriptObject)); static final MethodName AbstractOperations_IteratorNext = MethodName.findStatic( Types.AbstractOperations, "IteratorNext", Type.methodType(Types.ScriptObject, Types.ExecutionContext, Types.ScriptObject)); static final MethodName AbstractOperations_IteratorNext_Object = MethodName.findStatic( Types.AbstractOperations, "IteratorNext", Type.methodType(Types.ScriptObject, Types.ExecutionContext, Types.ScriptObject, Types.Object)); static final MethodName AbstractOperations_IteratorValue = MethodName.findStatic( Types.AbstractOperations, "IteratorValue", Type.methodType(Types.Object, Types.ExecutionContext, Types.ScriptObject)); static final MethodName AbstractOperations_ToPrimitive = MethodName.findStatic( Types.AbstractOperations, "ToPrimitive", Type.methodType(Types.Object, Types.ExecutionContext, Types.Object)); static final MethodName AbstractOperations_ToBoolean = MethodName.findStatic( Types.AbstractOperations, "ToBoolean", Type.methodType(Type.BOOLEAN_TYPE, Types.Object)); static final MethodName AbstractOperations_ToBoolean_double = MethodName.findStatic( Types.AbstractOperations, "ToBoolean", Type.methodType(Type.BOOLEAN_TYPE, Type.DOUBLE_TYPE)); static final MethodName AbstractOperations_ToFlatString = MethodName.findStatic( Types.AbstractOperations, "ToFlatString", Type.methodType(Types.String, Types.ExecutionContext, Types.Object)); static final MethodName AbstractOperations_ToNumber = MethodName.findStatic( Types.AbstractOperations, "ToNumber", Type.methodType(Type.DOUBLE_TYPE, Types.ExecutionContext, Types.Object)); static final MethodName AbstractOperations_ToNumber_CharSequence = MethodName.findStatic( Types.AbstractOperations, "ToNumber", Type.methodType(Type.DOUBLE_TYPE, Types.CharSequence)); static final MethodName AbstractOperations_ToInt32 = MethodName.findStatic( Types.AbstractOperations, "ToInt32", Type.methodType(Type.INT_TYPE, Types.ExecutionContext, Types.Object)); static final MethodName AbstractOperations_ToInt32_double = MethodName.findStatic( Types.AbstractOperations, "ToInt32", Type.methodType(Type.INT_TYPE, Type.DOUBLE_TYPE)); static final MethodName AbstractOperations_ToUint32 = MethodName.findStatic( Types.AbstractOperations, "ToUint32", Type.methodType(Type.LONG_TYPE, Types.ExecutionContext, Types.Object)); static final MethodName AbstractOperations_ToUint32_double = MethodName.findStatic( Types.AbstractOperations, "ToUint32", Type.methodType(Type.LONG_TYPE, Type.DOUBLE_TYPE)); static final MethodName AbstractOperations_ToObject = MethodName.findStatic( Types.AbstractOperations, "ToObject", Type.methodType(Types.ScriptObject, Types.ExecutionContext, Types.Object)); static final MethodName AbstractOperations_ToPropertyKey = MethodName.findStatic( Types.AbstractOperations, "ToPropertyKey", Type.methodType(Types.Object, Types.ExecutionContext, Types.Object)); static final MethodName AbstractOperations_ToString = MethodName.findStatic( Types.AbstractOperations, "ToString", Type.methodType(Types.CharSequence, Types.ExecutionContext, Types.Object)); static final MethodName AbstractOperations_ToString_int = MethodName.findStatic( Types.AbstractOperations, "ToString", Type.methodType(Types.String, Type.INT_TYPE)); static final MethodName AbstractOperations_ToString_long = MethodName .findStatic(Types.AbstractOperations, "ToString", Type.methodType(Types.String, Type.LONG_TYPE)); static final MethodName AbstractOperations_ToString_double = MethodName.findStatic( Types.AbstractOperations, "ToString", Type.methodType(Types.String, Type.DOUBLE_TYPE)); // class: AsyncAbstractOperations static final MethodName AsyncAbstractOperations_AsyncFunctionAwait = MethodName.findStatic( Types.AsyncAbstractOperations, "AsyncFunctionAwait", Type.methodType(Type.VOID_TYPE, Types.ExecutionContext, Types.Object)); // class: Boolean static final MethodName Boolean_toString = MethodName.findStatic(Types.Boolean, "toString", Type.methodType(Types.String, Type.BOOLEAN_TYPE)); // class: CharSequence static final MethodName CharSequence_length = MethodName.findInterface(Types.CharSequence, "length", Type.methodType(Type.INT_TYPE)); static final MethodName CharSequence_toString = MethodName.findInterface( Types.CharSequence, "toString", Type.methodType(Types.String)); // class: ExecutionContext static final MethodName ExecutionContext_getLexicalEnvironment = MethodName.findVirtual( Types.ExecutionContext, "getLexicalEnvironment", Type.methodType(Types.LexicalEnvironment)); static final MethodName ExecutionContext_getLexicalEnvironmentRecord = MethodName .findVirtual(Types.ExecutionContext, "getLexicalEnvironmentRecord", Type.methodType(Types.EnvironmentRecord)); static final MethodName ExecutionContext_getVariableEnvironmentRecord = MethodName .findVirtual(Types.ExecutionContext, "getVariableEnvironmentRecord", Type.methodType(Types.EnvironmentRecord)); static final MethodName ExecutionContext_pushLexicalEnvironment = MethodName.findVirtual( Types.ExecutionContext, "pushLexicalEnvironment", Type.methodType(Type.VOID_TYPE, Types.LexicalEnvironment)); static final MethodName ExecutionContext_popLexicalEnvironment = MethodName.findVirtual( Types.ExecutionContext, "popLexicalEnvironment", Type.methodType(Type.VOID_TYPE)); static final MethodName ExecutionContext_replaceLexicalEnvironment = MethodName .findVirtual(Types.ExecutionContext, "replaceLexicalEnvironment", Type.methodType(Type.VOID_TYPE, Types.LexicalEnvironment)); static final MethodName ExecutionContext_restoreLexicalEnvironment = MethodName .findVirtual(Types.ExecutionContext, "restoreLexicalEnvironment", Type.methodType(Type.VOID_TYPE, Types.LexicalEnvironment)); // class: LexicalEnvironment static final MethodName LexicalEnvironment_getEnvRec = MethodName.findVirtual( Types.LexicalEnvironment, "getEnvRec", Type.methodType(Types.EnvironmentRecord)); static final MethodName LexicalEnvironment_cloneDeclarativeEnvironment = MethodName .findStatic(Types.LexicalEnvironment, "cloneDeclarativeEnvironment", Type.methodType(Types.LexicalEnvironment, Types.LexicalEnvironment)); static final MethodName LexicalEnvironment_newDeclarativeEnvironment = MethodName .findStatic(Types.LexicalEnvironment, "newDeclarativeEnvironment", Type.methodType(Types.LexicalEnvironment, Types.LexicalEnvironment)); static final MethodName LexicalEnvironment_newCatchDeclarativeEnvironment = MethodName .findStatic(Types.LexicalEnvironment, "newCatchDeclarativeEnvironment", Type.methodType(Types.LexicalEnvironment, Types.LexicalEnvironment)); static final MethodName LexicalEnvironment_newObjectEnvironment = MethodName.findStatic( Types.LexicalEnvironment, "newObjectEnvironment", Type.methodType( Types.LexicalEnvironment, Types.ScriptObject, Types.LexicalEnvironment, Type.BOOLEAN_TYPE)); // class: OrdinaryFunction static final MethodName OrdinaryFunction_SetFunctionName_String = MethodName.findStatic( Types.OrdinaryFunction, "SetFunctionName", Type.methodType(Type.VOID_TYPE, Types.OrdinaryObject, Types.String)); static final MethodName OrdinaryFunction_SetFunctionName_Symbol = MethodName.findStatic( Types.OrdinaryFunction, "SetFunctionName", Type.methodType(Type.VOID_TYPE, Types.OrdinaryObject, Types.Symbol)); // class: ReturnValue static final MethodName ReturnValue_getValue = MethodName.findVirtual(Types.ReturnValue, "getValue", Type.methodType(Types.Object)); // class: ScriptException static final MethodName ScriptException_getValue = MethodName.findVirtual(Types.ScriptException, "getValue", Type.methodType(Types.Object)); // class: ScriptRuntime static final MethodName ScriptRuntime_CheckCallable = MethodName.findStatic( Types.ScriptRuntime, "CheckCallable", Type.methodType(Types.Callable, Types.Object, Types.ExecutionContext)); static final MethodName ScriptRuntime_EvaluateConstructorMethod = MethodName.findStatic( Types.ScriptRuntime, "EvaluateConstructorMethod", Type.methodType( Types.OrdinaryConstructorFunction, Types.ScriptObject, Types.OrdinaryObject, Types.RuntimeInfo$Function, Type.BOOLEAN_TYPE, Types.ExecutionContext)); static final MethodName ScriptRuntime_EvaluateClassDecorators = MethodName .findStatic(Types.ScriptRuntime, "EvaluateClassDecorators", Type.methodType( Type.VOID_TYPE, Types.OrdinaryConstructorFunction, Types.ArrayList, Types.ExecutionContext)); static final MethodName ScriptRuntime_EvaluateClassMethodDecorators = MethodName .findStatic(Types.ScriptRuntime, "EvaluateClassMethodDecorators", Type.methodType(Type.VOID_TYPE, Types.ArrayList, Types.ExecutionContext)); static final MethodName ScriptRuntime_getClassProto = MethodName.findStatic( Types.ScriptRuntime, "getClassProto", Type.methodType(Types.ScriptObject_, Types.Object, Types.ExecutionContext)); static final MethodName ScriptRuntime_getClassProto_Null = MethodName.findStatic( Types.ScriptRuntime, "getClassProto", Type.methodType(Types.ScriptObject_, Types.ExecutionContext)); static final MethodName ScriptRuntime_getDefaultClassProto = MethodName.findStatic( Types.ScriptRuntime, "getDefaultClassProto", Type.methodType(Types.ScriptObject_, Types.ExecutionContext)); static final MethodName ScriptRuntime_yieldThrowCompletion = MethodName.findStatic( Types.ScriptRuntime, "yieldThrowCompletion", Type.methodType(Types.ScriptObject, Types.ExecutionContext, Types.ScriptObject, Types.ScriptException)); static final MethodName ScriptRuntime_yieldReturnCompletion = MethodName.findStatic( Types.ScriptRuntime, "yieldReturnCompletion", Type.methodType(Types.ScriptObject, Types.ExecutionContext, Types.ScriptObject, Types.ReturnValue)); static final MethodName ScriptRuntime_reportPropertyNotCallable = MethodName.findStatic(Types.ScriptRuntime, "reportPropertyNotCallable", Type.methodType(Types.ScriptException, Types.String, Types.ExecutionContext)); static final MethodName ScriptRuntime_requireObjectResult = MethodName.findStatic(Types.ScriptRuntime, "requireObjectResult", Type.methodType(Types.ScriptObject, Types.Object, Types.String, Types.ExecutionContext)); // class: Type static final MethodName Type_isUndefinedOrNull = MethodName.findStatic(Types._Type, "isUndefinedOrNull", Type.methodType(Type.BOOLEAN_TYPE, Types.Object)); // class: ArrayList static final MethodName ArrayList_init = MethodName.findConstructor(Types.ArrayList, Type.methodType(Type.VOID_TYPE)); static final MethodName ArrayList_add = MethodName.findVirtual(Types.ArrayList, "add", Type.methodType(Type.BOOLEAN_TYPE, Types.Object)); // class: Throwable static final MethodName Throwable_addSuppressed = MethodName.findVirtual(Types.Throwable, "addSuppressed", Type.methodType(Type.VOID_TYPE, Types.Throwable)); } protected final CodeGenerator codegen; protected DefaultCodeGenerator(CodeGenerator codegen) { this.codegen = codegen; } /** * stack: [] {@literal ->} [value|reference] * * @param node * the expression node * @param mv * the code visitor * @return the value type returned by the expression */ protected final ValType expression(Expression node, CodeVisitor mv) { return codegen.expression(node, mv); } /** * stack: [] {@literal ->} [boxed(value)] * * @param node * the expression node * @param mv * the code visitor * @return the value type returned by the expression */ protected final ValType expressionBoxed(Expression node, CodeVisitor mv) { return codegen.expressionBoxed(node, mv); } /** * stack: [] {@literal ->} [] * * @param node * the abrupt node * @param mv * the code visitor * @return the variable holding the saved environment or {@code null} */ protected final Variable<LexicalEnvironment<?>> saveEnvironment(AbruptNode node, CodeVisitor mv) { EnumSet<Abrupt> abrupt = node.getAbrupt(); if (abrupt.contains(Abrupt.Break) || abrupt.contains(Abrupt.Continue)) { return saveEnvironment(mv); } return null; } /** * stack: [] {@literal ->} [] * * @param mv * the code visitor * @return the variable holding the saved environment */ protected final Variable<LexicalEnvironment<?>> saveEnvironment(CodeVisitor mv) { Variable<LexicalEnvironment<?>> savedEnv = mv.newVariable("savedEnv", LexicalEnvironment.class).uncheckedCast(); getLexicalEnvironment(mv); mv.store(savedEnv); return savedEnv; } /** * stack: [] {@literal ->} [] * * @param savedEnv * the variable which holds the saved environment * @param mv * the code visitor */ protected final void restoreEnvironment(Variable<LexicalEnvironment<?>> savedEnv, CodeVisitor mv) { mv.loadExecutionContext(); mv.load(savedEnv); mv.invoke(Methods.ExecutionContext_restoreLexicalEnvironment); } /** * stack: [] {@literal ->} [] * * @param savedEnv * the variable which holds the saved environment * @param mv * the code visitor */ protected final void replaceLexicalEnvironment(Variable<LexicalEnvironment<?>> savedEnv, CodeVisitor mv) { mv.loadExecutionContext(); mv.load(savedEnv); mv.invoke(Methods.ExecutionContext_replaceLexicalEnvironment); } /** * stack: [] {@literal ->} [lexEnv] * * @param mv * the code visitor */ protected final void getLexicalEnvironment(CodeVisitor mv) { mv.loadExecutionContext(); mv.invoke(Methods.ExecutionContext_getLexicalEnvironment); } /** * stack: [] {@literal ->} [] * * @param envRec * the variable which holds the lexical environment record * @param mv * the code visitor */ protected final <R extends EnvironmentRecord> void getLexicalEnvironmentRecord(Variable<? extends R> envRec, CodeVisitor mv) { mv.loadExecutionContext(); mv.invoke(Methods.ExecutionContext_getLexicalEnvironmentRecord); if (envRec.getType() != Types.EnvironmentRecord) { mv.checkcast(envRec.getType()); } mv.store(envRec); } /** * stack: [] {@literal ->} [] * * @param <R> * the environment record type * @param type * the environment record type * @param mv * the code visitor */ protected final <R extends EnvironmentRecord> Value<R> getLexicalEnvironmentRecord(Type type, CodeVisitor mv) { return asm -> { mv.loadExecutionContext(); mv.invoke(Methods.ExecutionContext_getLexicalEnvironmentRecord); if (type != Types.EnvironmentRecord) { mv.checkcast(type); } }; } /** * stack: [] {@literal ->} [] * * @param envRec * the variable which holds the variable environment record * @param mv * the code visitor */ protected final <R extends EnvironmentRecord> void getVariableEnvironmentRecord(Variable<? extends R> envRec, CodeVisitor mv) { mv.loadExecutionContext(); mv.invoke(Methods.ExecutionContext_getVariableEnvironmentRecord); if (envRec.getType() != Types.EnvironmentRecord) { mv.checkcast(envRec.getType()); } mv.store(envRec); } /** * stack: [] {@literal ->} [] * * @param <R> * the environment record type * @param type * the environment record type * @param mv * the code visitor */ protected final <R extends EnvironmentRecord> Value<R> getVariableEnvironmentRecord(Type type, CodeVisitor mv) { return asm -> { mv.loadExecutionContext(); mv.invoke(Methods.ExecutionContext_getVariableEnvironmentRecord); if (type != Types.EnvironmentRecord) { mv.checkcast(type); } }; } /** * Returns the current environment record type. * * @param mv * the code visitor * @return the current environment record type */ protected final Class<? extends EnvironmentRecord> getEnvironmentRecordClass(CodeVisitor mv) { Scope scope = mv.getScope(); while (!scope.isPresent()) { scope = scope.getParent(); } if (scope instanceof ScriptScope) { Script script = ((ScriptScope) scope).getNode(); if (!(script.isEvalScript() || script.isScripting())) { return GlobalEnvironmentRecord.class; } } else if (scope instanceof ModuleScope) { return ModuleEnvironmentRecord.class; } else if (scope instanceof WithScope) { return ObjectEnvironmentRecord.class; } return DeclarativeEnvironmentRecord.class; } /** * Creates a new object environment. * <p> * stack: [obj] {@literal ->} [lexEnv] * * @param mv * the code visitor * @param withEnvironment * the withEnvironment flag */ protected final void newObjectEnvironment(CodeVisitor mv, boolean withEnvironment) { mv.loadExecutionContext(); mv.invoke(Methods.ExecutionContext_getLexicalEnvironment); mv.iconst(withEnvironment); mv.invoke(Methods.LexicalEnvironment_newObjectEnvironment); } /** * Creates a new declarative environment. * <p> * stack: [] {@literal ->} [lexEnv] * * @param mv * the code visitor */ protected final void newDeclarativeEnvironment(BlockScope scope, CodeVisitor mv) { mv.loadExecutionContext(); mv.invoke(Methods.ExecutionContext_getLexicalEnvironment); mv.invoke(Methods.LexicalEnvironment_newDeclarativeEnvironment); } /** * Creates a new declarative environment for a {@code Catch} clause. * <p> * stack: [] {@literal ->} [lexEnv] * * @param mv * the code visitor */ protected final void newCatchDeclarativeEnvironment(BlockScope scope, CodeVisitor mv) { mv.loadExecutionContext(); mv.invoke(Methods.ExecutionContext_getLexicalEnvironment); mv.invoke(Methods.LexicalEnvironment_newCatchDeclarativeEnvironment); } /** * stack: [] {@literal ->} [lexEnv] * * @param mv * the code visitor */ protected final void cloneDeclarativeEnvironment(CodeVisitor mv) { mv.loadExecutionContext(); mv.invoke(Methods.ExecutionContext_getLexicalEnvironment); mv.invoke(Methods.LexicalEnvironment_cloneDeclarativeEnvironment); } /** * stack: [lexEnv] {@literal ->} [] * * @param mv * the code visitor */ protected final void pushLexicalEnvironment(CodeVisitor mv) { mv.loadExecutionContext(); mv.swap(); mv.invoke(Methods.ExecutionContext_pushLexicalEnvironment); } /** * Restores the previous lexical environment. * <p> * stack: [] {@literal ->} [] * * @param mv * the code visitor */ protected final void popLexicalEnvironment(CodeVisitor mv) { mv.loadExecutionContext(); mv.invoke(Methods.ExecutionContext_popLexicalEnvironment); } /** * Emit function call for: {@link LexicalEnvironment#getEnvRec()} * <p> * stack: [] {@literal ->} [] * * @param env * the variable which holds the lexical environment * @param envRec * the variable which holds the environment record * @param mv * the instruction visitor */ protected final <R extends EnvironmentRecord, R2 extends R> void getEnvRec( Variable<? extends LexicalEnvironment<? extends R2>> env, Variable<? extends R> envRec, InstructionVisitor mv) { mv.load(env); mv.invoke(Methods.LexicalEnvironment_getEnvRec); if (envRec.getType() != Types.EnvironmentRecord) { mv.checkcast(envRec.getType()); } mv.store(envRec); } /** * Emit function call for: {@link LexicalEnvironment#getEnvRec()} * <p> * stack: [env] {@literal ->} [env] * * @param env * the variable which holds the lexical environment * @param envRec * the variable which holds the environment record * @param mv * the instruction visitor */ protected final <R extends EnvironmentRecord> void getEnvRec(Variable<? extends R> envRec, InstructionVisitor mv) { mv.dup(); // TODO: Remove dup?! mv.invoke(Methods.LexicalEnvironment_getEnvRec); if (envRec.getType() != Types.EnvironmentRecord) { mv.checkcast(envRec.getType()); } mv.store(envRec); } /** * stack: [object] {@literal ->} [boolean] * * @param mv * the code visitor */ protected final void isUndefinedOrNull(CodeVisitor mv) { mv.invoke(Methods.Type_isUndefinedOrNull); } enum ValType { Undefined, Null, Boolean, Number, Number_int, Number_uint, String, Object, Reference, Any, Empty; public int size() { switch (this) { case Number: case Number_uint: return 2; case Number_int: case Undefined: case Null: case Boolean: case String: case Object: case Reference: case Any: return 1; case Empty: default: return 0; } } public boolean isNumeric() { switch (this) { case Number: case Number_int: case Number_uint: return true; case Undefined: case Null: case Boolean: case String: case Object: case Reference: case Any: case Empty: default: return false; } } public boolean isPrimitive() { switch (this) { case Undefined: case Null: case Boolean: case Number: case Number_int: case Number_uint: case String: return true; case Object: case Reference: case Any: case Empty: default: return false; } } public boolean isJavaPrimitive() { switch (this) { case Boolean: case Number: case Number_int: case Number_uint: return true; case Undefined: case Null: case String: case Object: case Reference: case Any: case Empty: default: return false; } } public Class<?> toClass() { switch (this) { case Boolean: return boolean.class; case String: return CharSequence.class; case Number: return double.class; case Number_int: return int.class; case Number_uint: return long.class; case Object: return ScriptObject.class; case Reference: return Reference.class; case Null: return Null.class; case Undefined: return Undefined.class; case Any: return Object.class; case Empty: default: throw new AssertionError(); } } public Type toType() { switch (this) { case Boolean: return Type.BOOLEAN_TYPE; case String: return Types.CharSequence; case Number: return Type.DOUBLE_TYPE; case Number_int: return Type.INT_TYPE; case Number_uint: return Type.LONG_TYPE; case Object: return Types.ScriptObject; case Reference: return Types.Reference; case Null: return Types.Null; case Undefined: return Types.Undefined; case Any: return Types.Object; case Empty: default: throw new AssertionError(); } } public Type toBoxedType() { switch (this) { case Boolean: return Types.Boolean; case String: return Types.CharSequence; case Number: return Types.Double; case Number_int: return Types.Integer; case Number_uint: return Types.Long; case Object: return Types.ScriptObject; case Reference: return Types.Reference; case Null: return Types.Null; case Undefined: return Types.Undefined; case Any: return Types.Object; case Empty: default: throw new AssertionError(); } } public static ValType of(Type type) { if (type.isPrimitive()) { if (Type.BOOLEAN_TYPE.equals(type)) { return ValType.Boolean; } if (Type.INT_TYPE.equals(type)) { return ValType.Number_int; } if (Type.LONG_TYPE.equals(type)) { return ValType.Number_uint; } if (Type.DOUBLE_TYPE.equals(type)) { return ValType.Number; } return ValType.Any; } if (Types.Boolean.equals(type)) { return ValType.Boolean; } if (Types.Integer.equals(type)) { return ValType.Number_int; } if (Types.Long.equals(type)) { return ValType.Number_uint; } if (Types.Double.equals(type)) { return ValType.Number; } if (Types.Null.equals(type)) { return ValType.Null; } if (Types.Undefined.equals(type)) { return ValType.Undefined; } if (Types.String.equals(type) || Types.CharSequence.equals(type)) { return ValType.String; } if (Types.ScriptObject.equals(type)) { return ValType.Object; } if (Types.OrdinaryObject.equals(type)) { return ValType.Object; } return ValType.Any; } } /** * stack: [Object] {@literal ->} [boolean] * * @param from * the input value type * @param mv * the code visitor * @return the returned value type */ protected static final ValType ToPrimitive(ValType from, CodeVisitor mv) { switch (from) { case Number: case Number_int: case Number_uint: case Undefined: case Null: case Boolean: case String: return from; case Object: case Any: mv.loadExecutionContext(); mv.swap(); mv.invoke(Methods.AbstractOperations_ToPrimitive); return ValType.Any; case Empty: case Reference: default: throw new AssertionError(); } } /** * stack: [Object] {@literal ->} [boolean] * * @param from * the input value type * @param mv * the code visitor */ protected static final void ToBoolean(ValType from, CodeVisitor mv) { switch (from) { case Number: mv.invoke(Methods.AbstractOperations_ToBoolean_double); return; case Number_int: mv.i2d(); mv.invoke(Methods.AbstractOperations_ToBoolean_double); return; case Number_uint: mv.l2d(); mv.invoke(Methods.AbstractOperations_ToBoolean_double); return; case Undefined: case Null: mv.pop(); mv.iconst(false); return; case Boolean: return; case String: { Jump l0 = new Jump(), l1 = new Jump(); mv.invoke(Methods.CharSequence_length); mv.ifeq(l0); mv.iconst(true); mv.goTo(l1); mv.mark(l0); mv.iconst(false); mv.mark(l1); return; } case Object: mv.pop(); mv.iconst(true); return; case Any: mv.invoke(Methods.AbstractOperations_ToBoolean); return; case Empty: case Reference: default: throw new AssertionError(); } } /** * stack: [Object] {@literal ->} [double] * * @param from * the input value type * @param mv * the code visitor */ protected static final void ToNumber(ValType from, CodeVisitor mv) { switch (from) { case Number: return; case Number_int: mv.i2d(); return; case Number_uint: mv.l2d(); return; case Undefined: mv.pop(); mv.get(Fields.Double_NaN); return; case Null: mv.pop(); mv.dconst(0); return; case Boolean: mv.i2d(); return; case String: mv.invoke(Methods.AbstractOperations_ToNumber_CharSequence); return; case Object: case Any: mv.loadExecutionContext(); mv.swap(); mv.invoke(Methods.AbstractOperations_ToNumber); return; case Empty: case Reference: default: throw new AssertionError(); } } /** * stack: [Object] {@literal ->} [int] * * @param from * the input value type * @param mv * the code visitor */ protected static final void ToInt32(ValType from, CodeVisitor mv) { switch (from) { case Number: mv.invoke(Methods.AbstractOperations_ToInt32_double); return; case Number_int: return; case Number_uint: mv.l2i(); return; case Undefined: case Null: mv.pop(); mv.iconst(0); return; case Boolean: return; case String: mv.invoke(Methods.AbstractOperations_ToNumber_CharSequence); mv.invoke(Methods.AbstractOperations_ToInt32_double); return; case Object: case Any: mv.loadExecutionContext(); mv.swap(); mv.invoke(Methods.AbstractOperations_ToInt32); return; case Empty: case Reference: default: throw new AssertionError(); } } /** * stack: [Object] {@literal ->} [long] * * @param from * the input value type * @param mv * the code visitor */ protected static final void ToUint32(ValType from, CodeVisitor mv) { switch (from) { case Number: mv.invoke(Methods.AbstractOperations_ToUint32_double); return; case Number_int: mv.i2l(); mv.lconst(0xffff_ffffL); mv.land(); return; case Number_uint: return; case Undefined: case Null: mv.pop(); mv.lconst(0); return; case Boolean: mv.i2l(); return; case String: mv.invoke(Methods.AbstractOperations_ToNumber_CharSequence); mv.invoke(Methods.AbstractOperations_ToUint32_double); return; case Object: case Any: mv.loadExecutionContext(); mv.swap(); mv.invoke(Methods.AbstractOperations_ToUint32); return; case Empty: case Reference: default: throw new AssertionError(); } } /** * stack: [Object] {@literal ->} [CharSequence] * * @param from * the input value type * @param mv * the code visitor */ protected static final void ToString(ValType from, CodeVisitor mv) { switch (from) { case Number: mv.invoke(Methods.AbstractOperations_ToString_double); return; case Number_int: mv.invoke(Methods.AbstractOperations_ToString_int); return; case Number_uint: mv.invoke(Methods.AbstractOperations_ToString_long); return; case Undefined: mv.pop(); mv.aconst("undefined"); return; case Null: mv.pop(); mv.aconst("null"); return; case Boolean: mv.invoke(Methods.Boolean_toString); return; case String: return; case Object: case Any: mv.loadExecutionContext(); mv.swap(); mv.invoke(Methods.AbstractOperations_ToString); return; case Empty: case Reference: default: throw new AssertionError(); } } /** * stack: [Object] {@literal ->} [String] * * @param from * the input value type * @param mv * the code visitor */ protected static final void ToFlatString(ValType from, CodeVisitor mv) { switch (from) { case Number: mv.invoke(Methods.AbstractOperations_ToString_double); return; case Number_int: mv.invoke(Methods.AbstractOperations_ToString_int); return; case Number_uint: mv.invoke(Methods.AbstractOperations_ToString_long); return; case Undefined: mv.pop(); mv.aconst("undefined"); return; case Null: mv.pop(); mv.aconst("null"); return; case Boolean: mv.invoke(Methods.Boolean_toString); return; case String: mv.invoke(Methods.CharSequence_toString); return; case Object: case Any: mv.loadExecutionContext(); mv.swap(); mv.invoke(Methods.AbstractOperations_ToFlatString); return; case Empty: case Reference: default: throw new AssertionError(); } } /** * stack: [Object] {@literal ->} [ScriptObject] * * @param from * the input value type * @param mv * the code visitor */ protected static final void ToObject(ValType from, CodeVisitor mv) { switch (from) { case Number: case Number_int: case Number_uint: case Boolean: mv.toBoxed(from); break; case Object: return; case Undefined: case Null: case String: case Any: break; case Empty: case Reference: default: throw new AssertionError(); } mv.loadExecutionContext(); mv.swap(); mv.invoke(Methods.AbstractOperations_ToObject); } /** * stack: [Object] {@literal ->} [ScriptObject] * * @param node * the current node * @param from * the input value type * @param mv * the code visitor */ protected static final void ToObject(Node node, ValType from, CodeVisitor mv) { switch (from) { case Number: case Number_int: case Number_uint: case Boolean: mv.toBoxed(from); break; case Object: return; case Undefined: case Null: case String: case Any: break; case Empty: case Reference: default: throw new AssertionError(); } mv.lineInfo(node); mv.loadExecutionContext(); mv.swap(); mv.invoke(Methods.AbstractOperations_ToObject); } /** * stack: [Object] {@literal ->} [String|Symbol] * * @param from * the input value type * @param mv * the code visitor */ protected static final ValType ToPropertyKey(ValType from, CodeVisitor mv) { switch (from) { case Number: mv.invoke(Methods.AbstractOperations_ToString_double); return ValType.String; case Number_int: mv.invoke(Methods.AbstractOperations_ToString_int); return ValType.String; case Number_uint: mv.invoke(Methods.AbstractOperations_ToString_long); return ValType.String; case Undefined: mv.pop(); mv.aconst("undefined"); return ValType.String; case Null: mv.pop(); mv.aconst("null"); return ValType.String; case Boolean: mv.invoke(Methods.Boolean_toString); return ValType.String; case String: mv.invoke(Methods.CharSequence_toString); return ValType.String; case Object: mv.loadExecutionContext(); mv.swap(); mv.invoke(Methods.AbstractOperations_ToFlatString); return ValType.String; case Any: mv.loadExecutionContext(); mv.swap(); mv.invoke(Methods.AbstractOperations_ToPropertyKey); return ValType.Any; case Empty: case Reference: default: throw new AssertionError(); } } /** * stack: [propertyKey, function] {@literal ->} [propertyKey, function] * * @param node * the function or class node * @param propertyKeyType * the property key value type * @param mv * the code visitor */ protected static void SetFunctionName(Node node, ValType propertyKeyType, CodeVisitor mv) { Jump hasOwnName = null; switch (hasOwnNameProperty(node)) { case HasOwn: return; case HasComputed: emitHasOwnNameProperty(mv); hasOwnName = new Jump(); mv.ifne(hasOwnName); default: } // stack: [propertyKey, function] -> [propertyKey, function, function, propertyKey] mv.dup2(); mv.swap(); if (propertyKeyType == ValType.String) { mv.invoke(Methods.OrdinaryFunction_SetFunctionName_String); } else { assert propertyKeyType == ValType.Any; Jump isString = new Jump(), afterSetFunctionName = new Jump(); mv.dup(); mv.instanceOf(Types.String); mv.ifeq(isString); { // stack: [propertyKey, function, function, propertyKey] -> [propertyKey, function] mv.checkcast(Types.String); mv.invoke(Methods.OrdinaryFunction_SetFunctionName_String); mv.goTo(afterSetFunctionName); } { mv.mark(isString); mv.checkcast(Types.Symbol); mv.invoke(Methods.OrdinaryFunction_SetFunctionName_Symbol); } mv.mark(afterSetFunctionName); } if (hasOwnName != null) { mv.mark(hasOwnName); } } /** * stack: [function] {@literal ->} [function] * * @param node * the function or class node * @param name * the new function name * @param mv * the code visitor */ protected static void SetFunctionName(Node node, Name name, CodeVisitor mv) { SetFunctionName(node, name.getIdentifier(), mv); } /** * stack: [function] {@literal ->} [function] * * @param node * the function or class node * @param name * the new function name * @param mv * the code visitor */ protected static void SetFunctionName(Node node, String name, CodeVisitor mv) { Jump hasOwnName = null; switch (hasOwnNameProperty(node)) { case HasOwn: return; case HasComputed: emitHasOwnNameProperty(mv); hasOwnName = new Jump(); mv.ifne(hasOwnName); default: } // stack: [function] -> [function, function, name] mv.dup(); mv.aconst(name); // stack: [function, function, name] -> [function] mv.invoke(Methods.OrdinaryFunction_SetFunctionName_String); if (hasOwnName != null) { mv.mark(hasOwnName); } } private static void emitHasOwnNameProperty(CodeVisitor mv) { // stack: [function] -> [function, cx, function, "name"] mv.dup(); mv.loadExecutionContext(); mv.swap(); mv.aconst("name"); // stack: [function, cx, function, "name"] -> [function, hasOwn] mv.invoke(Methods.AbstractOperations_HasOwnProperty); } private enum NameProperty { HasOwn, HasComputed, None } private static NameProperty hasOwnNameProperty(Node node) { if (node instanceof FunctionNode) { return NameProperty.None; } assert node instanceof ClassDefinition : node.getClass(); for (PropertyDefinition property : ((ClassDefinition) node).getProperties()) { if (property instanceof MethodDefinition) { MethodDefinition methodDefinition = (MethodDefinition) property; if (methodDefinition.isStatic()) { String methodName = methodDefinition.getPropertyName().getName(); if (methodName == null) { return NameProperty.HasComputed; } if ("name".equals(methodName)) { return NameProperty.HasOwn; } } if (!methodDefinition.getDecorators().isEmpty()) { // Decorator expressions are like computed names. return NameProperty.HasComputed; } } else if (property instanceof PropertyValueDefinition) { // Only static class properties are supported. PropertyValueDefinition valueDefinition = (PropertyValueDefinition) property; String methodName = valueDefinition.getPropertyName().getName(); if (methodName == null) { return NameProperty.HasComputed; } if ("name".equals(methodName)) { return NameProperty.HasOwn; } } } return NameProperty.None; } /** * 14.5.14 Runtime Semantics: ClassDefinitionEvaluation * * @param def * the class definition node * @param className * the class name or {@code null} if not present * @param mv * the code visitor */ protected final void ClassDefinitionEvaluation(ClassDefinition def, Name className, CodeVisitor mv) { mv.enterVariableScope(); Variable<ArrayList<Callable>> classDecorators = null; boolean hasClassDecorators = !def.getDecorators().isEmpty(); if (hasClassDecorators) { classDecorators = newDecoratorVariable("classDecorators", mv); evaluateDecorators(classDecorators, def.getDecorators(), mv); } mv.enterClassDefinition(); // step 1 (not applicable) // steps 2-4 BlockScope scope = def.getScope(); assert (scope != null && scope.isPresent()) == (className != null); Variable<DeclarativeEnvironmentRecord> classScopeEnvRec = null; if (className != null) { // stack: [] -> [classScope] newDeclarativeEnvironment(scope, mv); classScopeEnvRec = mv.newVariable("classScopeEnvRec", DeclarativeEnvironmentRecord.class); getEnvRec(classScopeEnvRec, mv); // stack: [classScope] -> [classScope] Name innerName = scope.resolveName(className, false); BindingOp<DeclarativeEnvironmentRecord> op = BindingOp.of(classScopeEnvRec, innerName); op.createImmutableBinding(classScopeEnvRec, innerName, true, mv); // stack: [classScope] -> [] pushLexicalEnvironment(mv); mv.enterScope(def); } // steps 5-7 // stack: [] -> [<constructorParent,proto>] Expression classHeritage = def.getHeritage(); if (classHeritage == null) { mv.loadExecutionContext(); mv.invoke(Methods.ScriptRuntime_getDefaultClassProto); } else if (classHeritage instanceof NullLiteral) { mv.loadExecutionContext(); mv.invoke(Methods.ScriptRuntime_getClassProto_Null); } else { expressionBoxed(classHeritage, mv); mv.loadExecutionContext(); mv.lineInfo(def); mv.invoke(Methods.ScriptRuntime_getClassProto); } // stack: [<constructorParent,proto>] -> [<constructorParent,proto>] Variable<OrdinaryObject> proto = mv.newVariable("proto", OrdinaryObject.class); mv.dup(); mv.aload(1, Types.ScriptObject); mv.checkcast(Types.OrdinaryObject); mv.store(proto); // stack: [<constructorParent,proto>] -> [constructorParent, proto] mv.aload(0, Types.ScriptObject); mv.load(proto); // steps 8-9 // stack: [constructorParent, proto] -> [constructorParent, proto, <rti>] MethodDefinition constructor = ConstructorMethod(def); assert constructor != null; MethodName method = codegen.compile(def); // Runtime Semantics: Evaluation -> MethodDefinition mv.invoke(method); // step 10 (not applicable) // steps 11-18 // stack: [constructorParent, proto, <rti>] -> [F] mv.iconst(classHeritage != null); mv.loadExecutionContext(); mv.lineInfo(def); mv.invoke(Methods.ScriptRuntime_EvaluateConstructorMethod); // stack: [F] -> [] Variable<OrdinaryConstructorFunction> F = mv.newVariable("F", OrdinaryConstructorFunction.class); mv.store(F); Variable<ArrayList<Object>> methodDecorators = null; boolean hasMethodDecorators = HasDecorators(def); if (hasMethodDecorators) { methodDecorators = newDecoratorVariable("methodDecorators", mv); } if (!constructor.getDecorators().isEmpty()) { addDecoratorObject(methodDecorators, proto, mv); evaluateDecorators(methodDecorators, constructor.getDecorators(), mv); addDecoratorKey(methodDecorators, "constructor", mv); } // steps 19-21 ClassPropertyEvaluation(codegen, def.getProperties(), F, proto, methodDecorators, mv); if (hasClassDecorators) { mv.load(F); mv.load(classDecorators); mv.loadExecutionContext(); mv.lineInfo(def); mv.invoke(Methods.ScriptRuntime_EvaluateClassDecorators); } if (hasMethodDecorators) { mv.load(methodDecorators); mv.loadExecutionContext(); mv.lineInfo(def); mv.invoke(Methods.ScriptRuntime_EvaluateClassMethodDecorators); } // steps 22-23 (moved) if (className != null) { // stack: [] -> [] Name innerName = scope.resolveName(className, false); BindingOp<DeclarativeEnvironmentRecord> op = BindingOp.of(classScopeEnvRec, innerName); op.initializeBinding(classScopeEnvRec, innerName, F, mv); mv.exitScope(); popLexicalEnvironment(mv); } // stack: [] -> [F] mv.load(F); mv.exitVariableScope(); // step 24 (return F) mv.exitClassDefinition(); } protected final <T> Variable<ArrayList<T>> newDecoratorVariable(String name, CodeVisitor mv) { Variable<ArrayList<T>> var = mv.newVariable(name, ArrayList.class).uncheckedCast(); mv.anew(Types.ArrayList, Methods.ArrayList_init); mv.store(var); return var; } protected final void addDecoratorObject(Variable<ArrayList<Object>> var, Variable<? extends ScriptObject> object, CodeVisitor mv) { mv.load(var); mv.load(object); mv.invoke(Methods.ArrayList_add); mv.pop(); } protected final void addDecoratorKey(Variable<ArrayList<Object>> var, String propertyKey, CodeVisitor mv) { mv.load(var); mv.aconst(propertyKey); mv.invoke(Methods.ArrayList_add); mv.pop(); } protected final void addDecoratorKey(Variable<ArrayList<Object>> var, ValType type, CodeVisitor mv) { mv.dup(type); mv.load(var); mv.swap(type.toType(), Types.ArrayList); mv.invoke(Methods.ArrayList_add); mv.pop(); } protected final <T> void evaluateDecorators(Variable<ArrayList<T>> var, List<Expression> decorators, CodeVisitor mv) { for (Expression decorator : decorators) { mv.load(var); expressionBoxed(decorator, mv); mv.loadExecutionContext(); mv.lineInfo(decorator); mv.invoke(Methods.ScriptRuntime_CheckCallable); mv.invoke(Methods.ArrayList_add); mv.pop(); } } /** * 14.4 Generator Function Definitions * <p> * 14.4.14 Runtime Semantics: Evaluation * <ul> * <li>YieldExpression : yield * AssignmentExpression * </ul> * <p> * stack: [value] {@literal ->} [value'] * * @param node * the expression node * @param mv * the code visitor */ protected final void delegatedYield(Expression node, CodeVisitor mv) { if (!mv.isAsync()) { delegatedYield(node, (iterator, received) -> { IteratorNext(node, iterator, received, mv); } , (iterator, received) -> { mv.loadExecutionContext(); mv.load(iterator); mv.load(received); mv.checkcast(Types.ScriptException); mv.invoke(Methods.ScriptRuntime_yieldThrowCompletion); } , (iterator, received) -> { mv.loadExecutionContext(); mv.load(iterator); mv.load(received); mv.checkcast(Types.ReturnValue); mv.invoke(Methods.ScriptRuntime_yieldReturnCompletion); } , mv); } else { delegatedYield(node, (iterator, received) -> { IteratorNext(node, iterator, received, mv); await(node, mv); // FIXME: spec bug - missing type check requireObjectResult(node, "next", mv); } , (iterator, received) -> { mv.enterVariableScope(); Variable<Callable> throwMethod = mv.newVariable("throwMethod", Callable.class); GetMethod(node, iterator, "throw", mv); mv.store(throwMethod); Jump noThrow = new Jump(), nextYield = new Jump(); mv.load(throwMethod); mv.ifnull(noThrow); { InvokeMethod(node, mv, throwMethod, iterator, __ -> { mv.load(received); mv.checkcast(Types.ScriptException); mv.invoke(Methods.ScriptException_getValue); }); await(node, mv); requireObjectResult(node, "throw", mv); mv.goTo(nextYield); } mv.mark(noThrow); { asyncIteratorClose(node, iterator, mv); reportPropertyNotCallable(node, "throw", mv); mv.athrow(); } mv.mark(nextYield); mv.exitVariableScope(); } , (iterator, received) -> { mv.enterVariableScope(); Variable<Callable> returnMethod = mv.newVariable("returnMethod", Callable.class); GetMethod(node, iterator, "return", mv); mv.store(returnMethod); Jump noReturn = new Jump(), nextYield = new Jump(); mv.load(returnMethod); mv.ifnull(noReturn); { InvokeMethod(node, mv, returnMethod, iterator, __ -> { mv.load(received); mv.checkcast(Types.ReturnValue); mv.invoke(Methods.ReturnValue_getValue); }); await(node, mv); requireObjectResult(node, "return", mv); mv.goTo(nextYield); } mv.mark(noReturn); { mv.anull(); } mv.mark(nextYield); mv.exitVariableScope(); } , mv); } } private void delegatedYield(Expression node, BiConsumer<Variable<ScriptObject>, Variable<Object>> iterNext, BiConsumer<Variable<ScriptObject>, Variable<Object>> iterThrow, BiConsumer<Variable<ScriptObject>, Variable<Object>> iterReturn, CodeVisitor mv) { Jump iteratorNext = new Jump(); Jump generatorYield = new Jump(); Jump generatorYieldOrReturn = new Jump(); Jump done = new Jump(); mv.lineInfo(node); mv.enterVariableScope(); Variable<ScriptObject> iterator = mv.newVariable("iterator", ScriptObject.class); Variable<ScriptObject> innerResult = mv.newVariable("innerResult", ScriptObject.class); Variable<Object> received = mv.newVariable("received", Object.class); /* steps 3-4 */ // stack: [value] -> [] mv.loadExecutionContext(); mv.swap(); mv.invoke(Methods.AbstractOperations_GetIterator); mv.store(iterator); /* step 5 */ // stack: [] -> [] mv.loadUndefined(); mv.store(received); /* step 6.a.i-6.a.ii */ // stack: [] -> [] mv.mark(iteratorNext); iterNext.accept(iterator, received); mv.store(innerResult); /* steps 6.a.iii-6.a.v */ // stack: [] -> [] IteratorComplete(node, innerResult, mv); mv.ifne(done); /* step 6.a.vi */ // stack: [] -> [Object(innerResult)] // force stack top to Object-type mv.mark(generatorYield); mv.load(innerResult); mv.checkcast(Types.Object); mv.suspend(); mv.store(received); /* step 6.b */ Jump isException = new Jump(); mv.load(received); mv.instanceOf(Types.ScriptException); mv.ifeq(isException); { /* steps 6.b.iii.1-4, 6.b.iv */ iterThrow.accept(iterator, received); mv.store(innerResult); mv.goTo(generatorYieldOrReturn); } mv.mark(isException); /* step 6.c */ mv.load(received); mv.instanceOf(Types.ReturnValue); mv.ifeq(iteratorNext); { /* steps 6.c.i-vii */ iterReturn.accept(iterator, received); mv.store(innerResult); mv.load(innerResult); mv.ifnonnull(generatorYieldOrReturn); { /* step 6.c.iv */ mv.popStack(); mv.returnCompletion(__ -> { mv.load(received); mv.checkcast(Types.ReturnValue); mv.invoke(Methods.ReturnValue_getValue); }); } } mv.mark(generatorYieldOrReturn); /* steps 6.b.iii.5-6, 6.c.viii-ix */ IteratorComplete(node, innerResult, mv); mv.ifeq(generatorYield); /* step 6.b.iii.7, 6.c.x */ mv.popStack(); mv.returnCompletion(__ -> { IteratorValue(node, innerResult, mv); }); /* step 6.a.v */ mv.mark(done); IteratorValue(node, innerResult, mv); mv.exitVariableScope(); } /** * 14.4 Generator Function Definitions * <p> * 14.4.14 Runtime Semantics: Evaluation * <ul> * <li>YieldExpression : yield * <li>YieldExpression : yield AssignmentExpression * </ul> * <p> * stack: [value] {@literal ->} [value'] * * @param node * the expression node * @param mv * the code visitor */ protected final void yield(Expression node, CodeVisitor mv) { mv.lineInfo(node); mv.loadExecutionContext(); mv.swap(); mv.iconst(false); mv.invoke(Methods.AbstractOperations_CreateIterResultObject); // force stack top to Object-type mv.checkcast(Types.Object); mv.suspend(); // check for exception throwAfterResume(mv); // check for return value returnAfterResume(mv); } /** * Extension: Async Function Definitions * <p> * stack: [value] {@literal ->} [value'] * * @param node * the expression node * @param mv * the code visitor */ protected final void await(Node node, CodeVisitor mv) { // stack: [value] -> [value'] mv.loadExecutionContext(); mv.swap(); mv.lineInfo(node); mv.invoke(Methods.AsyncAbstractOperations_AsyncFunctionAwait); // Reserve stack space for await return value. mv.anull(); mv.suspend(); // check for exception throwAfterResume(mv); } private void throwAfterResume(CodeVisitor mv) { Jump isException = new Jump(); mv.dup(); mv.instanceOf(Types.ScriptException); mv.ifeq(isException); { mv.checkcast(Types.ScriptException); mv.athrow(); } mv.mark(isException); } private void returnAfterResume(CodeVisitor mv) { Jump isReturn = new Jump(); mv.dup(); mv.instanceOf(Types.ReturnValue); mv.ifeq(isReturn); { mv.checkcast(Types.ReturnValue); mv.invoke(Methods.ReturnValue_getValue); if (mv.getStackSize() == 1) { mv.returnCompletion(); } else { mv.enterVariableScope(); Variable<Object> returnValue = mv.newVariable("returnValue", Object.class); mv.store(returnValue); mv.popStack(); mv.returnCompletion(returnValue); mv.exitVariableScope(); } } mv.mark(isReturn); } /** * IteratorNext ( iterator, value ) * * @param node * the ast node * @param iterator * the script iterator object * @param mv * the code visitor */ protected final void IteratorNext(Node node, Variable<ScriptObject> iterator, CodeVisitor mv) { mv.loadExecutionContext(); mv.load(iterator); mv.lineInfo(node); mv.invoke(Methods.AbstractOperations_IteratorNext); } /** * IteratorNext ( iterator, value ) * * @param node * the ast node * @param iterator * the script iterator object * @param value * the value to pass to the next() function * @param mv * the code visitor */ protected final void IteratorNext(Node node, Variable<ScriptObject> iterator, Value<Object> value, CodeVisitor mv) { mv.loadExecutionContext(); mv.load(iterator); mv.load(value); mv.lineInfo(node); mv.invoke(Methods.AbstractOperations_IteratorNext_Object); } /** * IteratorComplete (iterResult) * * @param node * the ast node * @param iterResult * the iterator result object * @param mv * the code visitor */ protected final void IteratorComplete(Node node, Variable<ScriptObject> iterResult, CodeVisitor mv) { mv.loadExecutionContext(); mv.load(iterResult); mv.lineInfo(node); mv.invoke(Methods.AbstractOperations_IteratorComplete); } /** * IteratorValue (iterResult) * * @param node * the ast node * @param iterResult * the iterator result object * @param mv * the code visitor */ protected final void IteratorValue(Node node, Variable<ScriptObject> iterResult, CodeVisitor mv) { mv.loadExecutionContext(); mv.load(iterResult); mv.lineInfo(node); mv.invoke(Methods.AbstractOperations_IteratorValue); } /** * GetMethod (O, P) * * @param node * the ast node * @param object * the script object * @param methodName * the method name * @param mv * the code visitor */ final void GetMethod(Node node, Variable<ScriptObject> object, String methodName, CodeVisitor mv) { mv.loadExecutionContext(); mv.load(object); mv.aconst(methodName); mv.lineInfo(node); mv.invoke(Methods.AbstractOperations_GetMethod); } /** * Emit: {@code method.call(cx, thisValue, arguments)} * * @param node * the ast node * @param mv * the code visitor * @param method * the callable object * @param thisValue * the call this-value * @param arguments * the method call arguments */ final void InvokeMethod(Node node, CodeVisitor mv, Value<Callable> method, Value<?> thisValue, Value<?>... arguments) { mv.load(method); mv.loadExecutionContext(); mv.load(thisValue); if (arguments.length == 0) { mv.get(Fields.ScriptRuntime_EMPTY_ARRAY); } else { mv.anewarray(Types.Object, arguments); } mv.lineInfo(node); mv.invokedynamic(Bootstrap.getCallName(), Bootstrap.getCallMethodDescriptor(), Bootstrap.getCallBootstrap()); } /** * Emit: * * <pre> * Callable returnMethod = GetMethod(cx, iterator, "return"); * if (returnMethod != null) { * Object innerResult = returnMethod.call(cx, iterator); * await; * if (!(innerResult instanceof ScriptObject)) { * throw newTypeError(cx, Messages.Key.NotObjectTypeReturned, "return"); * } * } * </pre> * * @param node * the ast node * @param iterator * the script iterator object * @param mv * the code visitor */ final void asyncIteratorClose(Node node, Variable<ScriptObject> iterator, CodeVisitor mv) { IteratorClose(node, iterator, returnMethod -> { InvokeMethod(node, mv, returnMethod, iterator); await(node, mv); requireObjectResult(node, "return", mv); mv.pop(); } , mv); } /** * Emit: * * <pre> * Callable returnMethod = GetMethod(cx, iterator, "return"); * if (returnMethod != null) { * try { * returnMethod.call(cx, iterator); * await; * } catch (ScriptException e) { * if (throwable != e) { * throwable.addSuppressed(e); * } * } * } * </pre> * * @param node * the ast node * @param iterator * the script iterator object * @param mv * the code visitor */ final void asyncIteratorClose(Node node, Variable<ScriptObject> iterator, Variable<? extends Throwable> throwable, CodeVisitor mv) { IteratorClose(node, iterator, returnMethod -> { TryCatchLabel startCatch = new TryCatchLabel(); TryCatchLabel endCatch = new TryCatchLabel(), handlerCatch = new TryCatchLabel(); Jump noException = new Jump(); mv.mark(startCatch); { InvokeMethod(node, mv, returnMethod, iterator); await(node, mv); mv.pop(); mv.goTo(noException); } mv.mark(endCatch); mv.catchHandler(handlerCatch, Types.ScriptException); { mv.enterVariableScope(); Variable<ScriptException> exception = mv.newVariable("exception", ScriptException.class); mv.store(exception); mv.load(throwable); mv.load(exception); mv.ifacmpeq(noException); { mv.load(throwable); mv.load(exception); mv.invoke(Methods.Throwable_addSuppressed); } mv.exitVariableScope(); } mv.tryCatch(startCatch, endCatch, handlerCatch, Types.ScriptException); mv.mark(noException); } , mv); } /** * <pre> * Callable returnMethod = GetMethod(cx, iterator, "return"); * if (returnMethod != null) { * <invoke return> * } * </pre> * * @param node * the ast node * @param iterator * the script iterator object * @param invokeReturn * the code snippet to invoke return() * @param mv * the code visitor */ final void IteratorClose(Node node, Variable<ScriptObject> iterator, Consumer<Variable<Callable>> invokeReturn, CodeVisitor mv) { mv.enterVariableScope(); Variable<Callable> returnMethod = mv.newVariable("returnMethod", Callable.class); GetMethod(node, iterator, "return", mv); mv.store(returnMethod); Jump done = new Jump(); mv.load(returnMethod); mv.ifnull(done); { invokeReturn.accept(returnMethod); } mv.mark(done); mv.exitVariableScope(); } /** * Extension: Async Generator Function Definitions * <p> * stack: [value] {@literal ->} [] * * @param node * the ast node * @param methodName * the method name * @param mv * the code visitor */ protected final void requireObjectResult(Node node, String methodName, CodeVisitor mv) { mv.aconst(methodName); mv.loadExecutionContext(); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_requireObjectResult); } private void reportPropertyNotCallable(Node node, String methodName, CodeVisitor mv) { mv.aconst(methodName); mv.loadExecutionContext(); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_reportPropertyNotCallable); } }