From 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Tue, 27 Jun 2017 06:07:23 +0000 Subject: webkitgtk-2.16.5 --- .../bytecompiler/BytecodeGenerator.cpp | 4635 +++++++++++++++----- .../bytecompiler/BytecodeGenerator.h | 1017 +++-- Source/JavaScriptCore/bytecompiler/Label.h | 12 +- Source/JavaScriptCore/bytecompiler/LabelScope.h | 26 +- .../JavaScriptCore/bytecompiler/NodesCodegen.cpp | 3014 ++++++++++--- Source/JavaScriptCore/bytecompiler/RegisterID.h | 8 +- .../bytecompiler/StaticPropertyAnalysis.h | 10 +- .../bytecompiler/StaticPropertyAnalyzer.h | 9 +- 8 files changed, 6697 insertions(+), 2034 deletions(-) (limited to 'Source/JavaScriptCore/bytecompiler') diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp index cd4490f59..4b211b7b6 100644 --- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp +++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2012, 2013, 2014 Apple Inc. All rights reserved. + * Copyright (C) 2008-2017 Apple Inc. All rights reserved. * Copyright (C) 2008 Cameron Zwarich * Copyright (C) 2012 Igalia, S.L. * @@ -12,7 +12,7 @@ * 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 Computer, Inc. ("Apple") nor the names of + * 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. * @@ -31,17 +31,30 @@ #include "config.h" #include "BytecodeGenerator.h" +#include "ArithProfile.h" +#include "BuiltinExecutables.h" +#include "BytecodeGeneratorification.h" +#include "BytecodeLivenessAnalysis.h" +#include "DefinePropertyAttributes.h" #include "Interpreter.h" -#include "JSActivation.h" +#include "JSCInlines.h" #include "JSFunction.h" -#include "JSNameScope.h" +#include "JSGeneratorFunction.h" +#include "JSLexicalEnvironment.h" +#include "JSTemplateRegistryKey.h" #include "LowLevelInterpreter.h" -#include "Operations.h" #include "Options.h" #include "StackAlignment.h" #include "StrongInlines.h" #include "UnlinkedCodeBlock.h" +#include "UnlinkedEvalCodeBlock.h" +#include "UnlinkedFunctionCodeBlock.h" #include "UnlinkedInstructionStream.h" +#include "UnlinkedModuleProgramCodeBlock.h" +#include "UnlinkedProgramCodeBlock.h" +#include +#include +#include #include #include @@ -55,21 +68,65 @@ void Label::setLocation(unsigned location) unsigned size = m_unresolvedJumps.size(); for (unsigned i = 0; i < size; ++i) - m_generator->m_instructions[m_unresolvedJumps[i].second].u.operand = m_location - m_unresolvedJumps[i].first; + m_generator.instructions()[m_unresolvedJumps[i].second].u.operand = m_location - m_unresolvedJumps[i].first; +} + +void Variable::dump(PrintStream& out) const +{ + out.print( + "{ident = ", m_ident, + ", offset = ", m_offset, + ", local = ", RawPointer(m_local), + ", attributes = ", m_attributes, + ", kind = ", m_kind, + ", symbolTableConstantIndex = ", m_symbolTableConstantIndex, + ", isLexicallyScoped = ", m_isLexicallyScoped, "}"); } ParserError BytecodeGenerator::generate() { - SamplingRegion samplingRegion("Bytecode Generation"); - m_codeBlock->setThisRegister(m_thisRegister.virtualRegister()); - m_scopeNode->emitBytecode(*this); + 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]; + for (auto& range : m_tryRanges) { int start = range.start->bind(); int end = range.end->bind(); @@ -98,15 +155,15 @@ ParserError BytecodeGenerator::generate() if (end <= start) continue; - ASSERT(range.tryData->targetScopeDepth != UINT_MAX); - UnlinkedHandlerInfo info = { - static_cast(start), static_cast(end), - static_cast(range.tryData->target->bind()), - range.tryData->targetScopeDepth - }; + UnlinkedHandlerInfo info(static_cast(start), static_cast(end), + static_cast(range.tryData->target->bind()), range.tryData->handlerType); m_codeBlock->addExceptionHandler(info); } + + if (isGeneratorOrAsyncFunctionBodyParseMode(m_codeBlock->parseMode())) + performGeneratorification(m_codeBlock.get(), m_instructions, m_generatorFrameSymbolTable.get(), m_generatorFrameSymbolTableIndex); + m_codeBlock->setInstructions(std::make_unique(m_instructions)); m_codeBlock->shrinkToFit(); @@ -116,517 +173,1078 @@ ParserError BytecodeGenerator::generate() return ParserError(ParserError::ErrorNone); } -bool BytecodeGenerator::addVar( - const Identifier& ident, ConstantMode constantMode, WatchMode watchMode, RegisterID*& r0) -{ - ASSERT(static_cast(m_codeBlock->m_numVars) == m_calleeRegisters.size()); - - ConcurrentJITLocker locker(symbolTable().m_lock); - int index = virtualRegisterForLocal(m_calleeRegisters.size()).offset(); - SymbolTableEntry newEntry(index, constantMode == IsConstant ? ReadOnly : 0); - SymbolTable::Map::AddResult result = symbolTable().add(locker, ident.impl(), newEntry); - - if (!result.isNewEntry) { - r0 = ®isterFor(result.iterator->value.getIndex()); - return false; - } - - if (watchMode == IsWatchable) { - while (m_watchableVariables.size() < static_cast(m_codeBlock->m_numVars)) - m_watchableVariables.append(Identifier()); - m_watchableVariables.append(ident); - } - - r0 = addVar(); - - ASSERT(watchMode == NotWatchable || static_cast(m_codeBlock->m_numVars) == m_watchableVariables.size()); - - return true; -} - -void BytecodeGenerator::preserveLastVar() -{ - if ((m_firstConstantIndex = m_calleeRegisters.size()) != 0) - m_lastVar = &m_calleeRegisters.last(); -} - -BytecodeGenerator::BytecodeGenerator(VM& vm, ProgramNode* programNode, UnlinkedProgramCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode) - : m_shouldEmitDebugHooks(debuggerMode == DebuggerOn) - , m_shouldEmitProfileHooks(profilerMode == ProfilerOn) - , m_symbolTable(0) +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_emptyValueRegister(0) - , m_globalObjectRegister(0) - , m_finallyDepth(0) - , m_localScopeDepth(0) , m_codeType(GlobalCode) - , m_nextConstantOffset(0) - , m_globalConstantIndex(0) - , m_hasCreatedActivation(true) - , m_firstLazyFunction(0) - , m_lastLazyFunction(0) - , m_staticPropertyAnalyzer(&m_instructions) , m_vm(&vm) - , m_lastOpcodeID(op_end) -#ifndef NDEBUG - , m_lastOpcodePosition(0) -#endif - , m_usesExceptions(false) - , m_expressionTooDeep(false) + , m_needsToUpdateArrowFunctionContext(programNode->usesArrowFunction() || programNode->usesEval()) { - if (m_shouldEmitDebugHooks) - m_codeBlock->setNeedsFullScopeChain(true); + ASSERT_UNUSED(parentScopeTDZVariables, !parentScopeTDZVariables->size()); + + for (auto& constantRegister : m_linkTimeConstantRegisters) + constantRegister = nullptr; + + allocateCalleeSaveSpace(); m_codeBlock->setNumParameters(1); // Allocate space for "this" - emitOpcode(op_enter); + emitEnter(); - const VarStack& varStack = programNode->varStack(); - const FunctionStack& functionStack = programNode->functionStack(); + allocateAndEmitScope(); - for (size_t i = 0; i < functionStack.size(); ++i) { - FunctionBodyNode* function = functionStack[i]; - UnlinkedFunctionExecutable* unlinkedFunction = makeFunction(function); - codeBlock->addFunctionDeclaration(*m_vm, function->ident(), unlinkedFunction); - } + emitWatchdog(); + + const FunctionStack& functionStack = programNode->functionStack(); - for (size_t i = 0; i < varStack.size(); ++i) - codeBlock->addVariableDeclaration(varStack[i].first, !!(varStack[i].second & DeclarationStacks::IsConstant)); + for (auto* function : functionStack) + 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, FunctionBodyNode* functionBody, UnlinkedFunctionCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode) - : m_shouldEmitDebugHooks(debuggerMode == DebuggerOn) - , m_shouldEmitProfileHooks(profilerMode == ProfilerOn) - , m_symbolTable(codeBlock->symbolTable()) - , m_scopeNode(functionBody) +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_activationRegister(0) - , m_emptyValueRegister(0) - , m_globalObjectRegister(0) - , m_finallyDepth(0) - , m_localScopeDepth(0) , m_codeType(FunctionCode) - , m_nextConstantOffset(0) - , m_globalConstantIndex(0) - , m_hasCreatedActivation(false) - , m_firstLazyFunction(0) - , m_lastLazyFunction(0) - , m_staticPropertyAnalyzer(&m_instructions) , m_vm(&vm) - , m_lastOpcodeID(op_end) -#ifndef NDEBUG - , m_lastOpcodePosition(0) -#endif - , m_usesExceptions(false) - , m_expressionTooDeep(false) + , 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()) { - if (m_shouldEmitDebugHooks) - m_codeBlock->setNeedsFullScopeChain(true); - - m_symbolTable->setUsesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode()); - Vector boundParameterProperties; - FunctionParameters& parameters = *functionBody->parameters(); - for (size_t i = 0; i < parameters.size(); i++) { - auto pattern = parameters.at(i); - if (pattern->isBindingNode()) - continue; - pattern->collectBoundIdentifiers(boundParameterProperties); - continue; + 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. + // If we do have default parameters, destructuring parameters, or a rest parameter, our parameters will be allocated in a different scope. + bool isSimpleParameterList = parameters.isSimpleParameterList(); + + 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->isArrowFunction()) || codeBlock->usesEval() || (functionNode->usesArrowFunction() && !codeBlock->isArrowFunction() && isArgumentsUsedInInnerArrowFunction())); + + if (isGeneratorOrAsyncFunctionBodyParseMode(parseMode)) { + // Generator and AsyncFunction never provides "arguments". "arguments" reference will be resolved in an upper generator function scope. + needsArguments = false; + + // Generator and AsyncFunction uses the var scope to save and resume its variables. So the lexical scope is always instantiated. + shouldCaptureSomeOfTheThings = true; } - m_symbolTable->setParameterCountIncludingThis(functionBody->parameters()->size() + 1); - emitOpcode(op_enter); - if (m_codeBlock->needsFullScopeChain()) { - m_activationRegister = addVar(); - emitInitLazyRegister(m_activationRegister); - m_codeBlock->setActivationRegister(m_activationRegister->virtualRegister()); + if (isGeneratorOrAsyncFunctionWrapperParseMode(parseMode) && 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, @generatorFrame) + // { + // arguments; // This `arguments` should reference to the gen's arguments. + // ... + // } + // } + // } + shouldCaptureSomeOfTheThings = true; } - m_symbolTable->setCaptureStart(virtualRegisterForLocal(m_codeBlock->m_numVars).offset()); + 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; + }; - if (functionBody->usesArguments() || codeBlock->usesEval()) { // May reify arguments object. - RegisterID* unmodifiedArgumentsRegister = addVar(); // Anonymous, so it can't be modified by user code. - RegisterID* argumentsRegister = addVar(propertyNames().arguments, IsVariable, NotWatchable); // Can be changed by assigning to 'arguments'. + m_calleeRegister.setIndex(CallFrameSlot::callee); - // We can save a little space by hard-coding the knowledge that the two - // 'arguments' values are stored in consecutive registers, and storing - // only the index of the assignable one. - codeBlock->setArgumentsRegister(argumentsRegister->virtualRegister()); - ASSERT_UNUSED(unmodifiedArgumentsRegister, unmodifiedArgumentsRegister->virtualRegister() == JSC::unmodifiedArgumentsRegister(codeBlock->argumentsRegister())); + initializeParameters(parameters); + ASSERT(!(isSimpleParameterList && m_restParameter)); - emitInitLazyRegister(argumentsRegister); - emitInitLazyRegister(unmodifiedArgumentsRegister); - - if (shouldTearOffArgumentsEagerly()) { - emitOpcode(op_create_arguments); - instructions().append(argumentsRegister->index()); - } + emitEnter(); + + if (isGeneratorOrAsyncFunctionBodyParseMode(parseMode)) + m_generatorRegister = &m_parameters[1]; + + allocateAndEmitScope(); + + emitWatchdog(); + + if (functionNameIsInScope(functionNode->ident(), functionNode->functionMode())) { + ASSERT(parseMode != SourceParseMode::GeneratorBodyMode); + ASSERT(!isAsyncFunctionBodyParseMode(parseMode)); + bool isDynamicScope = functionNameScopeIsDynamic(codeBlock->usesEval(), codeBlock->isStrictMode()); + bool isFunctionNameCaptured = captures(functionNode->ident().impl()); + bool markAsCaptured = isDynamicScope || isFunctionNameCaptured; + emitPushFunctionNameScope(functionNode->ident(), &m_calleeRegister, markAsCaptured); } - bool shouldCaptureAllTheThings = m_shouldEmitDebugHooks || codeBlock->usesEval(); + 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; - Vector capturedArguments; - if (functionBody->hasCapturedVariables() || shouldCaptureAllTheThings) { - FunctionParameters& parameters = *functionBody->parameters(); - capturedArguments.resize(parameters.size()); + if (functionNode->hasCapturedVariables()) { + FunctionParameters& parameters = *functionNode->parameters(); for (size_t i = 0; i < parameters.size(); ++i) { - capturedArguments[i] = 0; - auto pattern = parameters.at(i); + auto pattern = parameters.at(i).first; if (!pattern->isBindingNode()) continue; const Identifier& ident = static_cast(pattern)->boundProperty(); - if (!functionBody->captures(ident) && !shouldCaptureAllTheThings) - continue; - capturesAnyArgumentByName = true; - capturedArguments[i] = addVar(); + capturesAnyArgumentByName |= captures(ident.impl()); } } + + if (capturesAnyArgumentByName) + ASSERT(m_lexicalEnvironmentRegister); - if (capturesAnyArgumentByName && !shouldTearOffArgumentsEagerly()) { - size_t parameterCount = m_symbolTable->parameterCount(); - auto slowArguments = std::make_unique(parameterCount); - for (size_t i = 0; i < parameterCount; ++i) { - if (!capturedArguments[i]) { - ASSERT(slowArguments[i].status == SlowArgument::Normal); - slowArguments[i].index = CallFrame::argumentOffset(i); + // 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(*m_vm); + 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; } - slowArguments[i].status = SlowArgument::Captured; - slowArguments[i].index = capturedArguments[i]->index(); + + 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()); } - m_symbolTable->setSlowArguments(std::move(slowArguments)); } - - RegisterID* calleeRegister = resolveCallee(functionBody); // May push to the scope chain and/or add a captured var. - - const DeclarationStacks::FunctionStack& functionStack = functionBody->functionStack(); - const DeclarationStacks::VarStack& varStack = functionBody->varStack(); - - // Captured variables and functions go first so that activations don't have - // to step over the non-captured locals to mark them. - m_hasCreatedActivation = false; - if (functionBody->hasCapturedVariables()) { - for (size_t i = 0; i < functionStack.size(); ++i) { - FunctionBodyNode* function = functionStack[i]; - const Identifier& ident = function->ident(); - if (functionBody->captures(ident)) { - if (!m_hasCreatedActivation) { - m_hasCreatedActivation = true; - emitOpcode(op_create_activation); - instructions().append(m_activationRegister->index()); - } - m_functions.add(ident.impl()); - emitNewFunction(addVar(ident, IsVariable, IsWatchable), IsCaptured, function); + + 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; } } - for (size_t i = 0; i < varStack.size(); ++i) { - const Identifier& ident = varStack[i].first; - if (functionBody->captures(ident)) - addVar(ident, (varStack[i].second & DeclarationStacks::IsConstant) ? IsConstant : IsVariable, IsWatchable); + + bool shouldCreateArgumensVariable = !haveParameterNamedArguments + && !SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode).contains(m_codeBlock->parseMode()); + 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; } } - bool canLazilyCreateFunctions = !functionBody->needsActivationForMoreThanVariables() && !m_shouldEmitDebugHooks; - if (!canLazilyCreateFunctions && !m_hasCreatedActivation) { - m_hasCreatedActivation = true; - emitOpcode(op_create_activation); - instructions().append(m_activationRegister->index()); + + 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_symbolTable->setCaptureEnd(virtualRegisterForLocal(codeBlock->m_numVars).offset()); - m_firstLazyFunction = codeBlock->m_numVars; - for (size_t i = 0; i < functionStack.size(); ++i) { - FunctionBodyNode* function = functionStack[i]; - const Identifier& ident = function->ident(); - if (!functionBody->captures(ident)) { - m_functions.add(ident.impl()); - RefPtr reg = addVar(ident, IsVariable, NotWatchable); - // Don't lazily create functions that override the name 'arguments' - // as this would complicate lazy instantiation of actual arguments. - if (!canLazilyCreateFunctions || ident == propertyNames().arguments) - emitNewFunction(reg.get(), NotCaptured, function); + 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::AsyncArrowFunctionMode: + case SourceParseMode::AsyncMethodMode: + case SourceParseMode::AsyncFunctionMode: { + ASSERT(!isConstructor()); + ASSERT(constructorKind() == ConstructorKind::None); + m_generatorRegister = addVar(); + m_promiseCapabilityRegister = addVar(); + + if (parseMode != SourceParseMode::AsyncArrowFunctionMode) { + // FIXME: Emit to_this only when AsyncFunctionBody 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); + } + + emitNewObject(m_generatorRegister); + + // let promiseCapability be @newPromiseCapability(@Promise) + auto varNewPromiseCapability = variable(propertyNames().builtinNames().newPromiseCapabilityPrivateName()); + RefPtr scope = newTemporary(); + moveToDestinationIfNeeded(scope.get(), emitResolveScope(scope.get(), varNewPromiseCapability)); + RefPtr newPromiseCapability = emitGetFromScope(newTemporary(), scope.get(), varNewPromiseCapability, ThrowIfNotFound); + + CallArguments args(*this, nullptr, 1); + emitLoad(args.thisRegister(), jsUndefined()); + + auto varPromiseConstructor = variable(propertyNames().builtinNames().PromisePrivateName()); + moveToDestinationIfNeeded(scope.get(), emitResolveScope(scope.get(), varPromiseConstructor)); + emitGetFromScope(args.argumentRegister(0), scope.get(), varPromiseConstructor, ThrowIfNotFound); + + // JSTextPosition(int _line, int _offset, int _lineStartOffset) + JSTextPosition divot(m_scopeNode->firstLine(), m_scopeNode->startOffset(), m_scopeNode->lineStartOffset()); + emitCall(promiseCapabilityRegister(), newPromiseCapability.get(), NoExpectedFunction, args, divot, divot, divot, DebuggableCall::No); + break; + } + + case SourceParseMode::AsyncFunctionBodyMode: + case SourceParseMode::AsyncArrowFunctionBodyMode: + 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::Extends) { + Ref