/* * Copyright (C) 2008-2009, 2012-2016 Apple Inc. All rights reserved. * Copyright (C) 2008 Cameron Zwarich * Copyright (C) 2012 Igalia, S.L. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "BytecodeGenerator.h" #include "BuiltinExecutables.h" #include "BytecodeLivenessAnalysis.h" #include "Interpreter.h" #include "JSFunction.h" #include "JSGeneratorFunction.h" #include "JSLexicalEnvironment.h" #include "JSTemplateRegistryKey.h" #include "LowLevelInterpreter.h" #include "JSCInlines.h" #include "Options.h" #include "StackAlignment.h" #include "StrongInlines.h" #include "UnlinkedCodeBlock.h" #include "UnlinkedInstructionStream.h" #include #include using namespace std; namespace JSC { void Label::setLocation(unsigned location) { m_location = location; unsigned size = m_unresolvedJumps.size(); for (unsigned i = 0; i < size; ++i) m_generator.instructions()[m_unresolvedJumps[i].second].u.operand = m_location - m_unresolvedJumps[i].first; } ParserError BytecodeGenerator::generate() { m_codeBlock->setThisRegister(m_thisRegister.virtualRegister()); emitLogShadowChickenPrologueIfNecessary(); // If we have declared a variable named "arguments" and we are using arguments then we should // perform that assignment now. if (m_needToInitializeArguments) initializeVariable(variable(propertyNames().arguments), m_argumentsRegister); if (m_restParameter) m_restParameter->emit(*this); { RefPtr temp = newTemporary(); RefPtr globalScope; for (auto functionPair : m_functionsToInitialize) { FunctionMetadataNode* metadata = functionPair.first; FunctionVariableType functionType = functionPair.second; emitNewFunction(temp.get(), metadata); if (functionType == NormalFunctionVariable) initializeVariable(variable(metadata->ident()), temp.get()); else if (functionType == GlobalFunctionVariable) { if (!globalScope) { // We know this will resolve to the global object because our parser/global initialization code // doesn't allow let/const/class variables to have the same names as functions. RefPtr globalObjectScope = emitResolveScope(nullptr, Variable(metadata->ident())); globalScope = newBlockScopeVariable(); emitMove(globalScope.get(), globalObjectScope.get()); } emitPutToScope(globalScope.get(), Variable(metadata->ident()), temp.get(), ThrowIfNotFound, InitializationMode::NotInitialization); } else RELEASE_ASSERT_NOT_REACHED(); } } bool callingClassConstructor = constructorKind() != ConstructorKind::None && !isConstructor(); if (!callingClassConstructor) m_scopeNode->emitBytecode(*this); m_staticPropertyAnalyzer.kill(); for (unsigned i = 0; i < m_tryRanges.size(); ++i) { TryRange& range = m_tryRanges[i]; int start = range.start->bind(); int end = range.end->bind(); // This will happen for empty try blocks and for some cases of finally blocks: // // try { // try { // } finally { // return 42; // // *HERE* // } // } finally { // print("things"); // } // // The return will pop scopes to execute the outer finally block. But this includes // popping the try context for the inner try. The try context is live in the fall-through // part of the finally block not because we will emit a handler that overlaps the finally, // but because we haven't yet had a chance to plant the catch target. Then when we finish // emitting code for the outer finally block, we repush the try contex, this time with a // new start index. But that means that the start index for the try range corresponding // to the inner-finally-following-the-return (marked as "*HERE*" above) will be greater // than the end index of the try block. This is harmless since end < start handlers will // never get matched in our logic, but we do the runtime a favor and choose to not emit // such handlers at all. if (end <= start) continue; ASSERT(range.tryData->handlerType != HandlerType::Illegal); UnlinkedHandlerInfo info(static_cast(start), static_cast(end), static_cast(range.tryData->target->bind()), range.tryData->handlerType); m_codeBlock->addExceptionHandler(info); } m_codeBlock->setInstructions(std::make_unique(m_instructions)); m_codeBlock->shrinkToFit(); if (m_expressionTooDeep) return ParserError(ParserError::OutOfMemory); return ParserError(ParserError::ErrorNone); } BytecodeGenerator::BytecodeGenerator(VM& vm, ProgramNode* programNode, UnlinkedProgramCodeBlock* codeBlock, DebuggerMode debuggerMode, const VariableEnvironment* parentScopeTDZVariables) : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn) , m_scopeNode(programNode) , m_codeBlock(vm, codeBlock) , m_thisRegister(CallFrame::thisArgumentOffset()) , m_codeType(GlobalCode) , m_vm(&vm) , m_needsToUpdateArrowFunctionContext(programNode->usesArrowFunction() || programNode->usesEval()) { ASSERT_UNUSED(parentScopeTDZVariables, !parentScopeTDZVariables->size()); for (auto& constantRegister : m_linkTimeConstantRegisters) constantRegister = nullptr; allocateCalleeSaveSpace(); m_codeBlock->setNumParameters(1); // Allocate space for "this" emitEnter(); allocateAndEmitScope(); const FunctionStack& functionStack = programNode->functionStack(); for (size_t i = 0; i < functionStack.size(); ++i) { FunctionMetadataNode* function = functionStack[i]; m_functionsToInitialize.append(std::make_pair(function, GlobalFunctionVariable)); } if (Options::validateBytecode()) { for (auto& entry : programNode->varDeclarations()) RELEASE_ASSERT(entry.value.isVar()); } codeBlock->setVariableDeclarations(programNode->varDeclarations()); codeBlock->setLexicalDeclarations(programNode->lexicalVariables()); // Even though this program may have lexical variables that go under TDZ, when linking the get_from_scope/put_to_scope // operations we emit we will have ResolveTypes that implictly do TDZ checks. Therefore, we don't need // additional TDZ checks on top of those. This is why we can omit pushing programNode->lexicalVariables() // to the TDZ stack. if (needsToUpdateArrowFunctionContext()) { initializeArrowFunctionContextScopeIfNeeded(); emitPutThisToArrowFunctionContextScope(); } } BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, UnlinkedFunctionCodeBlock* codeBlock, DebuggerMode debuggerMode, const VariableEnvironment* parentScopeTDZVariables) : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn) , m_scopeNode(functionNode) , m_codeBlock(vm, codeBlock) , m_codeType(FunctionCode) , m_vm(&vm) , m_isBuiltinFunction(codeBlock->isBuiltinFunction()) , m_usesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode()) // FIXME: We should be able to have tail call elimination with the profiler // enabled. This is currently not possible because the profiler expects // op_will_call / op_did_call pairs before and after a call, which are not // compatible with tail calls (we have no way of emitting op_did_call). // https://bugs.webkit.org/show_bug.cgi?id=148819 , m_inTailPosition(Options::useTailCalls() && !isConstructor() && constructorKind() == ConstructorKind::None && isStrictMode()) , m_needsToUpdateArrowFunctionContext(functionNode->usesArrowFunction() || functionNode->usesEval()) , m_derivedContextType(codeBlock->derivedContextType()) { for (auto& constantRegister : m_linkTimeConstantRegisters) constantRegister = nullptr; if (m_isBuiltinFunction) m_shouldEmitDebugHooks = false; allocateCalleeSaveSpace(); SymbolTable* functionSymbolTable = SymbolTable::create(*m_vm); functionSymbolTable->setUsesNonStrictEval(m_usesNonStrictEval); int symbolTableConstantIndex = 0; FunctionParameters& parameters = *functionNode->parameters(); // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-functiondeclarationinstantiation // This implements IsSimpleParameterList in the Ecma 2015 spec. // If IsSimpleParameterList is false, we will create a strict-mode like arguments object. // IsSimpleParameterList is false if the argument list contains any default parameter values, // a rest parameter, or any destructuring patterns. bool isSimpleParameterList = true; // If we do have default parameters, destructuring parameters, or a rest parameter, our parameters will be allocated in a different scope. for (size_t i = 0; i < parameters.size(); i++) { std::pair parameter = parameters.at(i); bool hasDefaultParameterValue = !!parameter.second; auto pattern = parameter.first; bool isSimpleParameter = !hasDefaultParameterValue && pattern->isBindingNode(); isSimpleParameterList &= isSimpleParameter; } SourceParseMode parseMode = codeBlock->parseMode(); bool containsArrowOrEvalButNotInArrowBlock = ((functionNode->usesArrowFunction() && functionNode->doAnyInnerArrowFunctionsUseAnyFeature()) || functionNode->usesEval()) && !m_codeBlock->isArrowFunction(); bool shouldCaptureSomeOfTheThings = m_shouldEmitDebugHooks || functionNode->needsActivation() || containsArrowOrEvalButNotInArrowBlock; bool shouldCaptureAllOfTheThings = m_shouldEmitDebugHooks || codeBlock->usesEval(); bool needsArguments = (functionNode->usesArguments() || codeBlock->usesEval() || (functionNode->usesArrowFunction() && !codeBlock->isArrowFunction() && isArgumentsUsedInInnerArrowFunction())); // Generator never provides "arguments". "arguments" reference will be resolved in an upper generator function scope. if (parseMode == SourceParseMode::GeneratorBodyMode) needsArguments = false; if (parseMode == SourceParseMode::GeneratorWrapperFunctionMode && needsArguments) { // Generator does not provide "arguments". Instead, wrapping GeneratorFunction provides "arguments". // This is because arguments of a generator should be evaluated before starting it. // To workaround it, we evaluate these arguments as arguments of a wrapping generator function, and reference it from a generator. // // function *gen(a, b = hello()) // { // return { // @generatorNext: function (@generator, @generatorState, @generatorValue, @generatorResumeMode) // { // arguments; // This `arguments` should reference to the gen's arguments. // ... // } // } // } shouldCaptureSomeOfTheThings = true; } if (shouldCaptureAllOfTheThings) functionNode->varDeclarations().markAllVariablesAsCaptured(); auto captures = [&] (UniquedStringImpl* uid) -> bool { if (!shouldCaptureSomeOfTheThings) return false; if (needsArguments && uid == propertyNames().arguments.impl()) { // Actually, we only need to capture the arguments object when we "need full activation" // because of name scopes. But historically we did it this way, so for now we just preserve // the old behavior. // FIXME: https://bugs.webkit.org/show_bug.cgi?id=143072 return true; } return functionNode->captures(uid); }; auto varKind = [&] (UniquedStringImpl* uid) -> VarKind { return captures(uid) ? VarKind::Scope : VarKind::Stack; }; emitEnter(); allocateAndEmitScope(); m_calleeRegister.setIndex(JSStack::Callee); initializeParameters(parameters); ASSERT(!(isSimpleParameterList && m_restParameter)); // Before emitting a scope creation, emit a generator prologue that contains jump based on a generator's state. if (parseMode == SourceParseMode::GeneratorBodyMode) { m_generatorRegister = &m_parameters[1]; // Jump with switch_imm based on @generatorState. We don't take the coroutine styled generator implementation. // When calling `next()`, we would like to enter the same prologue instead of jumping based on the saved instruction pointer. // It's suitale for inlining, because it just inlines one `next` function implementation. beginGenerator(generatorStateRegister()); // Initial state. emitGeneratorStateLabel(); } if (functionNameIsInScope(functionNode->ident(), functionNode->functionMode())) { ASSERT(parseMode != SourceParseMode::GeneratorBodyMode); bool isDynamicScope = functionNameScopeIsDynamic(codeBlock->usesEval(), codeBlock->isStrictMode()); bool isFunctionNameCaptured = captures(functionNode->ident().impl()); bool markAsCaptured = isDynamicScope || isFunctionNameCaptured; emitPushFunctionNameScope(functionNode->ident(), &m_calleeRegister, markAsCaptured); } if (shouldCaptureSomeOfTheThings) m_lexicalEnvironmentRegister = addVar(); if (shouldCaptureSomeOfTheThings || vm.typeProfiler()) symbolTableConstantIndex = addConstantValue(functionSymbolTable)->index(); // We can allocate the "var" environment if we don't have default parameter expressions. If we have // default parameter expressions, we have to hold off on allocating the "var" environment because // the parent scope of the "var" environment is the parameter environment. if (isSimpleParameterList) initializeVarLexicalEnvironment(symbolTableConstantIndex, functionSymbolTable, shouldCaptureSomeOfTheThings); // Figure out some interesting facts about our arguments. bool capturesAnyArgumentByName = false; if (functionNode->hasCapturedVariables()) { FunctionParameters& parameters = *functionNode->parameters(); for (size_t i = 0; i < parameters.size(); ++i) { auto pattern = parameters.at(i).first; if (!pattern->isBindingNode()) continue; const Identifier& ident = static_cast(pattern)->boundProperty(); capturesAnyArgumentByName |= captures(ident.impl()); } } if (capturesAnyArgumentByName) ASSERT(m_lexicalEnvironmentRegister); // Need to know what our functions are called. Parameters have some goofy behaviors when it // comes to functions of the same name. for (FunctionMetadataNode* function : functionNode->functionStack()) m_functions.add(function->ident().impl()); if (needsArguments) { // Create the arguments object now. We may put the arguments object into the activation if // it is captured. Either way, we create two arguments object variables: one is our // private variable that is immutable, and another that is the user-visible variable. The // immutable one is only used here, or during formal parameter resolutions if we opt for // DirectArguments. m_argumentsRegister = addVar(); m_argumentsRegister->ref(); } if (needsArguments && !codeBlock->isStrictMode() && isSimpleParameterList) { // If we captured any formal parameter by name, then we use ScopedArguments. Otherwise we // use DirectArguments. With ScopedArguments, we lift all of our arguments into the // activation. if (capturesAnyArgumentByName) { functionSymbolTable->setArgumentsLength(vm, parameters.size()); // For each parameter, we have two possibilities: // Either it's a binding node with no function overlap, in which case it gets a name // in the symbol table - or it just gets space reserved in the symbol table. Either // way we lift the value into the scope. for (unsigned i = 0; i < parameters.size(); ++i) { ScopeOffset offset = functionSymbolTable->takeNextScopeOffset(NoLockingNecessary); functionSymbolTable->setArgumentOffset(vm, i, offset); if (UniquedStringImpl* name = visibleNameForParameter(parameters.at(i).first)) { VarOffset varOffset(offset); SymbolTableEntry entry(varOffset); // Stores to these variables via the ScopedArguments object will not do // notifyWrite(), since that would be cumbersome. Also, watching formal // parameters when "arguments" is in play is unlikely to be super profitable. // So, we just disable it. entry.disableWatching(); functionSymbolTable->set(NoLockingNecessary, name, entry); } emitOpcode(op_put_to_scope); instructions().append(m_lexicalEnvironmentRegister->index()); instructions().append(UINT_MAX); instructions().append(virtualRegisterForArgument(1 + i).offset()); instructions().append(GetPutInfo(ThrowIfNotFound, LocalClosureVar, InitializationMode::NotInitialization).operand()); instructions().append(symbolTableConstantIndex); instructions().append(offset.offset()); } // This creates a scoped arguments object and copies the overflow arguments into the // scope. It's the equivalent of calling ScopedArguments::createByCopying(). emitOpcode(op_create_scoped_arguments); instructions().append(m_argumentsRegister->index()); instructions().append(m_lexicalEnvironmentRegister->index()); } else { // We're going to put all parameters into the DirectArguments object. First ensure // that the symbol table knows that this is happening. for (unsigned i = 0; i < parameters.size(); ++i) { if (UniquedStringImpl* name = visibleNameForParameter(parameters.at(i).first)) functionSymbolTable->set(NoLockingNecessary, name, SymbolTableEntry(VarOffset(DirectArgumentsOffset(i)))); } emitOpcode(op_create_direct_arguments); instructions().append(m_argumentsRegister->index()); } } else if (isSimpleParameterList) { // Create the formal parameters the normal way. Any of them could be captured, or not. If // captured, lift them into the scope. We cannot do this if we have default parameter expressions // because when default parameter expressions exist, they belong in their own lexical environment // separate from the "var" lexical environment. for (unsigned i = 0; i < parameters.size(); ++i) { UniquedStringImpl* name = visibleNameForParameter(parameters.at(i).first); if (!name) continue; if (!captures(name)) { // This is the easy case - just tell the symbol table about the argument. It will // be accessed directly. functionSymbolTable->set(NoLockingNecessary, name, SymbolTableEntry(VarOffset(virtualRegisterForArgument(1 + i)))); continue; } ScopeOffset offset = functionSymbolTable->takeNextScopeOffset(NoLockingNecessary); const Identifier& ident = static_cast(parameters.at(i).first)->boundProperty(); functionSymbolTable->set(NoLockingNecessary, name, SymbolTableEntry(VarOffset(offset))); emitOpcode(op_put_to_scope); instructions().append(m_lexicalEnvironmentRegister->index()); instructions().append(addConstant(ident)); instructions().append(virtualRegisterForArgument(1 + i).offset()); instructions().append(GetPutInfo(ThrowIfNotFound, LocalClosureVar, InitializationMode::NotInitialization).operand()); instructions().append(symbolTableConstantIndex); instructions().append(offset.offset()); } } if (needsArguments && (codeBlock->isStrictMode() || !isSimpleParameterList)) { // Allocate a cloned arguments object. emitOpcode(op_create_cloned_arguments); instructions().append(m_argumentsRegister->index()); } // There are some variables that need to be preinitialized to something other than Undefined: // // - "arguments": unless it's used as a function or parameter, this should refer to the // arguments object. // // - functions: these always override everything else. // // The most logical way to do all of this is to initialize none of the variables until now, // and then initialize them in BytecodeGenerator::generate() in such an order that the rules // for how these things override each other end up holding. We would initialize "arguments" first, // then all arguments, then the functions. // // But some arguments are already initialized by default, since if they aren't captured and we // don't have "arguments" then we just point the symbol table at the stack slot of those // arguments. We end up initializing the rest of the arguments that have an uncomplicated // binding (i.e. don't involve destructuring) above when figuring out how to lay them out, // because that's just the simplest thing. This means that when we initialize them, we have to // watch out for the things that override arguments (namely, functions). // This is our final act of weirdness. "arguments" is overridden by everything except the // callee. We add it to the symbol table if it's not already there and it's not an argument. bool shouldCreateArgumentsVariableInParameterScope = false; if (needsArguments) { // If "arguments" is overridden by a function or destructuring parameter name, then it's // OK for us to call createVariable() because it won't change anything. It's also OK for // us to them tell BytecodeGenerator::generate() to write to it because it will do so // before it initializes functions and destructuring parameters. But if "arguments" is // overridden by a "simple" function parameter, then we have to bail: createVariable() // would assert and BytecodeGenerator::generate() would write the "arguments" after the // argument value had already been properly initialized. bool haveParameterNamedArguments = false; for (unsigned i = 0; i < parameters.size(); ++i) { UniquedStringImpl* name = visibleNameForParameter(parameters.at(i).first); if (name == propertyNames().arguments.impl()) { haveParameterNamedArguments = true; break; } } bool shouldCreateArgumensVariable = !haveParameterNamedArguments && !m_codeBlock->isArrowFunction(); shouldCreateArgumentsVariableInParameterScope = shouldCreateArgumensVariable && !isSimpleParameterList; // Do not create arguments variable in case of Arrow function. Value will be loaded from parent scope if (shouldCreateArgumensVariable && !shouldCreateArgumentsVariableInParameterScope) { createVariable( propertyNames().arguments, varKind(propertyNames().arguments.impl()), functionSymbolTable); m_needToInitializeArguments = true; } } for (FunctionMetadataNode* function : functionNode->functionStack()) { const Identifier& ident = function->ident(); createVariable(ident, varKind(ident.impl()), functionSymbolTable); m_functionsToInitialize.append(std::make_pair(function, NormalFunctionVariable)); } for (auto& entry : functionNode->varDeclarations()) { ASSERT(!entry.value.isLet() && !entry.value.isConst()); if (!entry.value.isVar()) // This is either a parameter or callee. continue; if (shouldCreateArgumentsVariableInParameterScope && entry.key.get() == propertyNames().arguments.impl()) continue; createVariable(Identifier::fromUid(m_vm, entry.key.get()), varKind(entry.key.get()), functionSymbolTable, IgnoreExisting); } m_newTargetRegister = addVar(); switch (parseMode) { case SourceParseMode::GeneratorWrapperFunctionMode: { m_generatorRegister = addVar(); // FIXME: Emit to_this only when Generator uses it. // https://bugs.webkit.org/show_bug.cgi?id=151586 m_codeBlock->addPropertyAccessInstruction(instructions().size()); emitOpcode(op_to_this); instructions().append(kill(&m_thisRegister)); instructions().append(0); instructions().append(0); emitMove(m_generatorRegister, &m_calleeRegister); emitCreateThis(m_generatorRegister); break; } case SourceParseMode::GeneratorBodyMode: { // |this| is already filled correctly before here. emitLoad(m_newTargetRegister, jsUndefined()); break; } default: { if (SourceParseMode::ArrowFunctionMode != parseMode) { if (isConstructor()) { emitMove(m_newTargetRegister, &m_thisRegister); if (constructorKind() == ConstructorKind::Derived) emitMoveEmptyValue(&m_thisRegister); else emitCreateThis(&m_thisRegister); } else if (constructorKind() != ConstructorKind::None) { emitThrowTypeError("Cannot call a class constructor"); } else if (functionNode->usesThis() || codeBlock->usesEval()) { m_codeBlock->addPropertyAccessInstruction(instructions().size()); emitOpcode(op_to_this); instructions().append(kill(&m_thisRegister)); instructions().append(0); instructions().append(0); } } break; } } // We need load |super| & |this| for arrow function before initializeDefaultParameterValuesAndSetupFunctionScopeStack // if we have default parameter expression. Because |super| & |this| values can be used there if (SourceParseMode::ArrowFunctionMode == parseMode && !isSimpleParameterList) { if (functionNode->usesThis() || functionNode->usesSuperProperty()) emitLoadThisFromArrowFunctionLexicalEnvironment(); if (m_scopeNode->usesNewTarget() || m_scopeNode->usesSuperCall()) emitLoadNewTargetFromArrowFunctionLexicalEnvironment(); } if (needsToUpdateArrowFunctionContext() && !codeBlock->isArrowFunction()) { bool canReuseLexicalEnvironment = isSimpleParameterList; initializeArrowFunctionContextScopeIfNeeded(functionSymbolTable, canReuseLexicalEnvironment); emitPutThisToArrowFunctionContextScope(); emitPutNewTargetToArrowFunctionContextScope(); emitPutDerivedConstructorToArrowFunctionContextScope(); } // All "addVar()"s needs to happen before "initializeDefaultParameterValuesAndSetupFunctionScopeStack()" is called // because a function's default parameter ExpressionNodes will use temporary registers. pushTDZVariables(*parentScopeTDZVariables, TDZCheckOptimization::DoNotOptimize); initializeDefaultParameterValuesAndSetupFunctionScopeStack(parameters, isSimpleParameterList, functionNode, functionSymbolTable, symbolTableConstantIndex, captures, shouldCreateArgumentsVariableInParameterScope); // If we don't have default parameter expression, then loading |this| inside an arrow function must be done // after initializeDefaultParameterValuesAndSetupFunctionScopeStack() because that function sets up the // SymbolTable stack and emitLoadThisFromArrowFunctionLexicalEnvironment() consults the SymbolTable stack if (SourceParseMode::ArrowFunctionMode == parseMode && isSimpleParameterList) { if (functionNode->usesThis() || functionNode->usesSuperProperty()) emitLoadThisFromArrowFunctionLexicalEnvironment(); if (m_scopeNode->usesNewTarget() || m_scopeNode->usesSuperCall()) emitLoadNewTargetFromArrowFunctionLexicalEnvironment(); } bool shouldInitializeBlockScopedFunctions = false; // We generate top-level function declarations in ::generate(). pushLexicalScope(m_scopeNode, TDZCheckOptimization::Optimize, NestedScopeType::IsNotNested, nullptr, shouldInitializeBlockScopedFunctions); } BytecodeGenerator::BytecodeGenerator(VM& vm, EvalNode* evalNode, UnlinkedEvalCodeBlock* codeBlock, DebuggerMode debuggerMode, const VariableEnvironment* parentScopeTDZVariables) : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn) , m_scopeNode(evalNode) , m_codeBlock(vm, codeBlock) , m_thisRegister(CallFrame::thisArgumentOffset()) , m_codeType(EvalCode) , m_vm(&vm) , m_usesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode()) , m_needsToUpdateArrowFunctionContext(evalNode->usesArrowFunction() || evalNode->usesEval()) , m_derivedContextType(codeBlock->derivedContextType()) { for (auto& constantRegister : m_linkTimeConstantRegisters) constantRegister = nullptr; allocateCalleeSaveSpace(); m_codeBlock->setNumParameters(1); emitEnter(); allocateAndEmitScope(); const DeclarationStacks::FunctionStack& functionStack = evalNode->functionStack(); for (size_t i = 0; i < functionStack.size(); ++i) m_codeBlock->addFunctionDecl(makeFunction(functionStack[i])); const VariableEnvironment& varDeclarations = evalNode->varDeclarations(); unsigned numVariables = varDeclarations.size(); Vector variables; variables.reserveCapacity(numVariables); for (auto& entry : varDeclarations) { ASSERT(entry.value.isVar()); ASSERT(entry.key->isAtomic() || entry.key->isSymbol()); variables.append(Identifier::fromUid(m_vm, entry.key.get())); } codeBlock->adoptVariables(variables); if (evalNode->usesSuperCall() || evalNode->usesNewTarget()) m_newTargetRegister = addVar(); pushTDZVariables(*parentScopeTDZVariables, TDZCheckOptimization::DoNotOptimize); if (codeBlock->isArrowFunctionContext() && (evalNode->usesThis() || evalNode->usesSuperProperty())) emitLoadThisFromArrowFunctionLexicalEnvironment(); if (evalNode->usesSuperCall() || evalNode->usesNewTarget()) emitLoadNewTargetFromArrowFunctionLexicalEnvironment(); if (needsToUpdateArrowFunctionContext() && !codeBlock->isArrowFunctionContext() && !isDerivedConstructorContext()) { initializeArrowFunctionContextScopeIfNeeded(); emitPutThisToArrowFunctionContextScope(); } bool shouldInitializeBlockScopedFunctions = false; // We generate top-level function declarations in ::generate(). pushLexicalScope(m_scopeNode, TDZCheckOptimization::Optimize, NestedScopeType::IsNotNested, nullptr, shouldInitializeBlockScopedFunctions); } BytecodeGenerator::BytecodeGenerator(VM& vm, ModuleProgramNode* moduleProgramNode, UnlinkedModuleProgramCodeBlock* codeBlock, DebuggerMode debuggerMode, const VariableEnvironment* parentScopeTDZVariables) : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn) , m_scopeNode(moduleProgramNode) , m_codeBlock(vm, codeBlock) , m_thisRegister(CallFrame::thisArgumentOffset()) , m_codeType(ModuleCode) , m_vm(&vm) , m_usesNonStrictEval(false) , m_needsToUpdateArrowFunctionContext(moduleProgramNode->usesArrowFunction() || moduleProgramNode->usesEval()) { ASSERT_UNUSED(parentScopeTDZVariables, !parentScopeTDZVariables->size()); for (auto& constantRegister : m_linkTimeConstantRegisters) constantRegister = nullptr; if (m_isBuiltinFunction) m_shouldEmitDebugHooks = false; allocateCalleeSaveSpace(); SymbolTable* moduleEnvironmentSymbolTable = SymbolTable::create(*m_vm); moduleEnvironmentSymbolTable->setUsesNonStrictEval(m_usesNonStrictEval); moduleEnvironmentSymbolTable->setScopeType(SymbolTable::ScopeType::LexicalScope); bool shouldCaptureAllOfTheThings = m_shouldEmitDebugHooks || codeBlock->usesEval(); if (shouldCaptureAllOfTheThings) moduleProgramNode->varDeclarations().markAllVariablesAsCaptured(); auto captures = [&] (UniquedStringImpl* uid) -> bool { return moduleProgramNode->captures(uid); }; auto lookUpVarKind = [&] (UniquedStringImpl* uid, const VariableEnvironmentEntry& entry) -> VarKind { // Allocate the exported variables in the module environment. if (entry.isExported()) return VarKind::Scope; // Allocate the namespace variables in the module environment to instantiate // it from the outside of the module code. if (entry.isImportedNamespace()) return VarKind::Scope; if (entry.isCaptured()) return VarKind::Scope; return captures(uid) ? VarKind::Scope : VarKind::Stack; }; emitEnter(); allocateAndEmitScope(); m_calleeRegister.setIndex(JSStack::Callee); m_codeBlock->setNumParameters(1); // Allocate space for "this" // Now declare all variables. for (auto& entry : moduleProgramNode->varDeclarations()) { ASSERT(!entry.value.isLet() && !entry.value.isConst()); if (!entry.value.isVar()) // This is either a parameter or callee. continue; // Imported bindings are not allocated in the module environment as usual variables' way. // These references remain the "Dynamic" in the unlinked code block. Later, when linking // the code block, we resolve the reference to the "ModuleVar". if (entry.value.isImported() && !entry.value.isImportedNamespace()) continue; createVariable(Identifier::fromUid(m_vm, entry.key.get()), lookUpVarKind(entry.key.get(), entry.value), moduleEnvironmentSymbolTable, IgnoreExisting); } VariableEnvironment& lexicalVariables = moduleProgramNode->lexicalVariables(); instantiateLexicalVariables(lexicalVariables, moduleEnvironmentSymbolTable, ScopeRegisterType::Block, lookUpVarKind); // We keep the symbol table in the constant pool. RegisterID* constantSymbolTable = nullptr; if (vm.typeProfiler()) constantSymbolTable = addConstantValue(moduleEnvironmentSymbolTable); else constantSymbolTable = addConstantValue(moduleEnvironmentSymbolTable->cloneScopePart(*m_vm)); pushTDZVariables(lexicalVariables, TDZCheckOptimization::Optimize); bool isWithScope = false; m_symbolTableStack.append(SymbolTableStackEntry { moduleEnvironmentSymbolTable, m_topMostScope, isWithScope, constantSymbolTable->index() }); emitPrefillStackTDZVariables(lexicalVariables, moduleEnvironmentSymbolTable); // makeFunction assumes that there's correct TDZ stack entries. // So it should be called after putting our lexical environment to the TDZ stack correctly. for (FunctionMetadataNode* function : moduleProgramNode->functionStack()) { const auto& iterator = moduleProgramNode->varDeclarations().find(function->ident().impl()); RELEASE_ASSERT(iterator != moduleProgramNode->varDeclarations().end()); RELEASE_ASSERT(!iterator->value.isImported()); VarKind varKind = lookUpVarKind(iterator->key.get(), iterator->value); if (varKind == VarKind::Scope) { // http://www.ecma-international.org/ecma-262/6.0/#sec-moduledeclarationinstantiation // Section 15.2.1.16.4, step 16-a-iv-1. // All heap allocated function declarations should be instantiated when the module environment // is created. They include the exported function declarations and not-exported-but-heap-allocated // function declarations. This is required because exported function should be instantiated before // executing the any module in the dependency graph. This enables the modules to link the imported // bindings before executing the any module code. // // And since function declarations are instantiated before executing the module body code, the spec // allows the functions inside the module to be executed before its module body is executed under // the circular dependencies. The following is the example. // // Module A (executed first): // import { b } from "B"; // // Here, the module "B" is not executed yet, but the function declaration is already instantiated. // // So we can call the function exported from "B". // b(); // // export function a() { // } // // Module B (executed second): // import { a } from "A"; // // export function b() { // c(); // } // // // c is not exported, but since it is referenced from the b, we should instantiate it before // // executing the "B" module code. // function c() { // a(); // } // // Module EntryPoint (executed last): // import "B"; // import "A"; // m_codeBlock->addFunctionDecl(makeFunction(function)); } else { // Stack allocated functions can be allocated when executing the module's body. m_functionsToInitialize.append(std::make_pair(function, NormalFunctionVariable)); } } // Remember the constant register offset to the top-most symbol table. This symbol table will be // cloned in the code block linking. After that, to create the module environment, we retrieve // the cloned symbol table from the linked code block by using this offset. codeBlock->setModuleEnvironmentSymbolTableConstantRegisterOffset(constantSymbolTable->index()); } BytecodeGenerator::~BytecodeGenerator() { } void BytecodeGenerator::initializeDefaultParameterValuesAndSetupFunctionScopeStack( FunctionParameters& parameters, bool isSimpleParameterList, FunctionNode* functionNode, SymbolTable* functionSymbolTable, int symbolTableConstantIndex, const std::function& captures, bool shouldCreateArgumentsVariableInParameterScope) { Vector>> valuesToMoveIntoVars; ASSERT(!(isSimpleParameterList && shouldCreateArgumentsVariableInParameterScope)); if (!isSimpleParameterList) { // Refer to the ES6 spec section 9.2.12: http://www.ecma-international.org/ecma-262/6.0/index.html#sec-functiondeclarationinstantiation // This implements step 21. VariableEnvironment environment; Vector allParameterNames; for (unsigned i = 0; i < parameters.size(); i++) parameters.at(i).first->collectBoundIdentifiers(allParameterNames); if (shouldCreateArgumentsVariableInParameterScope) allParameterNames.append(propertyNames().arguments); IdentifierSet parameterSet; for (auto& ident : allParameterNames) { parameterSet.add(ident.impl()); auto addResult = environment.add(ident); addResult.iterator->value.setIsLet(); // When we have default parameter expressions, parameters act like "let" variables. if (captures(ident.impl())) addResult.iterator->value.setIsCaptured(); } // This implements step 25 of section 9.2.12. pushLexicalScopeInternal(environment, TDZCheckOptimization::Optimize, NestedScopeType::IsNotNested, nullptr, TDZRequirement::UnderTDZ, ScopeType::LetConstScope, ScopeRegisterType::Block); if (shouldCreateArgumentsVariableInParameterScope) { Variable argumentsVariable = variable(propertyNames().arguments); initializeVariable(argumentsVariable, m_argumentsRegister); liftTDZCheckIfPossible(argumentsVariable); } RefPtr temp = newTemporary(); for (unsigned i = 0; i < parameters.size(); i++) { std::pair parameter = parameters.at(i); if (parameter.first->isRestParameter()) continue; RefPtr parameterValue = ®isterFor(virtualRegisterForArgument(1 + i)); emitMove(temp.get(), parameterValue.get()); if (parameter.second) { RefPtr condition = emitIsUndefined(newTemporary(), parameterValue.get()); RefPtr