/** * 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.semantics.StaticSemantics.IsStrict; import com.github.anba.es6draft.ast.ClassDefinition; import com.github.anba.es6draft.ast.FunctionDeclaration; import com.github.anba.es6draft.ast.FunctionExpression; import com.github.anba.es6draft.ast.FunctionNode; import com.github.anba.es6draft.ast.FunctionNode.ThisMode; import com.github.anba.es6draft.ast.MethodDefinition; import com.github.anba.es6draft.compiler.CodeGenerator.FunctionName; import com.github.anba.es6draft.compiler.assembler.Code.MethodCode; 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.Variable; import com.github.anba.es6draft.runtime.ExecutionContext; import com.github.anba.es6draft.runtime.internal.CompatibilityOption; import com.github.anba.es6draft.runtime.types.Constructor; import com.github.anba.es6draft.runtime.types.ScriptObject; import com.github.anba.es6draft.runtime.types.builtins.FunctionObject; import com.github.anba.es6draft.runtime.types.builtins.LegacyConstructorFunction; import com.github.anba.es6draft.runtime.types.builtins.OrdinaryAsyncFunction; import com.github.anba.es6draft.runtime.types.builtins.OrdinaryAsyncGenerator; import com.github.anba.es6draft.runtime.types.builtins.OrdinaryConstructorFunction; import com.github.anba.es6draft.runtime.types.builtins.OrdinaryConstructorGenerator; import com.github.anba.es6draft.runtime.types.builtins.OrdinaryFunction; import com.github.anba.es6draft.runtime.types.builtins.OrdinaryGenerator; /** * Generates bytecode for the function entry method */ final class FunctionCodeGenerator { private static final class Fields { static final FieldName Intrinsics_ObjectPrototype = FieldName.findStatic(Types.Intrinsics, "ObjectPrototype", Types.Intrinsics); static final FieldName MessagesKey_InvalidCallClass = FieldName.findStatic(Types.Messages$Key, "InvalidCallClass", Types.Messages$Key); static final FieldName MessagesKey_NotObjectTypeFromConstructor = FieldName.findStatic(Types.Messages$Key, "NotObjectTypeFromConstructor", Types.Messages$Key); } private static final class Methods { // ExecutionContext static final MethodName ExecutionContext_newFunctionExecutionContext = MethodName.findStatic( Types.ExecutionContext, "newFunctionExecutionContext", Type.methodType(Types.ExecutionContext, Types.FunctionObject, Types.LexicalEnvironment)); static final MethodName ExecutionContext_getCurrentFunction = MethodName.findVirtual(Types.ExecutionContext, "getCurrentFunction", Type.methodType(Types.FunctionObject)); static final MethodName ExecutionContext_getFunctionVariableEnvironmentRecord = MethodName.findVirtual( Types.ExecutionContext, "getFunctionVariableEnvironmentRecord", Type.methodType(Types.FunctionEnvironmentRecord)); // FunctionEnvironmentRecord static final MethodName FunctionEnvironmentRecord_getThisBinding = MethodName.findVirtual( Types.FunctionEnvironmentRecord, "getThisBinding", Type.methodType(Types.Object, Types.ExecutionContext)); // LegacyFunction static final MethodName LegacyConstructorFunction_getLegacyArguments = MethodName.findVirtual( Types.LegacyConstructorFunction, "getLegacyArguments", Type.methodType(Types.LegacyConstructorFunction$Arguments)); static final MethodName LegacyConstructorFunction_getLegacyCaller = MethodName .findVirtual(Types.LegacyConstructorFunction, "getLegacyCaller", Type.methodType(Types.FunctionObject)); static final MethodName LegacyConstructorFunction_setLegacyCaller = MethodName.findVirtual( Types.LegacyConstructorFunction, "setLegacyCaller", Type.methodType(Type.VOID_TYPE, Types.FunctionObject)); static final MethodName LegacyConstructorFunction_setLegacyArguments = MethodName.findVirtual( Types.LegacyConstructorFunction, "setLegacyArguments", Type.methodType(Type.VOID_TYPE, Types.LegacyConstructorFunction$Arguments)); static final MethodName LegacyConstructorFunction$Arguments_new = MethodName.findConstructor( Types.LegacyConstructorFunction$Arguments, Type.methodType(Type.VOID_TYPE, Types.Object_)); // LexicalEnvironment static final MethodName LexicalEnvironment_newFunctionEnvironment = MethodName.findStatic( Types.LexicalEnvironment, "newFunctionEnvironment", Type.methodType(Types.LexicalEnvironment, Types.FunctionObject, Types.Constructor, Types.Object)); static final MethodName LexicalEnvironment_newFunctionEnvironment_ConstructDerived = MethodName.findStatic( Types.LexicalEnvironment, "newFunctionEnvironment", Type.methodType(Types.LexicalEnvironment, Types.FunctionObject, Types.Constructor)); // OrdinaryAsyncFunction static final MethodName OrdinaryAsyncFunction_EvaluateBody = MethodName.findStatic(Types.OrdinaryAsyncFunction, "EvaluateBody", Type.methodType(Types.PromiseObject, Types.ExecutionContext, Types.OrdinaryAsyncFunction)); // OrdinaryAsyncGenerator static final MethodName OrdinaryAsyncGenerator_EvaluateBody = MethodName.findStatic( Types.OrdinaryAsyncGenerator, "EvaluateBody", Type.methodType(Types.AsyncGeneratorObject, Types.ExecutionContext, Types.OrdinaryAsyncGenerator)); // OrdinaryConstructorGenerator static final MethodName OrdinaryConstructorGenerator_EvaluateBody = MethodName.findStatic( Types.OrdinaryConstructorGenerator, "EvaluateBody", Type.methodType(Types.GeneratorObject, Types.ExecutionContext, Types.OrdinaryConstructorGenerator)); // OrdinaryGenerator static final MethodName OrdinaryGenerator_EvaluateBody = MethodName.findStatic(Types.OrdinaryGenerator, "EvaluateBody", Type.methodType(Types.GeneratorObject, Types.ExecutionContext, Types.OrdinaryGenerator)); // OrdinaryObject static final MethodName OrdinaryObject_OrdinaryCreateFromConstructor = MethodName.findStatic( Types.OrdinaryObject, "OrdinaryCreateFromConstructor", Type.methodType(Types.OrdinaryObject, Types.ExecutionContext, Types.Callable, Types.Intrinsics)); // class: Errors static final MethodName Errors_newTypeError = MethodName.findStatic(Types.Errors, "newTypeError", Type.methodType(Types.ScriptException, Types.ExecutionContext, Types.Messages$Key)); // class: TailCallInvocation static final MethodName TailCallInvocation_toConstructTailCall = MethodName.findVirtual( Types.TailCallInvocation, "toConstructTailCall", Type.methodType(Types.TailCallInvocation, Types.ScriptObject)); static final MethodName TailCallInvocation_toConstructTailCallWithEnvironment = MethodName.findVirtual( Types.TailCallInvocation, "toConstructTailCall", Type.methodType(Types.TailCallInvocation, Types.FunctionEnvironmentRecord)); // class: ScriptRuntime static final MethodName ScriptRuntime_functionThisValue = MethodName.findStatic(Types.ScriptRuntime, "functionThisValue", Type.methodType(Types.ScriptObject, Types.FunctionObject, Types.Object)); // class: PromiseAbstractOperations static final MethodName PromiseAbstractOperations_PromiseOf = MethodName.findStatic( Types.PromiseAbstractOperations, "PromiseOf", Type.methodType(Types.PromiseObject, Types.ExecutionContext, Types.ScriptException)); } private static final int FUNCTION = 0; private static final int EXECUTION_CONTEXT = 1; private static final int THIS_VALUE = 2; private static final int NEW_TARGET = 2; private static final int ARGUMENTS = 3; private static class CallMethodGenerator extends InstructionVisitor { private final String name; private final Type type; CallMethodGenerator(MethodCode method, String name, Type type) { super(method); this.name = name; this.type = type; } @Override public final void begin() { super.begin(); setParameterName(name, FUNCTION, type); setParameterName("callerContext", EXECUTION_CONTEXT, Types.ExecutionContext); setParameterName("thisValue", THIS_VALUE, Types.Object); setParameterName("arguments", ARGUMENTS, Types.Object_); } } private static class ConstructMethodGenerator extends InstructionVisitor { private final String name; private final Type type; ConstructMethodGenerator(MethodCode method, String name, Type type) { super(method); this.name = name; this.type = type; } @Override public final void begin() { super.begin(); setParameterName(name, FUNCTION, type); setParameterName("callerContext", EXECUTION_CONTEXT, Types.ExecutionContext); setParameterName("newTarget", NEW_TARGET, Types.Constructor); setParameterName("arguments", ARGUMENTS, Types.Object_); } } private final CodeGenerator codegen; FunctionCodeGenerator(CodeGenerator codegen) { this.codegen = codegen; } void generate(ClassDefinition node, boolean tailCall, boolean tailConstruct) { MethodDefinition constructor = node.getConstructor(); MethodDefinition callConstructor = node.getCallConstructor(); if (callConstructor == null) { generateCall(constructor); } else { generateClassCall(node); } generateConstruct(constructor, tailConstruct); } void generate(FunctionNode node, boolean tailCall) { generateCall(node); if (node.isConstructor()) { generateConstruct(node, tailCall); } } private void generateCall(FunctionNode node) { MethodCode method = codegen.newMethod(node, FunctionName.Call); InstructionVisitor mv = new CallMethodGenerator(method, targetName(node), targetType(node)); mv.lineInfo(node); mv.begin(); if (node.isAsync() && node.isGenerator()) { generateAsyncGeneratorCall(node, mv); } else if (node.isAsync()) { generateAsyncFunctionCall(node, mv); } else if (node.isGenerator()) { if (node.isConstructor()) { generateConstructorGeneratorCall(node, mv); } else { generateGeneratorCall(node, mv); } } else if (isClassConstructor(node)) { generateClassConstructorCall(mv); } else if (isLegacy(node)) { generateLegacyFunctionCall(node, mv); } else if (node.isConstructor()) { generateFunctionCall(node, OrdinaryConstructorFunction.class, mv); } else { generateFunctionCall(node, OrdinaryFunction.class, mv); } mv.end(); } private void generateConstruct(FunctionNode node, boolean tailCall) { MethodCode method = codegen.newMethod(node, tailCall ? FunctionName.ConstructTailCall : FunctionName.Construct); InstructionVisitor mv = new ConstructMethodGenerator(method, targetName(node), targetType(node)); mv.lineInfo(node); mv.begin(); if (node.isGenerator()) { generateGeneratorConstruct(node, mv); } else if (isDerivedClassConstructor(node)) { generateDerivedClassConstructorConstruct(node, tailCall, mv); } else if (isLegacy(node)) { generateLegacyFunctionConstruct(node, mv); } else { generateFunctionConstruct(node, tailCall, mv); } mv.end(); } private void generateClassCall(ClassDefinition node) { MethodDefinition constructor = node.getConstructor(); MethodDefinition callConstructor = node.getCallConstructor(); MethodCode method = codegen.newMethod(constructor, FunctionName.Call); InstructionVisitor mv = new CallMethodGenerator(method, targetName(constructor), targetType(constructor)); mv.lineInfo(callConstructor); mv.begin(); generateFunctionCall(callConstructor, OrdinaryConstructorFunction.class, mv); mv.end(); } private Type targetType(FunctionNode node) { if (node.isAsync() && node.isGenerator()) { return Types.OrdinaryAsyncGenerator; } else if (node.isGenerator()) { if (node.isConstructor()) { return Types.OrdinaryConstructorGenerator; } return Types.OrdinaryGenerator; } else if (node.isAsync()) { return Types.OrdinaryAsyncFunction; } else if (isLegacy(node)) { return Types.LegacyConstructorFunction; } else if (node.isConstructor()) { return Types.OrdinaryConstructorFunction; } else { return Types.OrdinaryFunction; } } private String targetName(FunctionNode node) { if (node.isGenerator()) { return "generator"; } else { return "function"; } } /** * Generate bytecode for: * * <pre> * oldCaller = function.getLegacyCaller() * oldArguments = function.getLegacyArguments() * function.setLegacyCaller(callerContext.getCurrentFunction()) * try { * calleeContext = newFunctionExecutionContext(function, null, thisValue) * return OrdinaryCallEvaluateBody(function, argumentsList) * } finally { * function.restoreLegacyProperties(oldCaller, oldArguments) * } * </pre> * * @param node * the function node * @param mv * the instruction visitor */ private void generateLegacyFunctionCall(FunctionNode node, InstructionVisitor mv) { final boolean hasArguments = codegen.isEnabled(CompatibilityOption.FunctionArguments); final boolean hasCaller = codegen.isEnabled(CompatibilityOption.FunctionCaller); Variable<LegacyConstructorFunction> function = mv.getParameter(FUNCTION, LegacyConstructorFunction.class); Variable<ExecutionContext> callerContext = mv.getParameter(EXECUTION_CONTEXT, ExecutionContext.class); Variable<Object> thisValue = mv.getParameter(THIS_VALUE, Object.class); Variable<Object[]> arguments = mv.getParameter(ARGUMENTS, Object[].class); Variable<ExecutionContext> calleeContext = mv.newVariable("calleeContext", ExecutionContext.class); Variable<FunctionObject> oldCaller = mv.newVariable("oldCaller", FunctionObject.class); Variable<LegacyConstructorFunction.Arguments> oldArguments = mv.newVariable("oldArguments", LegacyConstructorFunction.Arguments.class); Variable<Throwable> throwable = mv.newVariable("throwable", Throwable.class); // (1) Retrieve 'caller' and 'arguments' and store in local variables if (hasCaller) { mv.load(function); mv.invoke(Methods.LegacyConstructorFunction_getLegacyCaller); } else { mv.anull(); } mv.store(oldCaller); if (hasArguments) { mv.load(function); mv.invoke(Methods.LegacyConstructorFunction_getLegacyArguments); } else { mv.anull(); } mv.store(oldArguments); // (2) Update 'caller' and 'arguments' properties if (hasCaller) { setLegacyCaller(function, callerContext, mv); } if (hasArguments) { setLegacyArguments(function, arguments, mv); } TryCatchLabel startFinally = new TryCatchLabel(), endFinally = new TryCatchLabel(); TryCatchLabel handlerFinally = new TryCatchLabel(); mv.mark(startFinally); { // (3) Create a new ExecutionContext prepareCallAndBindThis(node, calleeContext, function, thisValue, mv); // (4) Call OrdinaryCallEvaluateBody ordinaryCallEvaluateBody(node, calleeContext, function, arguments, mv); // (5) Restore 'caller' and 'arguments' restoreLegacyProperties(function, oldCaller, oldArguments, mv); // (6) Return result value mv._return(); } mv.mark(endFinally); // Exception: Restore 'caller' and 'arguments' and then rethrow exception mv.finallyHandler(handlerFinally); mv.store(throwable); restoreLegacyProperties(function, oldCaller, oldArguments, mv); mv.load(throwable); mv.athrow(); mv.tryFinally(startFinally, endFinally, handlerFinally); } /** * Generate bytecode for: * * <pre> * calleeContext = newFunctionExecutionContext(function, null, thisValue) * result = OrdinaryCallEvaluateBody(function, argumentsList) * return returnResultOrUndefined(result) * </pre> * * @param node * the function node * @param functionClass * the target function class * @param mv * the instruction visitor */ private void generateFunctionCall(FunctionNode node, Class<? extends FunctionObject> functionClass, InstructionVisitor mv) { Variable<? extends FunctionObject> function = mv.getParameter(FUNCTION, functionClass); Variable<Object> thisValue = mv.getParameter(THIS_VALUE, Object.class); Variable<Object[]> arguments = mv.getParameter(ARGUMENTS, Object[].class); Variable<ExecutionContext> calleeContext = mv.newVariable("calleeContext", ExecutionContext.class); // (1) Create a new ExecutionContext /* steps 1-6 */ prepareCallAndBindThis(node, calleeContext, function, thisValue, mv); // (2) Call OrdinaryCallEvaluateBody /* steps 7-8 */ ordinaryCallEvaluateBody(node, calleeContext, function, arguments, mv); // (3) Return result value /* steps 9-11 */ mv._return(); } /** * Generate bytecode for: * * <pre> * throw Errors.newTypeError() * </pre> * * @param mv * the instruction visitor */ private void generateClassConstructorCall(InstructionVisitor mv) { Variable<ExecutionContext> callerContext = mv.getParameter(EXECUTION_CONTEXT, ExecutionContext.class); // 9.2.2 [[Call]] ( thisArgument, argumentsList) - step 2 mv.load(callerContext); mv.get(Fields.MessagesKey_InvalidCallClass); mv.invoke(Methods.Errors_newTypeError); mv.athrow(); } /** * Generate bytecode for: * * <pre> * oldCaller = function.getLegacyCaller() * oldArguments = function.getLegacyArguments() * function.setLegacyCaller(callerContext.getCurrentFunction()) * try { * thisArgument = OrdinaryCreateFromConstructor(callerContext, newTarget, %ObjectPrototype%) * calleeContext = newFunctionExecutionContext(function, newTarget, thisArgument) * result = OrdinaryCallEvaluateBody(function, argumentsList) * return returnResultOrThis(result) * } finally { * function.restoreLegacyProperties(oldCaller, oldArguments) * } * </pre> * * @param node * the function node * @param mv * the instruction visitor */ private void generateLegacyFunctionConstruct(FunctionNode node, InstructionVisitor mv) { final boolean hasArguments = codegen.isEnabled(CompatibilityOption.FunctionArguments); final boolean hasCaller = codegen.isEnabled(CompatibilityOption.FunctionCaller); Variable<LegacyConstructorFunction> function = mv.getParameter(FUNCTION, LegacyConstructorFunction.class); Variable<ExecutionContext> callerContext = mv.getParameter(EXECUTION_CONTEXT, ExecutionContext.class); Variable<Constructor> newTarget = mv.getParameter(NEW_TARGET, Constructor.class); Variable<Object[]> arguments = mv.getParameter(ARGUMENTS, Object[].class); Variable<ScriptObject> thisArg = mv.newVariable("thisArgument", ScriptObject.class); Variable<ExecutionContext> calleeContext = mv.newVariable("calleeContext", ExecutionContext.class); Variable<FunctionObject> oldCaller = mv.newVariable("oldCaller", FunctionObject.class); Variable<LegacyConstructorFunction.Arguments> oldArguments = mv.newVariable("oldArguments", LegacyConstructorFunction.Arguments.class); Variable<Throwable> throwable = mv.newVariable("throwable", Throwable.class); // (1) Retrieve 'caller' and 'arguments' and store in local variables if (hasCaller) { mv.load(function); mv.invoke(Methods.LegacyConstructorFunction_getLegacyCaller); } else { mv.anull(); } mv.store(oldCaller); if (hasArguments) { mv.load(function); mv.invoke(Methods.LegacyConstructorFunction_getLegacyArguments); } else { mv.anull(); } mv.store(oldArguments); // (2) Update 'caller' and 'arguments' properties if (hasCaller) { setLegacyCaller(function, callerContext, mv); } if (hasArguments) { setLegacyArguments(function, arguments, mv); } TryCatchLabel startFinally = new TryCatchLabel(), endFinally = new TryCatchLabel(); TryCatchLabel handlerFinally = new TryCatchLabel(); mv.mark(startFinally); { // (3) Create this-argument ordinaryCreateFromConstructor(callerContext, newTarget, thisArg, mv); // (4) Create a new ExecutionContext prepareCallAndBindThis(node, calleeContext, function, newTarget, thisArg, mv); // (5) Call OrdinaryCallEvaluateBody ordinaryCallEvaluateBody(node, calleeContext, function, arguments, mv); // (6) Restore 'caller' and 'arguments' restoreLegacyProperties(function, oldCaller, oldArguments, mv); // (7) Return result value returnResultOrThis(thisArg, false, mv); } mv.mark(endFinally); // Exception: Restore 'caller' and 'arguments' and then rethrow exception mv.finallyHandler(handlerFinally); mv.store(throwable); restoreLegacyProperties(function, oldCaller, oldArguments, mv); mv.load(throwable); mv.athrow(); mv.tryFinally(startFinally, endFinally, handlerFinally); } /** * Generate bytecode for: * * <pre> * thisArgument = OrdinaryCreateFromConstructor(callerContext, newTarget, %ObjectPrototype%) * calleeContext = newFunctionExecutionContext(function, newTarget, thisArgument) * result = OrdinaryCallEvaluateBody(function, argumentsList) * return returnResultOrThis(result) * </pre> * * @param node * the function node * @param tailCall * {@code true} if the constructor function contains a tail-call * @param mv * the instruction visitor */ private void generateFunctionConstruct(FunctionNode node, boolean tailCall, InstructionVisitor mv) { Variable<OrdinaryConstructorFunction> function = mv.getParameter(FUNCTION, OrdinaryConstructorFunction.class); Variable<ExecutionContext> callerContext = mv.getParameter(EXECUTION_CONTEXT, ExecutionContext.class); Variable<Constructor> newTarget = mv.getParameter(NEW_TARGET, Constructor.class); Variable<Object[]> arguments = mv.getParameter(ARGUMENTS, Object[].class); Variable<ScriptObject> thisArgument = mv.newVariable("thisArgument", ScriptObject.class); Variable<ExecutionContext> calleeContext = mv.newVariable("calleeContext", ExecutionContext.class); /* steps 1-5 */ ordinaryCreateFromConstructor(callerContext, newTarget, thisArgument, mv); // (1) Create a new ExecutionContext /* steps 6-10 */ prepareCallAndBindThis(node, calleeContext, function, newTarget, thisArgument, mv); // (2) Call OrdinaryCallEvaluateBody /* steps 11-12 */ ordinaryCallEvaluateBody(node, calleeContext, function, arguments, mv); // (3) Return result value /* steps 13-15 */ returnResultOrThis(thisArgument, tailCall, mv); } /** * Generate bytecode for: * * <pre> * calleeContext = newFunctionExecutionContext(function, newTarget) * result = OrdinaryCallEvaluateBody(function, argumentsList) * return returnResultOrThis(result) * </pre> * * @param node * the function node * @param tailCall * {@code true} if the constructor function contains a tail-call * @param mv * the instruction visitor */ private void generateDerivedClassConstructorConstruct(FunctionNode node, boolean tailCall, InstructionVisitor mv) { Variable<OrdinaryConstructorFunction> function = mv.getParameter(FUNCTION, OrdinaryConstructorFunction.class); Variable<ExecutionContext> callerContext = mv.getParameter(EXECUTION_CONTEXT, ExecutionContext.class); Variable<Constructor> newTarget = mv.getParameter(NEW_TARGET, Constructor.class); Variable<Object[]> arguments = mv.getParameter(ARGUMENTS, Object[].class); Variable<ExecutionContext> calleeContext = mv.newVariable("calleeContext", ExecutionContext.class); // (1) Create a new ExecutionContext /* steps 1-5 (not applicable) */ /* steps 6-7 */ prepareCall(calleeContext, function, newTarget, mv); /* steps 8-10 (not applicable) */ // (2) Call OrdinaryCallEvaluateBody /* steps 11-12 */ ordinaryCallEvaluateBody(node, calleeContext, function, arguments, mv); // (3) Return result value /* steps 13-15 */ returnResultOrThis(callerContext, calleeContext, tailCall, mv); } /** * Generate bytecode for: * * <pre> * calleeContext = newFunctionExecutionContext(function, null, thisValue) * function_init(calleeContext, function, arguments) * return EvaluateBody(calleeContext, generator) * </pre> * * @param node * the function node * @param mv * the instruction visitor */ private void generateAsyncFunctionCall(FunctionNode node, InstructionVisitor mv) { Variable<OrdinaryAsyncFunction> function = mv.getParameter(FUNCTION, OrdinaryAsyncFunction.class); Variable<Object> thisValue = mv.getParameter(THIS_VALUE, Object.class); Variable<Object[]> arguments = mv.getParameter(ARGUMENTS, Object[].class); Variable<ExecutionContext> calleeContext = mv.newVariable("calleeContext", ExecutionContext.class); // (1) Create a new ExecutionContext prepareCallAndBindThis(node, calleeContext, function, thisValue, mv); // (2) Perform FunctionDeclarationInstantiation { TryCatchLabel startCatch = new TryCatchLabel(); TryCatchLabel endCatch = new TryCatchLabel(), handlerCatch = new TryCatchLabel(); Jump noException = new Jump(); mv.mark(startCatch); functionDeclarationInstantiation(node, calleeContext, function, arguments, mv); mv.goTo(noException); mv.mark(endCatch); mv.catchHandler(handlerCatch, Types.ScriptException); { // stack: [exception] -> [cx, exception] mv.load(calleeContext); mv.swap(); // stack: [cx, exception] -> [promise] mv.invoke(Methods.PromiseAbstractOperations_PromiseOf); mv._return(); } mv.mark(noException); mv.tryCatch(startCatch, endCatch, handlerCatch, Types.ScriptException); } // (3) Perform EvaluateBody mv.load(calleeContext); mv.load(function); mv.invoke(Methods.OrdinaryAsyncFunction_EvaluateBody); // (4) Return result value mv._return(); } /** * Generate bytecode for: * * <pre> * calleeContext = newFunctionExecutionContext(generator, null, thisValue) * function_init(calleeContext, generator, arguments) * return EvaluateBody(calleeContext, generator) * </pre> * * @param node * the function node * @param mv * the instruction visitor */ private void generateAsyncGeneratorCall(FunctionNode node, InstructionVisitor mv) { Variable<OrdinaryAsyncGenerator> generator = mv.getParameter(FUNCTION, OrdinaryAsyncGenerator.class); Variable<Object> thisValue = mv.getParameter(THIS_VALUE, Object.class); Variable<Object[]> arguments = mv.getParameter(ARGUMENTS, Object[].class); Variable<ExecutionContext> calleeContext = mv.newVariable("calleeContext", ExecutionContext.class); // (1) Create a new ExecutionContext prepareCallAndBindThis(node, calleeContext, generator, thisValue, mv); // (2) Perform OrdinaryCallEvaluateBody - FunctionDeclarationInstantiation functionDeclarationInstantiation(node, calleeContext, generator, arguments, mv); // (3) Perform OrdinaryCallEvaluateBody - EvaluateBody mv.load(calleeContext); mv.load(generator); mv.invoke(Methods.OrdinaryAsyncGenerator_EvaluateBody); // (4) Return result value mv._return(); } /** * Generate bytecode for: * * <pre> * calleeContext = newFunctionExecutionContext(generator, null, thisValue) * function_init(calleeContext, generator, arguments) * return EvaluateBody(calleeContext, generator) * </pre> * * @param node * the function node * @param mv * the instruction visitor */ private void generateConstructorGeneratorCall(FunctionNode node, InstructionVisitor mv) { Variable<OrdinaryConstructorGenerator> generator = mv.getParameter(FUNCTION, OrdinaryConstructorGenerator.class); Variable<Object> thisValue = mv.getParameter(THIS_VALUE, Object.class); Variable<Object[]> arguments = mv.getParameter(ARGUMENTS, Object[].class); Variable<ExecutionContext> calleeContext = mv.newVariable("calleeContext", ExecutionContext.class); // (1) Create a new ExecutionContext prepareCallAndBindThis(node, calleeContext, generator, thisValue, mv); // (2) Perform OrdinaryCallEvaluateBody - FunctionDeclarationInstantiation functionDeclarationInstantiation(node, calleeContext, generator, arguments, mv); // (3) Perform OrdinaryCallEvaluateBody - EvaluateBody mv.load(calleeContext); mv.load(generator); mv.invoke(Methods.OrdinaryConstructorGenerator_EvaluateBody); // (4) Return result value mv._return(); } /** * Generate bytecode for: * * <pre> * calleeContext = newFunctionExecutionContext(generator, null, thisValue) * function_init(calleeContext, generator, arguments) * return EvaluateBody(calleeContext, generator) * </pre> * * @param node * the function node * @param mv * the instruction visitor */ private void generateGeneratorCall(FunctionNode node, InstructionVisitor mv) { Variable<OrdinaryGenerator> generator = mv.getParameter(FUNCTION, OrdinaryGenerator.class); Variable<Object> thisValue = mv.getParameter(THIS_VALUE, Object.class); Variable<Object[]> arguments = mv.getParameter(ARGUMENTS, Object[].class); Variable<ExecutionContext> calleeContext = mv.newVariable("calleeContext", ExecutionContext.class); // (1) Create a new ExecutionContext prepareCallAndBindThis(node, calleeContext, generator, thisValue, mv); // (2) Perform OrdinaryCallEvaluateBody - FunctionDeclarationInstantiation functionDeclarationInstantiation(node, calleeContext, generator, arguments, mv); // (3) Perform OrdinaryCallEvaluateBody - EvaluateBody mv.load(calleeContext); mv.load(generator); mv.invoke(Methods.OrdinaryGenerator_EvaluateBody); // (4) Return result value mv._return(); } /** * Generate bytecode for: * * <pre> * calleeContext = newFunctionExecutionContext(generator, newTarget) * function_init(calleeContext, generator, arguments) * generatorObject = EvaluateBody(calleeContext, generator) * BindThisValue(calleeContext, generatorObject) * return generatorObject * </pre> * * @param node * the function node * @param mv * the instruction visitor */ private void generateGeneratorConstruct(FunctionNode node, InstructionVisitor mv) { Variable<OrdinaryConstructorGenerator> generator = mv.getParameter(FUNCTION, OrdinaryConstructorGenerator.class); Variable<Constructor> newTarget = mv.getParameter(NEW_TARGET, Constructor.class); Variable<Object[]> arguments = mv.getParameter(ARGUMENTS, Object[].class); Variable<ExecutionContext> calleeContext = mv.newVariable("calleeContext", ExecutionContext.class); // 9.2.4 FunctionAllocate - Generator functions are always derived constructor kinds. // (1) Create a new ExecutionContext prepareCall(calleeContext, generator, newTarget, mv); // (2) Perform OrdinaryCallEvaluateBody - FunctionDeclarationInstantiation functionDeclarationInstantiation(node, calleeContext, generator, arguments, mv); // (3) Perform OrdinaryCallEvaluateBody - EvaluateBody mv.load(calleeContext); mv.load(generator); mv.invoke(Methods.OrdinaryConstructorGenerator_EvaluateBody); // (4) Return result value mv._return(); } /** * 9.2.1.1 PrepareForOrdinaryCall( F, newTarget )<br> * 9.2.1.2 OrdinaryCallBindThis ( F, calleeContext, thisArgument ) * * <pre> * funEnv = newFunctionEnvironment(function, newTarget, thisValue) * calleeContext = newFunctionExecutionContext(function, funEnv) * </pre> * * @param node * the function node * @param calleeContext * the variable which holds the callee context * @param function * the variable which holds the function object * @param thisArgument * the variable which holds the thisArgument * @param mv * the instruction visitor */ private void prepareCallAndBindThis(FunctionNode node, Variable<ExecutionContext> calleeContext, Variable<? extends FunctionObject> function, Variable<? extends Object> thisArgument, InstructionVisitor mv) { mv.load(function); { // Create new function environment. mv.load(function); mv.anull(); ordinaryCallBindThis(node, function, thisArgument, mv); mv.invoke(Methods.LexicalEnvironment_newFunctionEnvironment); } mv.invoke(Methods.ExecutionContext_newFunctionExecutionContext); mv.store(calleeContext); } /** * 9.2.1.1 PrepareForOrdinaryCall( F, newTarget )<br> * 9.2.1.2 OrdinaryCallBindThis ( F, calleeContext, thisArgument ) * * <pre> * funEnv = newFunctionEnvironment(function, newTarget, thisValue) * calleeContext = newFunctionExecutionContext(function, funEnv) * </pre> * * @param node * the function node * @param calleeContext * the variable which holds the callee context * @param function * the variable which holds the function object * @param newTarget * the variable which holds the newTarget * @param thisArgument * the variable which holds the thisArgument * @param mv * the instruction visitor */ private void prepareCallAndBindThis(FunctionNode node, Variable<ExecutionContext> calleeContext, Variable<? extends FunctionObject> function, Variable<Constructor> newTarget, Variable<ScriptObject> thisArgument, InstructionVisitor mv) { mv.load(function); { // Create new function environment. mv.load(function); mv.load(newTarget); mv.load(thisArgument); mv.invoke(Methods.LexicalEnvironment_newFunctionEnvironment); } mv.invoke(Methods.ExecutionContext_newFunctionExecutionContext); mv.store(calleeContext); } /** * 9.2.1.1 PrepareForOrdinaryCall( F, newTarget ) * * <pre> * funEnv = newFunctionEnvironment(function, newTarget) * calleeContext = newFunctionExecutionContext(function, funEnv) * </pre> * * @param calleeContext * the variable which holds the callee context * @param function * the variable which holds the function object * @param newTarget * the variable which holds the newTarget constructor * @param mv * the instruction visitor */ private void prepareCall(Variable<ExecutionContext> calleeContext, Variable<? extends FunctionObject> function, Variable<Constructor> newTarget, InstructionVisitor mv) { mv.load(function); { // Create new function environment. mv.load(function); mv.load(newTarget); mv.invoke(Methods.LexicalEnvironment_newFunctionEnvironment_ConstructDerived); } mv.invoke(Methods.ExecutionContext_newFunctionExecutionContext); mv.store(calleeContext); } /** * 9.2.1.2 OrdinaryCallBindThis ( F, calleeContext, thisArgument ) * * @param node * the function node * @param function * the variable which holds the function object * @param thisArgument * the variable which holds the thisArgument * @param mv * the instruction visitor */ private void ordinaryCallBindThis(FunctionNode node, Variable<? extends FunctionObject> function, Variable<? extends Object> thisArgument, InstructionVisitor mv) { /* step 1 */ FunctionNode.ThisMode thisMode = node.getThisMode(); /* step 2 */ if (thisMode == ThisMode.Lexical) { mv.anull(); return; } /* steps 3-4 (not applicable) */ /* steps 5-6 */ if (thisMode == ThisMode.Strict) { /* step 5 */ mv.load(thisArgument); } else { /* step 6 */ mv.load(function); mv.load(thisArgument); mv.lineInfo(node); mv.invoke(Methods.ScriptRuntime_functionThisValue); } /* steps 7-9 (not applicable) */ } /** * 9.2.1.3 OrdinaryCallEvaluateBody ( F, argumentsList ) * * @param node * the function node * @param calleeContext * the variable which holds the callee context * @param function * the variable which holds the function object * @param arguments * the variable which holds the function arguments * @param mv * the instruction visitor */ private void ordinaryCallEvaluateBody(FunctionNode node, Variable<ExecutionContext> calleeContext, Variable<? extends FunctionObject> function, Variable<Object[]> arguments, InstructionVisitor mv) { /* steps 1-2 (Perform FunctionDeclarationInstantiation) */ functionDeclarationInstantiation(node, calleeContext, function, arguments, mv); /* step 3 (Perform EvaluateBody) */ evaluateBody(node, calleeContext, mv); } /** * <code> * function_init(calleeContext, function, arguments) * </code> * * @param node * the function node * @param calleeContext * the variable which holds the callee context * @param function * the variable which holds the function object * @param arguments * the variable which holds the function arguments * @param mv * the instruction visitor */ private void functionDeclarationInstantiation(FunctionNode node, Variable<ExecutionContext> calleeContext, Variable<? extends FunctionObject> function, Variable<Object[]> arguments, InstructionVisitor mv) { mv.load(calleeContext); mv.load(function); mv.load(arguments); mv.invoke(codegen.methodDesc(node, FunctionName.Init)); } /** * <code> * function_code(calleeContext) * </code> * * @param node * the function node * @param calleeContext * the variable which holds the callee context * @param mv * the instruction visitor */ private void evaluateBody(FunctionNode node, Variable<ExecutionContext> calleeContext, InstructionVisitor mv) { mv.load(calleeContext); mv.invoke(codegen.methodDesc(node, FunctionName.Code)); } /** * <code> * function.setLegacyCaller(callerContext.getCurrentFunction()) * </code> * * @param function * the variable which holds the function object * @param callerContext * the variable which holds the caller context * @param mv * the instruction visitor */ private void setLegacyCaller(Variable<? extends FunctionObject> function, Variable<ExecutionContext> callerContext, InstructionVisitor mv) { mv.load(function); mv.load(callerContext); mv.invoke(Methods.ExecutionContext_getCurrentFunction); mv.invoke(Methods.LegacyConstructorFunction_setLegacyCaller); } /** * <code> * function.setLegacyCaller(callerContext.getCurrentFunction()) * </code> * * @param function * the variable which holds the function object * @param arguments * the variable which holds the function arguments * @param mv * the instruction visitor */ private void setLegacyArguments(Variable<? extends FunctionObject> function, Variable<Object[]> arguments, InstructionVisitor mv) { mv.load(function); mv.anew(Types.LegacyConstructorFunction$Arguments, Methods.LegacyConstructorFunction$Arguments_new, arguments); mv.invoke(Methods.LegacyConstructorFunction_setLegacyArguments); } /** * <code> * function.setLegacyCaller(oldCaller) * function.setLegacyArguments(oldArguments) * </code> * * @param function * the variable which holds the function object * @param oldCaller * the variable which holds the old caller * @param oldArguments * the variable which holds the old arguments * @param mv * the instruction visitor */ private void restoreLegacyProperties(Variable<? extends FunctionObject> function, Variable<FunctionObject> oldCaller, Variable<LegacyConstructorFunction.Arguments> oldArguments, InstructionVisitor mv) { mv.load(function); mv.load(oldCaller); mv.invoke(Methods.LegacyConstructorFunction_setLegacyCaller); mv.load(function); mv.load(oldArguments); mv.invoke(Methods.LegacyConstructorFunction_setLegacyArguments); } /** * <code> * thisArgument = OrdinaryCreateFromConstructor(callerContext, newTarget, %ObjectPrototype%) * </code> * * @param callerContext * the variable which holds the caller context * @param newTarget * the variable which holds the newTarget constructor * @param thisArgument * the variable which holds the thisArgument * @param mv * the instruction visitor */ private void ordinaryCreateFromConstructor(Variable<ExecutionContext> callerContext, Variable<Constructor> newTarget, Variable<ScriptObject> thisArgument, InstructionVisitor mv) { mv.load(callerContext); mv.load(newTarget); mv.get(Fields.Intrinsics_ObjectPrototype); mv.invoke(Methods.OrdinaryObject_OrdinaryCreateFromConstructor); mv.store(thisArgument); } /** * Generate bytecode for: * * <pre> * if (tailCall && result instanceof TailCallInvocation) { * return ((TailCallInvocation) result).toConstructTailCall(thisArgument); * } * if (Type.isObject(result)) { * return Type.objectValue(result); * } * return thisArgument; * </pre> * * @param thisArgument * the variable which holds the thisArgument * @param tailCall * {@code true} if the constructor function contains a tail-call * @param mv * the instruction visitor */ private void returnResultOrThis(Variable<ScriptObject> thisArgument, boolean tailCall, InstructionVisitor mv) { if (tailCall) { Jump noTailCall = new Jump(); mv.dup(); mv.instanceOf(Types.TailCallInvocation); mv.ifeq(noTailCall); { mv.checkcast(Types.TailCallInvocation); mv.load(thisArgument); mv.invoke(Methods.TailCallInvocation_toConstructTailCall); mv._return(); } mv.mark(noTailCall); } Jump noResult = new Jump(); mv.dup(); mv.instanceOf(Types.ScriptObject); mv.ifeq(noResult); { mv.checkcast(Types.ScriptObject); mv._return(); } mv.mark(noResult); mv.pop(); mv.load(thisArgument); mv._return(); } /** * Generate bytecode for: * * <pre> * if (tailCall && result instanceof TailCallInvocation) { * return ((TailCallInvocation) result).toConstructTailCall(envRec); * } * if (Type.isObject(result)) { * return Type.objectValue(result); * } * if (!Type.isUndefined(result)) { * throw Errors.newTypeError(); * } * return envRec.getThisBinding(callerContext); * </pre> * * @param callerContext * the variable which holds the caller context * @param calleeContext * the variable which holds the callee context * @param tailCall * {@code true} if the constructor function contains a tail-call * @param mv * the instruction visitor */ private void returnResultOrThis(Variable<ExecutionContext> callerContext, Variable<ExecutionContext> calleeContext, boolean tailCall, InstructionVisitor mv) { if (tailCall) { Jump noTailCall = new Jump(); mv.dup(); mv.instanceOf(Types.TailCallInvocation); mv.ifeq(noTailCall); { mv.checkcast(Types.TailCallInvocation); mv.load(calleeContext); mv.invoke(Methods.ExecutionContext_getFunctionVariableEnvironmentRecord); mv.invoke(Methods.TailCallInvocation_toConstructTailCallWithEnvironment); mv._return(); } mv.mark(noTailCall); } Jump notObject = new Jump(); mv.dup(); mv.instanceOf(Types.ScriptObject); mv.ifeq(notObject); { mv.checkcast(Types.ScriptObject); mv._return(); } mv.mark(notObject); Jump notUndefined = new Jump(); mv.dup(); mv.loadUndefined(); mv.ifacmpeq(notUndefined); { mv.load(callerContext); mv.get(Fields.MessagesKey_NotObjectTypeFromConstructor); mv.invoke(Methods.Errors_newTypeError); mv.athrow(); } mv.mark(notUndefined); mv.pop(); mv.load(calleeContext); mv.invoke(Methods.ExecutionContext_getFunctionVariableEnvironmentRecord); mv.load(callerContext); mv.invoke(Methods.FunctionEnvironmentRecord_getThisBinding); // If the this-binding is present it's a ScriptObject; if it's not present calling // getThisBinding() will result in a ReferenceError being thrown. So emitting a // checkcast instruction is safe here. mv.checkcast(Types.ScriptObject); mv._return(); } private boolean isLegacy(FunctionNode node) { if (IsStrict(node)) { return false; } if (!(node instanceof FunctionDeclaration || node instanceof FunctionExpression)) { return false; } return codegen.isEnabled(CompatibilityOption.FunctionArguments) || codegen.isEnabled(CompatibilityOption.FunctionCaller); } private boolean isClassConstructor(FunctionNode node) { if (node instanceof MethodDefinition) { return ((MethodDefinition) node).isClassConstructor(); } return false; } private boolean isDerivedClassConstructor(FunctionNode node) { if (node instanceof MethodDefinition) { return ((MethodDefinition) node).getType() == MethodDefinition.MethodType.DerivedConstructor; } return false; } }