diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
commit | 32761a6cee1d0dee366b885b7b9c777e67885688 (patch) | |
tree | d6bec92bebfb216f4126356e55518842c2f476a1 /Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp | |
parent | a4e969f4965059196ca948db781e52f7cfebf19e (diff) | |
download | WebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz |
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp')
-rw-r--r-- | Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp | 3315 |
1 files changed, 724 insertions, 2591 deletions
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp index ae8b4762c..cd4490f59 100644 --- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp +++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2012-2015 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2012, 2013, 2014 Apple Inc. All rights reserved. * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca> * 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 Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Computer, 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,15 +31,12 @@ #include "config.h" #include "BytecodeGenerator.h" -#include "BuiltinExecutables.h" -#include "BytecodeLivenessAnalysis.h" #include "Interpreter.h" +#include "JSActivation.h" #include "JSFunction.h" -#include "JSGeneratorFunction.h" -#include "JSLexicalEnvironment.h" -#include "JSTemplateRegistryKey.h" +#include "JSNameScope.h" #include "LowLevelInterpreter.h" -#include "JSCInlines.h" +#include "Operations.h" #include "Options.h" #include "StackAlignment.h" #include "StrongInlines.h" @@ -58,49 +55,16 @@ void Label::setLocation(unsigned 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; + m_generator->m_instructions[m_unresolvedJumps[i].second].u.operand = m_location - m_unresolvedJumps[i].first; } ParserError BytecodeGenerator::generate() { SamplingRegion samplingRegion("Bytecode Generation"); - - m_codeBlock->setThisRegister(m_thisRegister.virtualRegister()); - // 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); + m_codeBlock->setThisRegister(m_thisRegister.virtualRegister()); - { - RefPtr<RegisterID> temp = newTemporary(); - RefPtr<RegisterID> 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<RegisterID> globalObjectScope = emitResolveScope(nullptr, Variable(metadata->ident())); - globalScope = newBlockScopeVariable(); - emitMove(globalScope.get(), globalObjectScope.get()); - } - emitPutToScope(globalScope.get(), Variable(metadata->ident()), temp.get(), ThrowIfNotFound, NotInitialization); - } else - RELEASE_ASSERT_NOT_REACHED(); - } - } - - bool callingClassConstructor = constructorKind() != ConstructorKind::None && !isConstructor(); - if (!callingClassConstructor) - m_scopeNode->emitBytecode(*this); + m_scopeNode->emitBytecode(*this); m_staticPropertyAnalyzer.kill(); @@ -134,9 +98,12 @@ ParserError BytecodeGenerator::generate() if (end <= start) continue; - ASSERT(range.tryData->handlerType != HandlerType::Illegal); - UnlinkedHandlerInfo info(static_cast<uint32_t>(start), static_cast<uint32_t>(end), - static_cast<uint32_t>(range.tryData->target->bind()), range.tryData->handlerType); + ASSERT(range.tryData->targetScopeDepth != UINT_MAX); + UnlinkedHandlerInfo info = { + static_cast<uint32_t>(start), static_cast<uint32_t>(end), + static_cast<uint32_t>(range.tryData->target->bind()), + range.tryData->targetScopeDepth + }; m_codeBlock->addExceptionHandler(info); } @@ -149,863 +116,465 @@ ParserError BytecodeGenerator::generate() return ParserError(ParserError::ErrorNone); } -BytecodeGenerator::BytecodeGenerator(VM& vm, ProgramNode* programNode, UnlinkedProgramCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode, const VariableEnvironment* parentScopeTDZVariables) - : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn) - , m_shouldEmitProfileHooks(Options::forceProfilerBytecodeGeneration() || profilerMode == ProfilerOn) +bool BytecodeGenerator::addVar( + const Identifier& ident, ConstantMode constantMode, WatchMode watchMode, RegisterID*& r0) +{ + ASSERT(static_cast<size_t>(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<size_t>(m_codeBlock->m_numVars)) + m_watchableVariables.append(Identifier()); + m_watchableVariables.append(ident); + } + + r0 = addVar(); + + ASSERT(watchMode == NotWatchable || static_cast<size_t>(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) , 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_needsToUpdateArrowFunctionContext(programNode->usesArrowFunction() || programNode->usesEval()) + , m_lastOpcodeID(op_end) +#ifndef NDEBUG + , m_lastOpcodePosition(0) +#endif + , m_usesExceptions(false) + , m_expressionTooDeep(false) { - ASSERT_UNUSED(parentScopeTDZVariables, !parentScopeTDZVariables->size()); - - for (auto& constantRegister : m_linkTimeConstantRegisters) - constantRegister = nullptr; - - allocateCalleeSaveSpace(); + if (m_shouldEmitDebugHooks) + m_codeBlock->setNeedsFullScopeChain(true); m_codeBlock->setNumParameters(1); // Allocate space for "this" - emitEnter(); - - allocateAndEmitScope(); + emitOpcode(op_enter); + const VarStack& varStack = programNode->varStack(); 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(); + FunctionBodyNode* function = functionStack[i]; + UnlinkedFunctionExecutable* unlinkedFunction = makeFunction(function); + codeBlock->addFunctionDeclaration(*m_vm, function->ident(), unlinkedFunction); } + + for (size_t i = 0; i < varStack.size(); ++i) + codeBlock->addVariableDeclaration(varStack[i].first, !!(varStack[i].second & DeclarationStacks::IsConstant)); + } -BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, UnlinkedFunctionCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode, const VariableEnvironment* parentScopeTDZVariables) - : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn) - , m_shouldEmitProfileHooks(Options::forceProfilerBytecodeGeneration() || profilerMode == ProfilerOn) - , m_scopeNode(functionNode) +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) , 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_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_shouldEmitProfileHooks) - , m_needsToUpdateArrowFunctionContext(functionNode->usesArrowFunction() || functionNode->usesEval()) - , m_derivedContextType(codeBlock->derivedContextType()) + , m_lastOpcodeID(op_end) +#ifndef NDEBUG + , m_lastOpcodePosition(0) +#endif + , m_usesExceptions(false) + , m_expressionTooDeep(false) { - 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 = addConstantValue(functionSymbolTable)->index(); + if (m_shouldEmitDebugHooks) + m_codeBlock->setNeedsFullScopeChain(true); + m_symbolTable->setUsesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode()); Vector<Identifier> boundParameterProperties; - FunctionParameters& parameters = *functionNode->parameters(); - if (!parameters.hasDefaultParameterValues()) { - // If we do have default parameters, they will be allocated in a separate scope. - for (size_t i = 0; i < parameters.size(); i++) { - auto pattern = parameters.at(i).first; - if (pattern->isBindingNode()) - continue; - pattern->collectBoundIdentifiers(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; } + m_symbolTable->setParameterCountIncludingThis(functionBody->parameters()->size() + 1); - SourceParseMode parseMode = codeBlock->parseMode(); - - bool containsArrowOrEvalButNotInArrowBlock = needsToUpdateArrowFunctionContext() && !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())); - - // 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; + emitOpcode(op_enter); + if (m_codeBlock->needsFullScopeChain()) { + m_activationRegister = addVar(); + emitInitLazyRegister(m_activationRegister); + m_codeBlock->setActivationRegister(m_activationRegister->virtualRegister()); } - 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); - - // 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]; + m_symbolTable->setCaptureStart(virtualRegisterForLocal(m_codeBlock->m_numVars).offset()); - // 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. + 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'. - beginGenerator(generatorStateRegister()); + // 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())); - // Initial state. - emitGeneratorStateLabel(); + emitInitLazyRegister(argumentsRegister); + emitInitLazyRegister(unmodifiedArgumentsRegister); + + if (shouldTearOffArgumentsEagerly()) { + emitOpcode(op_create_arguments); + instructions().append(argumentsRegister->index()); + } } - 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); - } + bool shouldCaptureAllTheThings = m_shouldEmitDebugHooks || codeBlock->usesEval(); - if (shouldCaptureSomeOfTheThings) { - m_lexicalEnvironmentRegister = addVar(); - // 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 (!parameters.hasDefaultParameterValues()) - initializeVarLexicalEnvironment(symbolTableConstantIndex); - } - - // Figure out some interesting facts about our arguments. bool capturesAnyArgumentByName = false; - if (functionNode->hasCapturedVariables()) { - FunctionParameters& parameters = *functionNode->parameters(); + Vector<RegisterID*, 0, UnsafeVectorOverflow> capturedArguments; + if (functionBody->hasCapturedVariables() || shouldCaptureAllTheThings) { + FunctionParameters& parameters = *functionBody->parameters(); + capturedArguments.resize(parameters.size()); for (size_t i = 0; i < parameters.size(); ++i) { - auto pattern = parameters.at(i).first; + capturedArguments[i] = 0; + auto pattern = parameters.at(i); if (!pattern->isBindingNode()) continue; const Identifier& ident = static_cast<const BindingNode*>(pattern)->boundProperty(); - capturesAnyArgumentByName |= captures(ident.impl()); + if (!functionBody->captures(ident) && !shouldCaptureAllTheThings) + continue; + capturesAnyArgumentByName = true; + capturedArguments[i] = addVar(); } } - - 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(); - } - - // 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. - // FIXME: Take into account destructuring to make isSimpleParameterList false. https://bugs.webkit.org/show_bug.cgi?id=151450 - bool isSimpleParameterList = !parameters.hasDefaultParameterValues() && !m_restParameter; - 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(); - 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(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, 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(name, SymbolTableEntry(VarOffset(DirectArgumentsOffset(i)))); + if (capturesAnyArgumentByName && !shouldTearOffArgumentsEagerly()) { + size_t parameterCount = m_symbolTable->parameterCount(); + auto slowArguments = std::make_unique<SlowArgument[]>(parameterCount); + for (size_t i = 0; i < parameterCount; ++i) { + if (!capturedArguments[i]) { + ASSERT(slowArguments[i].status == SlowArgument::Normal); + slowArguments[i].index = CallFrame::argumentOffset(i); + continue; } - - emitOpcode(op_create_direct_arguments); - instructions().append(m_argumentsRegister->index()); + slowArguments[i].status = SlowArgument::Captured; + slowArguments[i].index = capturedArguments[i]->index(); } - } else if (!parameters.hasDefaultParameterValues()) { - // Create the formal parameters the normal way. Any of them could be captured, or not. If - // captured, lift them into the scope. We can not 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(name, SymbolTableEntry(VarOffset(virtualRegisterForArgument(1 + i)))); - continue; + 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); } - - ScopeOffset offset = functionSymbolTable->takeNextScopeOffset(); - const Identifier& ident = - static_cast<const BindingNode*>(parameters.at(i).first)->boundProperty(); - functionSymbolTable->set(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, NotInitialization).operand()); - instructions().append(symbolTableConstantIndex); - instructions().append(offset.offset()); + } + 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); } } - - if (needsArguments && (codeBlock->isStrictMode() || !isSimpleParameterList)) { - // Allocate an out-of-bands arguments object. - emitOpcode(op_create_out_of_band_arguments); - instructions().append(m_argumentsRegister->index()); - } - - // Now declare all variables. - for (const Identifier& ident : boundParameterProperties) { - ASSERT(!parameters.hasDefaultParameterValues()); - createVariable(ident, varKind(ident.impl()), functionSymbolTable); + 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()) { + + 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(); - createVariable(ident, varKind(ident.impl()), functionSymbolTable); - m_functionsToInitialize.append(std::make_pair(function, NormalFunctionVariable)); + if (!functionBody->captures(ident)) { + m_functions.add(ident.impl()); + RefPtr<RegisterID> 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); + else { + emitInitLazyRegister(reg.get()); + m_lazyFunctions.set(reg->virtualRegister().toLocal(), function); + } + } } - for (auto& entry : functionNode->varDeclarations()) { - ASSERT(!entry.value.isLet() && !entry.value.isConst()); - if (!entry.value.isVar()) // This is either a parameter or callee. - continue; - // Variables named "arguments" are never const. - createVariable(Identifier::fromUid(m_vm, entry.key.get()), varKind(entry.key.get()), functionSymbolTable, IgnoreExisting); + m_lastLazyFunction = canLazilyCreateFunctions ? codeBlock->m_numVars : m_firstLazyFunction; + 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, NotWatchable); } - // 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. - 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; - } - } + if (shouldCaptureAllTheThings) + m_symbolTable->setCaptureEnd(virtualRegisterForLocal(codeBlock->m_numVars).offset()); - // Do not create arguments variable in case of Arrow function. Value will be loaded from parent scope - if (!haveParameterNamedArguments && !m_codeBlock->isArrowFunction()) { - createVariable( - propertyNames().arguments, varKind(propertyNames().arguments.impl()), functionSymbolTable); + if (m_symbolTable->captureCount()) + emitOpcode(op_touch_entry); + + m_parameters.grow(parameters.size() + 1); // reserve space for "this" - m_needToInitializeArguments = true; + // Add "this" as a parameter + int nextParameterIndex = CallFrame::thisArgumentOffset(); + m_thisRegister.setIndex(nextParameterIndex++); + m_codeBlock->addParameter(); + Vector<std::pair<RegisterID*, const DeconstructionPatternNode*>> deconstructedParameters; + for (size_t i = 0; i < parameters.size(); ++i, ++nextParameterIndex) { + int index = nextParameterIndex; + auto pattern = parameters.at(i); + if (!pattern->isBindingNode()) { + m_codeBlock->addParameter(); + RegisterID& parameter = registerFor(index); + parameter.setIndex(index); + deconstructedParameters.append(std::make_pair(¶meter, pattern)); + continue; + } + auto simpleParameter = static_cast<const BindingNode*>(pattern); + if (capturedArguments.size() && capturedArguments[i]) { + ASSERT((functionBody->hasCapturedVariables() && functionBody->captures(simpleParameter->boundProperty())) || shouldCaptureAllTheThings); + index = capturedArguments[i]->index(); + RegisterID original(nextParameterIndex); + emitMove(capturedArguments[i], &original); } + addParameter(simpleParameter->boundProperty(), index); } + preserveLastVar(); - m_newTargetRegister = addVar(); - switch (parseMode) { - case SourceParseMode::GeneratorWrapperFunctionMode: { - m_generatorRegister = addVar(); + // We declare the callee's name last because it should lose to a var, function, and/or parameter declaration. + addCallee(functionBody, calleeRegister); - // FIXME: Emit to_this only when Generator uses it. - // https://bugs.webkit.org/show_bug.cgi?id=151586 + if (isConstructor()) { + emitCreateThis(&m_thisRegister); + } else if (functionBody->usesThis() || codeBlock->usesEval() || m_shouldEmitDebugHooks) { 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; - } - } - - // All "addVar()"s needs to happen before "initializeDefaultParameterValuesAndSetupFunctionScopeStack()" is called - // because a function's default parameter ExpressionNodes will use temporary registers. - m_TDZStack.append(std::make_pair(*parentScopeTDZVariables, false)); - initializeDefaultParameterValuesAndSetupFunctionScopeStack(parameters, functionNode, functionSymbolTable, symbolTableConstantIndex, captures); - - // 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 && (functionNode->usesThis() || isDerivedClassContext() || isDerivedConstructorContext())) - emitLoadThisFromArrowFunctionLexicalEnvironment(); - - if (needsToUpdateArrowFunctionContext() && !codeBlock->isArrowFunction()) { - initializeArrowFunctionContextScopeIfNeeded(functionSymbolTable); - emitPutThisToArrowFunctionContextScope(); - emitPutNewTargetToArrowFunctionContextScope(); - emitPutDerivedConstructorToArrowFunctionContextScope(); + for (size_t i = 0; i < deconstructedParameters.size(); i++) { + auto& entry = deconstructedParameters[i]; + entry.second->bindValue(*this, entry.first); } - - pushLexicalScope(m_scopeNode, TDZCheckOptimization::Optimize); } -BytecodeGenerator::BytecodeGenerator(VM& vm, EvalNode* evalNode, UnlinkedEvalCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode, const VariableEnvironment* parentScopeTDZVariables) - : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn) - , m_shouldEmitProfileHooks(Options::forceProfilerBytecodeGeneration() || profilerMode == ProfilerOn) +BytecodeGenerator::BytecodeGenerator(VM& vm, EvalNode* evalNode, UnlinkedEvalCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode) + : m_shouldEmitDebugHooks(debuggerMode == DebuggerOn) + , m_shouldEmitProfileHooks(profilerMode == ProfilerOn) + , m_symbolTable(codeBlock->symbolTable()) , m_scopeNode(evalNode) , m_codeBlock(vm, codeBlock) , m_thisRegister(CallFrame::thisArgumentOffset()) + , m_emptyValueRegister(0) + , m_globalObjectRegister(0) + , m_finallyDepth(0) + , m_localScopeDepth(0) , m_codeType(EvalCode) + , m_nextConstantOffset(0) + , m_globalConstantIndex(0) + , m_hasCreatedActivation(true) + , m_firstLazyFunction(0) + , m_lastLazyFunction(0) + , m_staticPropertyAnalyzer(&m_instructions) , m_vm(&vm) - , m_usesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode()) - , m_needsToUpdateArrowFunctionContext(evalNode->usesArrowFunction() || evalNode->usesEval()) - , m_derivedContextType(codeBlock->derivedContextType()) + , m_lastOpcodeID(op_end) +#ifndef NDEBUG + , m_lastOpcodePosition(0) +#endif + , m_usesExceptions(false) + , m_expressionTooDeep(false) { - for (auto& constantRegister : m_linkTimeConstantRegisters) - constantRegister = nullptr; - - allocateCalleeSaveSpace(); + m_codeBlock->setNeedsFullScopeChain(true); + m_symbolTable->setUsesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode()); m_codeBlock->setNumParameters(1); - emitEnter(); - - allocateAndEmitScope(); + emitOpcode(op_enter); 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(); + const DeclarationStacks::VarStack& varStack = evalNode->varStack(); + unsigned numVariables = varStack.size(); Vector<Identifier, 0, UnsafeVectorOverflow> 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())); + for (size_t i = 0; i < numVariables; ++i) { + ASSERT(varStack[i].first.impl()->isIdentifier()); + variables.append(varStack[i].first); } codeBlock->adoptVariables(variables); - - m_TDZStack.append(std::make_pair(*parentScopeTDZVariables, false)); - - if (codeBlock->isArrowFunctionContext() && evalNode->usesThis()) - emitLoadThisFromArrowFunctionLexicalEnvironment(); - - if (needsToUpdateArrowFunctionContext() && !codeBlock->isArrowFunctionContext()) { - initializeArrowFunctionContextScopeIfNeeded(); - emitPutThisToArrowFunctionContextScope(); - } - - pushLexicalScope(m_scopeNode, TDZCheckOptimization::Optimize); + preserveLastVar(); } -BytecodeGenerator::BytecodeGenerator(VM& vm, ModuleProgramNode* moduleProgramNode, UnlinkedModuleProgramCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode, const VariableEnvironment* parentScopeTDZVariables) - : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn) - , m_shouldEmitProfileHooks(Options::forceProfilerBytecodeGeneration() || profilerMode == ProfilerOn) - , 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()) +BytecodeGenerator::~BytecodeGenerator() { - 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)); - - m_TDZStack.append(std::make_pair(lexicalVariables, true)); - m_symbolTableStack.append(SymbolTableStackEntry { Strong<SymbolTable>(*m_vm, moduleEnvironmentSymbolTable), m_topMostScope, false, 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() +RegisterID* BytecodeGenerator::emitInitLazyRegister(RegisterID* reg) { + emitOpcode(op_init_lazy_reg); + instructions().append(reg->index()); + ASSERT(!hasWatchableVariable(reg->index())); + return reg; } -void BytecodeGenerator::initializeDefaultParameterValuesAndSetupFunctionScopeStack( - FunctionParameters& parameters, FunctionNode* functionNode, SymbolTable* functionSymbolTable, - int symbolTableConstantIndex, const std::function<bool (UniquedStringImpl*)>& captures) +RegisterID* BytecodeGenerator::resolveCallee(FunctionBodyNode* functionBodyNode) { - Vector<std::pair<Identifier, RefPtr<RegisterID>>> valuesToMoveIntoVars; - if (parameters.hasDefaultParameterValues()) { - // 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<Identifier> allParameterNames; - for (unsigned i = 0; i < parameters.size(); i++) - parameters.at(i).first->collectBoundIdentifiers(allParameterNames); - 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); - - RefPtr<RegisterID> temp = newTemporary(); - for (unsigned i = 0; i < parameters.size(); i++) { - std::pair<DestructuringPatternNode*, ExpressionNode*> parameter = parameters.at(i); - if (parameter.first->isRestParameter()) - continue; - RefPtr<RegisterID> parameterValue = ®isterFor(virtualRegisterForArgument(1 + i)); - emitMove(temp.get(), parameterValue.get()); - if (parameter.second) { - RefPtr<RegisterID> condition = emitIsUndefined(newTemporary(), parameterValue.get()); - RefPtr<Label> skipDefaultParameterBecauseNotUndefined = newLabel(); - emitJumpIfFalse(condition.get(), skipDefaultParameterBecauseNotUndefined.get()); - emitNode(temp.get(), parameter.second); - emitLabel(skipDefaultParameterBecauseNotUndefined.get()); - } + if (functionBodyNode->ident().isNull() || !functionBodyNode->functionNameIsInScope()) + return 0; - parameter.first->bindValue(*this, temp.get()); - } - - // Final act of weirdness for default parameters. If a "var" also - // has the same name as a parameter, it should start out as the - // value of that parameter. Note, though, that they will be distinct - // bindings. - // This is step 28 of section 9.2.12. - for (auto& entry : functionNode->varDeclarations()) { - if (!entry.value.isVar()) // This is either a parameter or callee. - continue; - - if (parameterSet.contains(entry.key)) { - Identifier ident = Identifier::fromUid(m_vm, entry.key.get()); - Variable var = variable(ident); - RegisterID* scope = emitResolveScope(nullptr, var); - RefPtr<RegisterID> value = emitGetFromScope(newTemporary(), scope, var, DoNotThrowIfNotFound); - valuesToMoveIntoVars.append(std::make_pair(ident, value)); - } - } - - // Functions with default parameter expressions must have a separate environment - // record for parameters and "var"s. The "var" environment record must have the - // parameter environment record as its parent. - // See step 28 of section 9.2.12. - if (m_lexicalEnvironmentRegister) - initializeVarLexicalEnvironment(symbolTableConstantIndex); - } + m_calleeRegister.setIndex(JSStack::Callee); - if (m_lexicalEnvironmentRegister) - pushScopedControlFlowContext(); - m_symbolTableStack.append(SymbolTableStackEntry{ Strong<SymbolTable>(*m_vm, functionSymbolTable), m_lexicalEnvironmentRegister, false, symbolTableConstantIndex }); + // If non-strict eval is in play, we use a separate object in the scope chain for the callee's name. + if ((m_codeBlock->usesEval() && !m_codeBlock->isStrictMode()) || m_shouldEmitDebugHooks) + emitPushNameScope(functionBodyNode->ident(), &m_calleeRegister, ReadOnly | DontDelete); - // This completes step 28 of section 9.2.12. - for (unsigned i = 0; i < valuesToMoveIntoVars.size(); i++) { - ASSERT(parameters.hasDefaultParameterValues()); - Variable var = variable(valuesToMoveIntoVars[i].first); - RegisterID* scope = emitResolveScope(nullptr, var); - emitPutToScope(scope, var, valuesToMoveIntoVars[i].second.get(), DoNotThrowIfNotFound, NotInitialization); - } + if (!functionBodyNode->captures(functionBodyNode->ident())) + return &m_calleeRegister; - if (!parameters.hasDefaultParameterValues()) { - ASSERT(!valuesToMoveIntoVars.size()); - // Initialize destructuring parameters the old way as if we don't have any default parameter values. - // If we have default parameter values, we handle this case above. - for (unsigned i = 0; i < parameters.size(); i++) { - DestructuringPatternNode* pattern = parameters.at(i).first; - if (!pattern->isBindingNode() && !pattern->isRestParameter()) { - RefPtr<RegisterID> parameterValue = ®isterFor(virtualRegisterForArgument(1 + i)); - pattern->bindValue(*this, parameterValue.get()); - } - } - } + // Move the callee into the captured section of the stack. + return emitMove(addVar(), IsCaptured, &m_calleeRegister); } -void BytecodeGenerator::initializeArrowFunctionContextScopeIfNeeded(SymbolTable* symbolTable) +void BytecodeGenerator::addCallee(FunctionBodyNode* functionBodyNode, RegisterID* calleeRegister) { - if (m_arrowFunctionContextLexicalEnvironmentRegister != nullptr) + if (functionBodyNode->ident().isNull() || !functionBodyNode->functionNameIsInScope()) return; - - if (m_lexicalEnvironmentRegister != nullptr) { - m_arrowFunctionContextLexicalEnvironmentRegister = m_lexicalEnvironmentRegister; - - if (!m_codeBlock->isArrowFunction()) { - ScopeOffset offset; - - offset = symbolTable->takeNextScopeOffset(); - symbolTable->set(propertyNames().thisIdentifier.impl(), SymbolTableEntry(VarOffset(offset))); - - if (m_codeType == FunctionCode) { - offset = symbolTable->takeNextScopeOffset(); - symbolTable->set(propertyNames().newTargetLocalPrivateName.impl(), SymbolTableEntry(VarOffset(offset))); - } - - if (isConstructor() && constructorKind() == ConstructorKind::Derived) { - offset = symbolTable->takeNextScopeOffset(); - symbolTable->set(propertyNames().derivedConstructorPrivateName.impl(), SymbolTableEntry(VarOffset(offset))); - } - } + // If non-strict eval is in play, we use a separate object in the scope chain for the callee's name. + if ((m_codeBlock->usesEval() && !m_codeBlock->isStrictMode()) || m_shouldEmitDebugHooks) return; - } - VariableEnvironment environment; - auto addResult = environment.add(propertyNames().thisIdentifier); - addResult.iterator->value.setIsCaptured(); - addResult.iterator->value.setIsConst(); - - if (m_codeType == FunctionCode) { - auto addTarget = environment.add(propertyNames().newTargetLocalPrivateName); - addTarget.iterator->value.setIsCaptured(); - addTarget.iterator->value.setIsLet(); - } - - if (isConstructor() && constructorKind() == ConstructorKind::Derived) { - auto derivedConstructor = environment.add(propertyNames().derivedConstructorPrivateName); - derivedConstructor.iterator->value.setIsCaptured(); - derivedConstructor.iterator->value.setIsLet(); - } - - size_t size = m_symbolTableStack.size(); - pushLexicalScopeInternal(environment, TDZCheckOptimization::Optimize, NestedScopeType::IsNotNested, nullptr, TDZRequirement::UnderTDZ, ScopeType::LetConstScope, ScopeRegisterType::Block); - - ASSERT_UNUSED(size, m_symbolTableStack.size() == size + 1); - - m_arrowFunctionContextLexicalEnvironmentRegister = m_symbolTableStack.last().m_scope; + ASSERT(calleeRegister); + symbolTable().add(functionBodyNode->ident().impl(), SymbolTableEntry(calleeRegister->index(), ReadOnly)); } -RegisterID* BytecodeGenerator::initializeNextParameter() +void BytecodeGenerator::addParameter(const Identifier& ident, int parameterIndex) { - VirtualRegister reg = virtualRegisterForArgument(m_codeBlock->numParameters()); - RegisterID& parameter = registerFor(reg); - parameter.setIndex(reg.offset()); + // Parameters overwrite var declarations, but not function declarations. + StringImpl* rep = ident.impl(); + if (!m_functions.contains(rep)) { + symbolTable().set(rep, parameterIndex); + RegisterID& parameter = registerFor(parameterIndex); + parameter.setIndex(parameterIndex); + } + + // To maintain the calling convention, we have to allocate unique space for + // each parameter, even if the parameter doesn't make it into the symbol table. m_codeBlock->addParameter(); - return ¶meter; } -void BytecodeGenerator::initializeParameters(FunctionParameters& parameters) +bool BytecodeGenerator::willResolveToArguments(const Identifier& ident) { - // Make sure the code block knows about all of our parameters, and make sure that parameters - // needing destructuring are noted. - m_parameters.grow(parameters.size() + 1); // reserve space for "this" - m_thisRegister.setIndex(initializeNextParameter()->index()); // this - for (unsigned i = 0; i < parameters.size(); ++i) { - auto pattern = parameters.at(i).first; - if (pattern->isRestParameter()) { - RELEASE_ASSERT(!m_restParameter); - m_restParameter = static_cast<RestParameterNode*>(pattern); - } else - initializeNextParameter(); - } -} + if (ident != propertyNames().arguments) + return false; + + if (!shouldOptimizeLocals()) + return false; + + SymbolTableEntry entry = symbolTable().get(ident.impl()); + if (entry.isNull()) + return false; -void BytecodeGenerator::initializeVarLexicalEnvironment(int symbolTableConstantIndex) -{ - RELEASE_ASSERT(m_lexicalEnvironmentRegister); - emitOpcode(op_create_lexical_environment); - instructions().append(m_lexicalEnvironmentRegister->index()); - instructions().append(scopeRegister()->index()); - instructions().append(symbolTableConstantIndex); - instructions().append(addConstantValue(jsUndefined())->index()); - - emitOpcode(op_mov); - instructions().append(scopeRegister()->index()); - instructions().append(m_lexicalEnvironmentRegister->index()); + if (m_codeBlock->usesArguments() && m_codeType == FunctionCode) + return true; + + return false; } -UniquedStringImpl* BytecodeGenerator::visibleNameForParameter(DestructuringPatternNode* pattern) +RegisterID* BytecodeGenerator::uncheckedRegisterForArguments() { - if (pattern->isBindingNode()) { - const Identifier& ident = static_cast<const BindingNode*>(pattern)->boundProperty(); - if (!m_functions.contains(ident.impl())) - return ident.impl(); - } - return nullptr; -} + ASSERT(willResolveToArguments(propertyNames().arguments)); -RegisterID* BytecodeGenerator::newRegister() -{ - m_calleeLocals.append(virtualRegisterForLocal(m_calleeLocals.size())); - int numCalleeLocals = max<int>(m_codeBlock->m_numCalleeLocals, m_calleeLocals.size()); - numCalleeLocals = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), numCalleeLocals); - m_codeBlock->m_numCalleeLocals = numCalleeLocals; - return &m_calleeLocals.last(); + SymbolTableEntry entry = symbolTable().get(propertyNames().arguments.impl()); + ASSERT(!entry.isNull()); + return ®isterFor(entry.getIndex()); } -void BytecodeGenerator::reclaimFreeRegisters() +RegisterID* BytecodeGenerator::createLazyRegisterIfNecessary(RegisterID* reg) { - while (m_calleeLocals.size() && !m_calleeLocals.last().refCount()) - m_calleeLocals.removeLast(); + if (!reg->virtualRegister().isLocal()) + return reg; + + int localVariableNumber = reg->virtualRegister().toLocal(); + + if (m_lastLazyFunction <= localVariableNumber || localVariableNumber < m_firstLazyFunction) + return reg; + emitLazyNewFunction(reg, m_lazyFunctions.get(localVariableNumber)); + return reg; } -RegisterID* BytecodeGenerator::newBlockScopeVariable() +RegisterID* BytecodeGenerator::newRegister() { - reclaimFreeRegisters(); - - return newRegister(); + m_calleeRegisters.append(virtualRegisterForLocal(m_calleeRegisters.size())); + int numCalleeRegisters = max<int>(m_codeBlock->m_numCalleeRegisters, m_calleeRegisters.size()); + numCalleeRegisters = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), numCalleeRegisters); + m_codeBlock->m_numCalleeRegisters = numCalleeRegisters; + return &m_calleeRegisters.last(); } RegisterID* BytecodeGenerator::newTemporary() { - reclaimFreeRegisters(); - + // Reclaim free register IDs. + while (m_calleeRegisters.size() && !m_calleeRegisters.last().refCount()) + m_calleeRegisters.removeLast(); + RegisterID* result = newRegister(); result->setTemporary(); return result; @@ -1018,9 +587,9 @@ LabelScopePtr BytecodeGenerator::newLabelScope(LabelScope::Type type, const Iden m_labelScopes.removeLast(); // Allocate new label scope. - LabelScope scope(type, name, labelScopeDepth(), newLabel(), type == LabelScope::Loop ? newLabel() : PassRefPtr<Label>()); // Only loops have continue targets. + LabelScope scope(type, name, scopeDepth(), newLabel(), type == LabelScope::Loop ? newLabel() : PassRefPtr<Label>()); // Only loops have continue targets. m_labelScopes.append(scope); - return LabelScopePtr(m_labelScopes, m_labelScopes.size() - 1); + return LabelScopePtr(&m_labelScopes, m_labelScopes.size() - 1); } PassRefPtr<Label> BytecodeGenerator::newLabel() @@ -1030,7 +599,7 @@ PassRefPtr<Label> BytecodeGenerator::newLabel() m_labels.removeLast(); // Allocate new label ID. - m_labels.append(*this); + m_labels.append(this); return &m_labels.last(); } @@ -1088,22 +657,9 @@ UnlinkedValueProfile BytecodeGenerator::emitProfiledOpcode(OpcodeID opcodeID) return result; } -void BytecodeGenerator::emitEnter() -{ - emitOpcode(op_enter); - emitWatchdog(); -} - void BytecodeGenerator::emitLoopHint() { emitOpcode(op_loop_hint); - emitWatchdog(); -} - -void BytecodeGenerator::emitWatchdog() -{ - if (vm()->watchdog()) - emitOpcode(op_watchdog); } void BytecodeGenerator::retrieveLastBinaryOp(int& dstIndex, int& src1Index, int& src2Index) @@ -1401,15 +957,9 @@ PassRefPtr<Label> BytecodeGenerator::emitJumpIfNotFunctionApply(RegisterID* cond return target; } -bool BytecodeGenerator::hasConstant(const Identifier& ident) const -{ - UniquedStringImpl* rep = ident.impl(); - return m_identifierMap.contains(rep); -} - unsigned BytecodeGenerator::addConstant(const Identifier& ident) { - UniquedStringImpl* rep = ident.impl(); + StringImpl* rep = ident.impl(); IdentifierMap::AddResult result = m_identifierMap.add(rep, m_codeBlock->numberOfIdentifiers()); if (result.isNewEntry) m_codeBlock->addIdentifier(ident); @@ -1431,69 +981,42 @@ RegisterID* BytecodeGenerator::addConstantEmptyValue() return m_emptyValueRegister; } -RegisterID* BytecodeGenerator::addConstantValue(JSValue v, SourceCodeRepresentation sourceCodeRepresentation) +RegisterID* BytecodeGenerator::addConstantValue(JSValue v) { if (!v) return addConstantEmptyValue(); int index = m_nextConstantOffset; - - if (sourceCodeRepresentation == SourceCodeRepresentation::Double && v.isInt32()) - v = jsDoubleNumber(v.asNumber()); - EncodedJSValueWithRepresentation valueMapKey { JSValue::encode(v), sourceCodeRepresentation }; - JSValueMap::AddResult result = m_jsValueMap.add(valueMapKey, m_nextConstantOffset); + JSValueMap::AddResult result = m_jsValueMap.add(JSValue::encode(v), m_nextConstantOffset); if (result.isNewEntry) { m_constantPoolRegisters.append(FirstConstantRegisterIndex + m_nextConstantOffset); ++m_nextConstantOffset; - m_codeBlock->addConstant(v, sourceCodeRepresentation); + m_codeBlock->addConstant(v); } else index = result.iterator->value; return &m_constantPoolRegisters[index]; } -RegisterID* BytecodeGenerator::emitMoveLinkTimeConstant(RegisterID* dst, LinkTimeConstant type) -{ - unsigned constantIndex = static_cast<unsigned>(type); - if (!m_linkTimeConstantRegisters[constantIndex]) { - int index = m_nextConstantOffset; - m_constantPoolRegisters.append(FirstConstantRegisterIndex + m_nextConstantOffset); - ++m_nextConstantOffset; - m_codeBlock->addConstant(type); - m_linkTimeConstantRegisters[constantIndex] = &m_constantPoolRegisters[index]; - } - - emitOpcode(op_mov); - instructions().append(dst->index()); - instructions().append(m_linkTimeConstantRegisters[constantIndex]->index()); - - return dst; -} - unsigned BytecodeGenerator::addRegExp(RegExp* r) { return m_codeBlock->addRegExp(r); } -RegisterID* BytecodeGenerator::emitMoveEmptyValue(RegisterID* dst) +RegisterID* BytecodeGenerator::emitMove(RegisterID* dst, CaptureMode captureMode, RegisterID* src) { - RefPtr<RegisterID> emptyValue = addConstantEmptyValue(); + m_staticPropertyAnalyzer.mov(dst->index(), src->index()); - emitOpcode(op_mov); + emitOpcode(captureMode == IsCaptured ? op_captured_mov : op_mov); instructions().append(dst->index()); - instructions().append(emptyValue->index()); + instructions().append(src->index()); + if (captureMode == IsCaptured) + instructions().append(watchableVariable(dst->index())); return dst; } RegisterID* BytecodeGenerator::emitMove(RegisterID* dst, RegisterID* src) { - ASSERT(src != m_emptyValueRegister); - - m_staticPropertyAnalyzer.mov(dst->index(), src->index()); - emitOpcode(op_mov); - instructions().append(dst->index()); - instructions().append(src->index()); - - return dst; + return emitMove(dst, captureMode(dst->index()), src); } RegisterID* BytecodeGenerator::emitUnaryOp(OpcodeID opcodeID, RegisterID* dst, RegisterID* src) @@ -1575,7 +1098,7 @@ RegisterID* BytecodeGenerator::emitEqualityOp(OpcodeID opcodeID, RegisterID* dst } if (value == "object") { rewindUnaryOp(); - emitOpcode(op_is_object_or_null); + emitOpcode(op_is_object); instructions().append(dst->index()); instructions().append(srcIndex); return dst; @@ -1597,104 +1120,22 @@ RegisterID* BytecodeGenerator::emitEqualityOp(OpcodeID opcodeID, RegisterID* dst return dst; } -void BytecodeGenerator::emitTypeProfilerExpressionInfo(const JSTextPosition& startDivot, const JSTextPosition& endDivot) -{ - ASSERT(vm()->typeProfiler()); - - unsigned start = startDivot.offset; // Ranges are inclusive of their endpoints, AND 0 indexed. - unsigned end = endDivot.offset - 1; // End Ranges already go one past the inclusive range, so subtract 1. - unsigned instructionOffset = instructions().size() - 1; - m_codeBlock->addTypeProfilerExpressionInfo(instructionOffset, start, end); -} - -void BytecodeGenerator::emitProfileType(RegisterID* registerToProfile, ProfileTypeBytecodeFlag flag) -{ - if (!vm()->typeProfiler()) - return; - - if (!registerToProfile) - return; - - emitOpcode(op_profile_type); - instructions().append(registerToProfile->index()); - instructions().append(0); - instructions().append(flag); - instructions().append(0); - instructions().append(resolveType()); - - // Don't emit expression info for this version of profile type. This generally means - // we're profiling information for something that isn't in the actual text of a JavaScript - // program. For example, implicit return undefined from a function call. -} - -void BytecodeGenerator::emitProfileType(RegisterID* registerToProfile, const JSTextPosition& startDivot, const JSTextPosition& endDivot) -{ - emitProfileType(registerToProfile, ProfileTypeBytecodeDoesNotHaveGlobalID, startDivot, endDivot); -} - -void BytecodeGenerator::emitProfileType(RegisterID* registerToProfile, ProfileTypeBytecodeFlag flag, const JSTextPosition& startDivot, const JSTextPosition& endDivot) -{ - if (!vm()->typeProfiler()) - return; - - if (!registerToProfile) - return; - - // The format of this instruction is: op_profile_type regToProfile, TypeLocation*, flag, identifier?, resolveType? - emitOpcode(op_profile_type); - instructions().append(registerToProfile->index()); - instructions().append(0); - instructions().append(flag); - instructions().append(0); - instructions().append(resolveType()); - - emitTypeProfilerExpressionInfo(startDivot, endDivot); -} - -void BytecodeGenerator::emitProfileType(RegisterID* registerToProfile, const Variable& var, const JSTextPosition& startDivot, const JSTextPosition& endDivot) -{ - if (!vm()->typeProfiler()) - return; - - if (!registerToProfile) - return; - - ProfileTypeBytecodeFlag flag; - int symbolTableOrScopeDepth; - if (var.local() || var.offset().isScope()) { - flag = ProfileTypeBytecodeLocallyResolved; - symbolTableOrScopeDepth = var.symbolTableConstantIndex(); - } else { - flag = ProfileTypeBytecodeClosureVar; - symbolTableOrScopeDepth = localScopeDepth(); - } - - // The format of this instruction is: op_profile_type regToProfile, TypeLocation*, flag, identifier?, resolveType? - emitOpcode(op_profile_type); - instructions().append(registerToProfile->index()); - instructions().append(symbolTableOrScopeDepth); - instructions().append(flag); - instructions().append(addConstant(var.ident())); - instructions().append(resolveType()); - - emitTypeProfilerExpressionInfo(startDivot, endDivot); -} - -void BytecodeGenerator::emitProfileControlFlow(int textOffset) +RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, bool b) { - if (vm()->controlFlowProfiler()) { - RELEASE_ASSERT(textOffset >= 0); - size_t bytecodeOffset = instructions().size(); - m_codeBlock->addOpProfileControlFlowBytecodeOffset(bytecodeOffset); - - emitOpcode(op_profile_control_flow); - instructions().append(textOffset); - } + return emitLoad(dst, jsBoolean(b)); } -RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, bool b) +RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, double number) { - return emitLoad(dst, jsBoolean(b)); + // FIXME: Our hash tables won't hold infinity, so we make a new JSValue each time. + // Later we can do the extra work to handle that like the other cases. They also don't + // work correctly with NaN as a key. + if (std::isnan(number) || number == HashTraits<double>::emptyValue() || HashTraits<double>::isDeletedValue(number)) + return emitLoad(dst, jsNumber(number)); + JSValue& valueInMap = m_numberMap.add(number, JSValue()).iterator->value; + if (!valueInMap) + valueInMap = jsNumber(number); + return emitLoad(dst, valueInMap); } RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, const Identifier& identifier) @@ -1705,9 +1146,9 @@ RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, const Identifier& ident return emitLoad(dst, JSValue(stringInMap)); } -RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, JSValue v, SourceCodeRepresentation sourceCodeRepresentation) +RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, JSValue v) { - RegisterID* constantID = addConstantValue(v, sourceCodeRepresentation); + RegisterID* constantID = addConstantValue(v); if (dst) return emitMove(dst, constantID); return constantID; @@ -1728,543 +1169,113 @@ RegisterID* BytecodeGenerator::emitLoadGlobalObject(RegisterID* dst) return m_globalObjectRegister; } -template<typename LookUpVarKindFunctor> -bool BytecodeGenerator::instantiateLexicalVariables(const VariableEnvironment& lexicalVariables, SymbolTable* symbolTable, ScopeRegisterType scopeRegisterType, LookUpVarKindFunctor lookUpVarKind) +bool BytecodeGenerator::isCaptured(int operand) { - bool hasCapturedVariables = false; - { - ConcurrentJITLocker locker(symbolTable->m_lock); - for (auto& entry : lexicalVariables) { - ASSERT(entry.value.isLet() || entry.value.isConst()); - ASSERT(!entry.value.isVar()); - SymbolTableEntry symbolTableEntry = symbolTable->get(locker, entry.key.get()); - ASSERT(symbolTableEntry.isNull()); - - // Imported bindings which are not the namespace bindings are not allocated - // in the module environment as usual variables' way. - // And since these types of the variables only seen in the module environment, - // other lexical environment need not to take care this. - if (entry.value.isImported() && !entry.value.isImportedNamespace()) - continue; - - VarKind varKind = lookUpVarKind(entry.key.get(), entry.value); - VarOffset varOffset; - if (varKind == VarKind::Scope) { - varOffset = VarOffset(symbolTable->takeNextScopeOffset(locker)); - hasCapturedVariables = true; - } else { - ASSERT(varKind == VarKind::Stack); - RegisterID* local; - if (scopeRegisterType == ScopeRegisterType::Block) { - local = newBlockScopeVariable(); - local->ref(); - } else - local = addVar(); - varOffset = VarOffset(local->virtualRegister()); - } - - SymbolTableEntry newEntry(varOffset, entry.value.isConst() ? ReadOnly : 0); - symbolTable->add(locker, entry.key.get(), newEntry); - } - } - return hasCapturedVariables; + return m_symbolTable && m_symbolTable->isCaptured(operand); } -void BytecodeGenerator::emitPrefillStackTDZVariables(const VariableEnvironment& lexicalVariables, SymbolTable* symbolTable) +Local BytecodeGenerator::local(const Identifier& property) { - // Prefill stack variables with the TDZ empty value. - // Scope variables will be initialized to the TDZ empty value when JSLexicalEnvironment is allocated. - for (auto& entry : lexicalVariables) { - // Imported bindings which are not the namespace bindings are not allocated - // in the module environment as usual variables' way. - // And since these types of the variables only seen in the module environment, - // other lexical environment need not to take care this. - if (entry.value.isImported() && !entry.value.isImportedNamespace()) - continue; - - SymbolTableEntry symbolTableEntry = symbolTable->get(entry.key.get()); - ASSERT(!symbolTableEntry.isNull()); - VarOffset offset = symbolTableEntry.varOffset(); - if (offset.isScope()) - continue; - - ASSERT(offset.isStack()); - emitMoveEmptyValue(®isterFor(offset.stackOffset())); - } -} - -void BytecodeGenerator::pushLexicalScope(VariableEnvironmentNode* node, TDZCheckOptimization tdzCheckOptimization, NestedScopeType nestedScopeType, RegisterID** constantSymbolTableResult) -{ - VariableEnvironment& environment = node->lexicalVariables(); - pushLexicalScopeInternal(environment, tdzCheckOptimization, nestedScopeType, constantSymbolTableResult, TDZRequirement::UnderTDZ, ScopeType::LetConstScope, ScopeRegisterType::Block); -} - -void BytecodeGenerator::pushLexicalScopeInternal(VariableEnvironment& environment, TDZCheckOptimization tdzCheckOptimization, NestedScopeType nestedScopeType, - RegisterID** constantSymbolTableResult, TDZRequirement tdzRequirement, ScopeType scopeType, ScopeRegisterType scopeRegisterType) -{ - if (!environment.size()) - return; - - if (m_shouldEmitDebugHooks) - environment.markAllVariablesAsCaptured(); - - Strong<SymbolTable> symbolTable(*m_vm, SymbolTable::create(*m_vm)); - switch (scopeType) { - case ScopeType::CatchScope: - symbolTable->setScopeType(SymbolTable::ScopeType::CatchScope); - break; - case ScopeType::LetConstScope: - symbolTable->setScopeType(SymbolTable::ScopeType::LexicalScope); - break; - case ScopeType::FunctionNameScope: - symbolTable->setScopeType(SymbolTable::ScopeType::FunctionNameScope); - break; - } - - if (nestedScopeType == NestedScopeType::IsNested) - symbolTable->markIsNestedLexicalScope(); - - auto lookUpVarKind = [] (UniquedStringImpl*, const VariableEnvironmentEntry& entry) -> VarKind { - return entry.isCaptured() ? VarKind::Scope : VarKind::Stack; - }; - - bool hasCapturedVariables = instantiateLexicalVariables(environment, symbolTable.get(), scopeRegisterType, lookUpVarKind); - - RegisterID* newScope = nullptr; - RegisterID* constantSymbolTable = nullptr; - int symbolTableConstantIndex = 0; - if (vm()->typeProfiler()) { - constantSymbolTable = addConstantValue(symbolTable.get()); - symbolTableConstantIndex = constantSymbolTable->index(); - } - if (hasCapturedVariables) { - if (scopeRegisterType == ScopeRegisterType::Block) { - newScope = newBlockScopeVariable(); - newScope->ref(); - } else - newScope = addVar(); - if (!constantSymbolTable) { - ASSERT(!vm()->typeProfiler()); - constantSymbolTable = addConstantValue(symbolTable->cloneScopePart(*m_vm)); - symbolTableConstantIndex = constantSymbolTable->index(); - } - if (constantSymbolTableResult) - *constantSymbolTableResult = constantSymbolTable; - - emitOpcode(op_create_lexical_environment); - instructions().append(newScope->index()); - instructions().append(scopeRegister()->index()); - instructions().append(constantSymbolTable->index()); - instructions().append(addConstantValue(tdzRequirement == TDZRequirement::UnderTDZ ? jsTDZValue() : jsUndefined())->index()); - - emitMove(scopeRegister(), newScope); - - pushScopedControlFlowContext(); - } - - m_symbolTableStack.append(SymbolTableStackEntry{ symbolTable, newScope, false, symbolTableConstantIndex }); - bool canOptimizeTDZChecks = tdzCheckOptimization == TDZCheckOptimization::Optimize; - if (tdzRequirement == TDZRequirement::UnderTDZ) - m_TDZStack.append(std::make_pair(environment, canOptimizeTDZChecks)); - - if (tdzRequirement == TDZRequirement::UnderTDZ) - emitPrefillStackTDZVariables(environment, symbolTable.get()); -} - -void BytecodeGenerator::popLexicalScope(VariableEnvironmentNode* node) -{ - VariableEnvironment& environment = node->lexicalVariables(); - popLexicalScopeInternal(environment, TDZRequirement::UnderTDZ); -} - -void BytecodeGenerator::popLexicalScopeInternal(VariableEnvironment& environment, TDZRequirement tdzRequirement) -{ - // NOTE: This function only makes sense for scopes that aren't ScopeRegisterType::Var (only function name scope right now is ScopeRegisterType::Var). - // This doesn't make sense for ScopeRegisterType::Var because we deref RegisterIDs here. - if (!environment.size()) - return; - - if (m_shouldEmitDebugHooks) - environment.markAllVariablesAsCaptured(); - - SymbolTableStackEntry stackEntry = m_symbolTableStack.takeLast(); - Strong<SymbolTable> symbolTable = stackEntry.m_symbolTable; - ConcurrentJITLocker locker(symbolTable->m_lock); - bool hasCapturedVariables = false; - for (auto& entry : environment) { - if (entry.value.isCaptured()) { - hasCapturedVariables = true; - continue; - } - SymbolTableEntry symbolTableEntry = symbolTable->get(locker, entry.key.get()); - ASSERT(!symbolTableEntry.isNull()); - VarOffset offset = symbolTableEntry.varOffset(); - ASSERT(offset.isStack()); - RegisterID* local = ®isterFor(offset.stackOffset()); - local->deref(); - } - - if (hasCapturedVariables) { - RELEASE_ASSERT(stackEntry.m_scope); - emitPopScope(scopeRegister(), stackEntry.m_scope); - popScopedControlFlowContext(); - stackEntry.m_scope->deref(); - } - - if (tdzRequirement == TDZRequirement::UnderTDZ) - m_TDZStack.removeLast(); -} - -void BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration(VariableEnvironmentNode* node, RegisterID* loopSymbolTable) -{ - VariableEnvironment& environment = node->lexicalVariables(); - if (!environment.size()) - return; - if (m_shouldEmitDebugHooks) - environment.markAllVariablesAsCaptured(); - if (!environment.hasCapturedVariables()) - return; - - RELEASE_ASSERT(loopSymbolTable); - - // This function needs to do setup for a for loop's activation if any of - // the for loop's lexically declared variables are captured (that is, variables - // declared in the loop header, not the loop body). This function needs to - // make a copy of the current activation and copy the values from the previous - // activation into the new activation because each iteration of a for loop - // gets a new activation. - - SymbolTableStackEntry stackEntry = m_symbolTableStack.last(); - Strong<SymbolTable> symbolTable = stackEntry.m_symbolTable; - RegisterID* loopScope = stackEntry.m_scope; - ASSERT(symbolTable->scopeSize()); - ASSERT(loopScope); - Vector<std::pair<RegisterID*, Identifier>> activationValuesToCopyOver; - - { - ConcurrentJITLocker locker(symbolTable->m_lock); - activationValuesToCopyOver.reserveInitialCapacity(symbolTable->scopeSize()); - - for (auto end = symbolTable->end(locker), ptr = symbolTable->begin(locker); ptr != end; ++ptr) { - if (!ptr->value.varOffset().isScope()) - continue; - - RefPtr<UniquedStringImpl> ident = ptr->key; - Identifier identifier = Identifier::fromUid(m_vm, ident.get()); - - RegisterID* transitionValue = newBlockScopeVariable(); - transitionValue->ref(); - emitGetFromScope(transitionValue, loopScope, variableForLocalEntry(identifier, ptr->value, loopSymbolTable->index(), true), DoNotThrowIfNotFound); - activationValuesToCopyOver.uncheckedAppend(std::make_pair(transitionValue, identifier)); - } - } - - // We need this dynamic behavior of the executing code to ensure - // each loop iteration has a new activation object. (It's pretty ugly). - // Also, this new activation needs to be assigned to the same register - // as the previous scope because the loop body is compiled under - // the assumption that the scope's register index is constant even - // though the value in that register will change on each loop iteration. - RefPtr<RegisterID> parentScope = emitGetParentScope(newTemporary(), loopScope); - emitMove(scopeRegister(), parentScope.get()); - - emitOpcode(op_create_lexical_environment); - instructions().append(loopScope->index()); - instructions().append(scopeRegister()->index()); - instructions().append(loopSymbolTable->index()); - instructions().append(addConstantValue(jsTDZValue())->index()); - - emitMove(scopeRegister(), loopScope); - - { - ConcurrentJITLocker locker(symbolTable->m_lock); - for (auto pair : activationValuesToCopyOver) { - const Identifier& identifier = pair.second; - SymbolTableEntry entry = symbolTable->get(locker, identifier.impl()); - RELEASE_ASSERT(!entry.isNull()); - RegisterID* transitionValue = pair.first; - emitPutToScope(loopScope, variableForLocalEntry(identifier, entry, loopSymbolTable->index(), true), transitionValue, DoNotThrowIfNotFound, NotInitialization); - transitionValue->deref(); - } - } -} - -Variable BytecodeGenerator::variable(const Identifier& property, ThisResolutionType thisResolutionType) -{ - if (property == propertyNames().thisIdentifier && thisResolutionType == ThisResolutionType::Local) { - return Variable(property, VarOffset(thisRegister()->virtualRegister()), thisRegister(), - ReadOnly, Variable::SpecialVariable, 0, false); - } + if (property == propertyNames().thisIdentifier) + return Local(thisRegister(), ReadOnly, NotCaptured); - // We can optimize lookups if the lexical variable is found before a "with" or "catch" - // scope because we're guaranteed static resolution. If we have to pass through - // a "with" or "catch" scope we loose this guarantee. - // We can't optimize cases like this: - // { - // let x = ...; - // with (o) { - // doSomethingWith(x); - // } - // } - // Because we can't gaurantee static resolution on x. - // But, in this case, we are guaranteed static resolution: - // { - // let x = ...; - // with (o) { - // let x = ...; - // doSomethingWith(x); - // } - // } - for (unsigned i = m_symbolTableStack.size(); i--; ) { - SymbolTableStackEntry& stackEntry = m_symbolTableStack[i]; - if (stackEntry.m_isWithScope) - return Variable(property); - Strong<SymbolTable>& symbolTable = stackEntry.m_symbolTable; - SymbolTableEntry symbolTableEntry = symbolTable->get(property.impl()); - if (symbolTableEntry.isNull()) - continue; - bool resultIsCallee = false; - if (symbolTable->scopeType() == SymbolTable::ScopeType::FunctionNameScope) { - if (m_usesNonStrictEval) { - // We don't know if an eval has introduced a "var" named the same thing as the function name scope variable name. - // We resort to dynamic lookup to answer this question. - Variable result = Variable(property); - return result; - } - resultIsCallee = true; - } - Variable result = variableForLocalEntry(property, symbolTableEntry, stackEntry.m_symbolTableConstantIndex, symbolTable->scopeType() == SymbolTable::ScopeType::LexicalScope); - if (resultIsCallee) - result.setIsReadOnly(); - return result; - } + if (property == propertyNames().arguments) + createArgumentsIfNecessary(); - return Variable(property); -} + if (!shouldOptimizeLocals()) + return Local(); -Variable BytecodeGenerator::variableForLocalEntry( - const Identifier& property, const SymbolTableEntry& entry, int symbolTableConstantIndex, bool isLexicallyScoped) -{ - VarOffset offset = entry.varOffset(); - - RegisterID* local; - if (offset.isStack()) - local = ®isterFor(offset.stackOffset()); - else - local = nullptr; - - return Variable(property, offset, local, entry.getAttributes(), Variable::NormalVariable, symbolTableConstantIndex, isLexicallyScoped); + SymbolTableEntry entry = symbolTable().get(property.impl()); + if (entry.isNull()) + return Local(); + + RegisterID* local = createLazyRegisterIfNecessary(®isterFor(entry.getIndex())); + return Local(local, entry.getAttributes(), captureMode(local->index())); } -void BytecodeGenerator::createVariable( - const Identifier& property, VarKind varKind, SymbolTable* symbolTable, ExistingVariableMode existingVariableMode) +Local BytecodeGenerator::constLocal(const Identifier& property) { - ASSERT(property != propertyNames().thisIdentifier); - ConcurrentJITLocker locker(symbolTable->m_lock); - SymbolTableEntry entry = symbolTable->get(locker, property.impl()); - - if (!entry.isNull()) { - if (existingVariableMode == IgnoreExisting) - return; - - // Do some checks to ensure that the variable we're being asked to create is sufficiently - // compatible with the one we have already created. + if (m_codeType != FunctionCode) + return Local(); - VarOffset offset = entry.varOffset(); - - // We can't change our minds about whether it's captured. - if (offset.kind() != varKind) { - dataLog( - "Trying to add variable called ", property, " as ", varKind, - " but it was already added as ", offset, ".\n"); - RELEASE_ASSERT_NOT_REACHED(); - } + SymbolTableEntry entry = symbolTable().get(property.impl()); + if (entry.isNull()) + return Local(); - return; - } - - VarOffset varOffset; - if (varKind == VarKind::Scope) - varOffset = VarOffset(symbolTable->takeNextScopeOffset(locker)); - else { - ASSERT(varKind == VarKind::Stack); - varOffset = VarOffset(virtualRegisterForLocal(m_calleeLocals.size())); - } - SymbolTableEntry newEntry(varOffset, 0); - symbolTable->add(locker, property.impl(), newEntry); - - if (varKind == VarKind::Stack) { - RegisterID* local = addVar(); - RELEASE_ASSERT(local->index() == varOffset.stackOffset().offset()); - } + RegisterID* local = createLazyRegisterIfNecessary(®isterFor(entry.getIndex())); + return Local(local, entry.getAttributes(), captureMode(local->index())); } -RegisterID* BytecodeGenerator::emitOverridesHasInstance(RegisterID* dst, RegisterID* constructor, RegisterID* hasInstanceValue) +void BytecodeGenerator::emitCheckHasInstance(RegisterID* dst, RegisterID* value, RegisterID* base, Label* target) { - emitOpcode(op_overrides_has_instance); + size_t begin = instructions().size(); + emitOpcode(op_check_has_instance); instructions().append(dst->index()); - instructions().append(constructor->index()); - instructions().append(hasInstanceValue->index()); - return dst; + instructions().append(value->index()); + instructions().append(base->index()); + instructions().append(target->bind(begin, instructions().size())); } // Indicates the least upper bound of resolve type based on local scope. The bytecode linker // will start with this ResolveType and compute the least upper bound including intercepting scopes. ResolveType BytecodeGenerator::resolveType() { - for (unsigned i = m_symbolTableStack.size(); i--; ) { - if (m_symbolTableStack[i].m_isWithScope) - return Dynamic; - if (m_usesNonStrictEval && m_symbolTableStack[i].m_symbolTable->scopeType() == SymbolTable::ScopeType::FunctionNameScope) { - // We never want to assign to a FunctionNameScope. Returning Dynamic here achieves this goal. - // If we aren't in non-strict eval mode, then NodesCodeGen needs to take care not to emit - // a put_to_scope with the destination being the function name scope variable. - return Dynamic; - } - } - - if (m_usesNonStrictEval) + if (m_localScopeDepth) + return Dynamic; + if (m_symbolTable && m_symbolTable->usesNonStrictEval()) return GlobalPropertyWithVarInjectionChecks; return GlobalProperty; } -RegisterID* BytecodeGenerator::emitResolveScope(RegisterID* dst, const Variable& variable) +RegisterID* BytecodeGenerator::emitResolveScope(RegisterID* dst, const Identifier& identifier) { - switch (variable.offset().kind()) { - case VarKind::Stack: - return nullptr; - - case VarKind::DirectArgument: - return argumentsRegister(); - - case VarKind::Scope: - // This always refers to the activation that *we* allocated, and not the current scope that code - // lives in. Note that this will change once we have proper support for block scoping. Once that - // changes, it will be correct for this code to return scopeRegister(). The only reason why we - // don't do that already is that m_lexicalEnvironment is required by ConstDeclNode. ConstDeclNode - // requires weird things because it is a shameful pile of nonsense, but block scoping would make - // that code sensible and obviate the need for us to do bad things. - for (unsigned i = m_symbolTableStack.size(); i--; ) { - SymbolTableStackEntry& stackEntry = m_symbolTableStack[i]; - // We should not resolve a variable to VarKind::Scope if a "with" scope lies in between the current - // scope and the resolved scope. - RELEASE_ASSERT(!stackEntry.m_isWithScope); - - if (stackEntry.m_symbolTable->get(variable.ident().impl()).isNull()) - continue; - - RegisterID* scope = stackEntry.m_scope; - RELEASE_ASSERT(scope); - return scope; - } + m_codeBlock->addPropertyAccessInstruction(instructions().size()); - RELEASE_ASSERT_NOT_REACHED(); - return nullptr; - - case VarKind::Invalid: - // Indicates non-local resolution. - - m_codeBlock->addPropertyAccessInstruction(instructions().size()); - - // resolve_scope dst, id, ResolveType, depth - dst = tempDestination(dst); - emitOpcode(op_resolve_scope); - instructions().append(kill(dst)); - instructions().append(scopeRegister()->index()); - instructions().append(addConstant(variable.ident())); - instructions().append(resolveType()); - instructions().append(localScopeDepth()); - instructions().append(0); - return dst; - } - - RELEASE_ASSERT_NOT_REACHED(); - return nullptr; -} + ASSERT(!m_symbolTable || !m_symbolTable->contains(identifier.impl()) || resolveType() == Dynamic); -RegisterID* BytecodeGenerator::emitGetFromScope(RegisterID* dst, RegisterID* scope, const Variable& variable, ResolveMode resolveMode) -{ - switch (variable.offset().kind()) { - case VarKind::Stack: - return emitMove(dst, variable.local()); - - case VarKind::DirectArgument: { - UnlinkedValueProfile profile = emitProfiledOpcode(op_get_from_arguments); - instructions().append(kill(dst)); - instructions().append(scope->index()); - instructions().append(variable.offset().capturedArgumentsOffset().offset()); - instructions().append(profile); - return dst; - } - - case VarKind::Scope: - case VarKind::Invalid: { - m_codeBlock->addPropertyAccessInstruction(instructions().size()); - - // get_from_scope dst, scope, id, GetPutInfo, Structure, Operand - UnlinkedValueProfile profile = emitProfiledOpcode(op_get_from_scope); - instructions().append(kill(dst)); - instructions().append(scope->index()); - instructions().append(addConstant(variable.ident())); - instructions().append(GetPutInfo(resolveMode, variable.offset().isScope() ? LocalClosureVar : resolveType(), NotInitialization).operand()); - instructions().append(localScopeDepth()); - instructions().append(variable.offset().isScope() ? variable.offset().scopeOffset().offset() : 0); - instructions().append(profile); - return dst; - } } - - RELEASE_ASSERT_NOT_REACHED(); + // resolve_scope dst, id, ResolveType, depth + emitOpcode(op_resolve_scope); + instructions().append(kill(dst)); + instructions().append(addConstant(identifier)); + instructions().append(resolveType()); + instructions().append(0); + instructions().append(0); + return dst; } -RegisterID* BytecodeGenerator::emitPutToScope(RegisterID* scope, const Variable& variable, RegisterID* value, ResolveMode resolveMode, InitializationMode initializationMode) +RegisterID* BytecodeGenerator::emitGetFromScope(RegisterID* dst, RegisterID* scope, const Identifier& identifier, ResolveMode resolveMode) { - switch (variable.offset().kind()) { - case VarKind::Stack: - emitMove(variable.local(), value); - return value; - - case VarKind::DirectArgument: - emitOpcode(op_put_to_arguments); - instructions().append(scope->index()); - instructions().append(variable.offset().capturedArgumentsOffset().offset()); - instructions().append(value->index()); - return value; - - case VarKind::Scope: - case VarKind::Invalid: { - m_codeBlock->addPropertyAccessInstruction(instructions().size()); - - // put_to_scope scope, id, value, GetPutInfo, Structure, Operand - emitOpcode(op_put_to_scope); - instructions().append(scope->index()); - instructions().append(addConstant(variable.ident())); - instructions().append(value->index()); - ScopeOffset offset; - if (variable.offset().isScope()) { - offset = variable.offset().scopeOffset(); - instructions().append(GetPutInfo(resolveMode, LocalClosureVar, initializationMode).operand()); - instructions().append(variable.symbolTableConstantIndex()); - } else { - ASSERT(resolveType() != LocalClosureVar); - instructions().append(GetPutInfo(resolveMode, resolveType(), initializationMode).operand()); - instructions().append(localScopeDepth()); - } - instructions().append(!!offset ? offset.offset() : 0); - return value; - } } - - RELEASE_ASSERT_NOT_REACHED(); + m_codeBlock->addPropertyAccessInstruction(instructions().size()); + + // get_from_scope dst, scope, id, ResolveModeAndType, Structure, Operand + UnlinkedValueProfile profile = emitProfiledOpcode(op_get_from_scope); + instructions().append(kill(dst)); + instructions().append(scope->index()); + instructions().append(addConstant(identifier)); + instructions().append(ResolveModeAndType(resolveMode, resolveType()).operand()); + instructions().append(0); + instructions().append(0); + instructions().append(profile); + return dst; } -RegisterID* BytecodeGenerator::initializeVariable(const Variable& variable, RegisterID* value) +RegisterID* BytecodeGenerator::emitPutToScope(RegisterID* scope, const Identifier& identifier, RegisterID* value, ResolveMode resolveMode) { - RELEASE_ASSERT(variable.offset().kind() != VarKind::Invalid); - RegisterID* scope = emitResolveScope(nullptr, variable); - return emitPutToScope(scope, variable, value, ThrowIfNotFound, NotInitialization); + m_codeBlock->addPropertyAccessInstruction(instructions().size()); + + // put_to_scope scope, id, value, ResolveModeAndType, Structure, Operand + emitOpcode(op_put_to_scope); + instructions().append(scope->index()); + instructions().append(addConstant(identifier)); + instructions().append(value->index()); + instructions().append(ResolveModeAndType(resolveMode, resolveType()).operand()); + instructions().append(0); + instructions().append(0); + return value; } RegisterID* BytecodeGenerator::emitInstanceOf(RegisterID* dst, RegisterID* value, RegisterID* basePrototype) -{ +{ emitOpcode(op_instanceof); instructions().append(dst->index()); instructions().append(value->index()); @@ -2272,20 +1283,19 @@ RegisterID* BytecodeGenerator::emitInstanceOf(RegisterID* dst, RegisterID* value return dst; } -RegisterID* BytecodeGenerator::emitInstanceOfCustom(RegisterID* dst, RegisterID* value, RegisterID* constructor, RegisterID* hasInstanceValue) +RegisterID* BytecodeGenerator::emitInitGlobalConst(const Identifier& identifier, RegisterID* value) { - emitOpcode(op_instanceof_custom); - instructions().append(dst->index()); + ASSERT(m_codeType == GlobalCode); + emitOpcode(op_init_global_const_nop); + instructions().append(0); instructions().append(value->index()); - instructions().append(constructor->index()); - instructions().append(hasInstanceValue->index()); - return dst; + instructions().append(0); + instructions().append(addConstant(identifier)); + return value; } RegisterID* BytecodeGenerator::emitGetById(RegisterID* dst, RegisterID* base, const Identifier& property) { - ASSERT_WITH_MESSAGE(!parseIndex(property), "Indexed properties should be handled with get_by_val."); - m_codeBlock->addPropertyAccessInstruction(instructions().size()); UnlinkedValueProfile profile = emitProfiledOpcode(op_get_by_id); @@ -2300,10 +1310,18 @@ RegisterID* BytecodeGenerator::emitGetById(RegisterID* dst, RegisterID* base, co return dst; } -RegisterID* BytecodeGenerator::emitPutById(RegisterID* base, const Identifier& property, RegisterID* value) +RegisterID* BytecodeGenerator::emitGetArgumentsLength(RegisterID* dst, RegisterID* base) { - ASSERT_WITH_MESSAGE(!parseIndex(property), "Indexed properties should be handled with put_by_val."); + emitOpcode(op_get_arguments_length); + instructions().append(dst->index()); + ASSERT(base->virtualRegister() == m_codeBlock->argumentsRegister()); + instructions().append(base->index()); + instructions().append(addConstant(propertyNames().length)); + return dst; +} +RegisterID* BytecodeGenerator::emitPutById(RegisterID* base, const Identifier& property, RegisterID* value) +{ unsigned propertyIndex = addConstant(property); m_staticPropertyAnalyzer.putById(base->index(), propertyIndex); @@ -2314,19 +1332,16 @@ RegisterID* BytecodeGenerator::emitPutById(RegisterID* base, const Identifier& p instructions().append(base->index()); instructions().append(propertyIndex); instructions().append(value->index()); - instructions().append(0); // old structure - instructions().append(0); // offset - instructions().append(0); // new structure - instructions().append(0); // structure chain - instructions().append(static_cast<int>(PutByIdNone)); // is not direct - + instructions().append(0); + instructions().append(0); + instructions().append(0); + instructions().append(0); + instructions().append(0); return value; } -RegisterID* BytecodeGenerator::emitDirectPutById(RegisterID* base, const Identifier& property, RegisterID* value, PropertyNode::PutType putType) +RegisterID* BytecodeGenerator::emitDirectPutById(RegisterID* base, const Identifier& property, RegisterID* value) { - ASSERT_WITH_MESSAGE(!parseIndex(property), "Indexed properties should be handled with put_by_val(direct)."); - unsigned propertyIndex = addConstant(property); m_staticPropertyAnalyzer.putById(base->index(), propertyIndex); @@ -2337,106 +1352,66 @@ RegisterID* BytecodeGenerator::emitDirectPutById(RegisterID* base, const Identif instructions().append(base->index()); instructions().append(propertyIndex); instructions().append(value->index()); - instructions().append(0); // old structure - instructions().append(0); // offset - instructions().append(0); // new structure - instructions().append(0); // structure chain (unused if direct) - instructions().append(static_cast<int>((putType == PropertyNode::KnownDirect || property != m_vm->propertyNames->underscoreProto) ? PutByIdIsDirect : PutByIdNone)); + instructions().append(0); + instructions().append(0); + instructions().append(0); + instructions().append(0); + instructions().append( + property != m_vm->propertyNames->underscoreProto + && PropertyName(property).asIndex() == PropertyName::NotAnIndex); return value; } -void BytecodeGenerator::emitPutGetterById(RegisterID* base, const Identifier& property, unsigned attributes, RegisterID* getter) -{ - unsigned propertyIndex = addConstant(property); - m_staticPropertyAnalyzer.putById(base->index(), propertyIndex); - - emitOpcode(op_put_getter_by_id); - instructions().append(base->index()); - instructions().append(propertyIndex); - instructions().append(attributes); - instructions().append(getter->index()); -} - -void BytecodeGenerator::emitPutSetterById(RegisterID* base, const Identifier& property, unsigned attributes, RegisterID* setter) -{ - unsigned propertyIndex = addConstant(property); - m_staticPropertyAnalyzer.putById(base->index(), propertyIndex); - - emitOpcode(op_put_setter_by_id); - instructions().append(base->index()); - instructions().append(propertyIndex); - instructions().append(attributes); - instructions().append(setter->index()); -} - -void BytecodeGenerator::emitPutGetterSetter(RegisterID* base, const Identifier& property, unsigned attributes, RegisterID* getter, RegisterID* setter) +void BytecodeGenerator::emitPutGetterSetter(RegisterID* base, const Identifier& property, RegisterID* getter, RegisterID* setter) { unsigned propertyIndex = addConstant(property); m_staticPropertyAnalyzer.putById(base->index(), propertyIndex); - emitOpcode(op_put_getter_setter_by_id); + emitOpcode(op_put_getter_setter); instructions().append(base->index()); instructions().append(propertyIndex); - instructions().append(attributes); instructions().append(getter->index()); instructions().append(setter->index()); } -void BytecodeGenerator::emitPutGetterByVal(RegisterID* base, RegisterID* property, unsigned attributes, RegisterID* getter) +RegisterID* BytecodeGenerator::emitDeleteById(RegisterID* dst, RegisterID* base, const Identifier& property) { - emitOpcode(op_put_getter_by_val); + emitOpcode(op_del_by_id); + instructions().append(dst->index()); instructions().append(base->index()); - instructions().append(property->index()); - instructions().append(attributes); - instructions().append(getter->index()); + instructions().append(addConstant(property)); + return dst; } -void BytecodeGenerator::emitPutSetterByVal(RegisterID* base, RegisterID* property, unsigned attributes, RegisterID* setter) +RegisterID* BytecodeGenerator::emitGetArgumentByVal(RegisterID* dst, RegisterID* base, RegisterID* property) { - emitOpcode(op_put_setter_by_val); + UnlinkedArrayProfile arrayProfile = newArrayProfile(); + UnlinkedValueProfile profile = emitProfiledOpcode(op_get_argument_by_val); + instructions().append(kill(dst)); + ASSERT(base->virtualRegister() == m_codeBlock->argumentsRegister()); instructions().append(base->index()); instructions().append(property->index()); - instructions().append(attributes); - instructions().append(setter->index()); -} - -RegisterID* BytecodeGenerator::emitDeleteById(RegisterID* dst, RegisterID* base, const Identifier& property) -{ - emitOpcode(op_del_by_id); - instructions().append(dst->index()); - instructions().append(base->index()); - instructions().append(addConstant(property)); + instructions().append(arrayProfile); + instructions().append(profile); return dst; } RegisterID* BytecodeGenerator::emitGetByVal(RegisterID* dst, RegisterID* base, RegisterID* property) { for (size_t i = m_forInContextStack.size(); i > 0; i--) { - ForInContext* context = m_forInContextStack[i - 1].get(); - if (context->local() != property) - continue; - - if (!context->isValid()) - break; - - if (context->type() == ForInContext::IndexedForInContextType) { - property = static_cast<IndexedForInContext*>(context)->index(); - break; + ForInContext& context = m_forInContextStack[i - 1]; + if (context.propertyRegister == property) { + emitOpcode(op_get_by_pname); + instructions().append(dst->index()); + instructions().append(base->index()); + instructions().append(property->index()); + instructions().append(context.expectedSubscriptRegister->index()); + instructions().append(context.iterRegister->index()); + instructions().append(context.indexRegister->index()); + return dst; } - - ASSERT(context->type() == ForInContext::StructureForInContextType); - StructureForInContext* structureContext = static_cast<StructureForInContext*>(context); - UnlinkedValueProfile profile = emitProfiledOpcode(op_get_direct_pname); - instructions().append(kill(dst)); - instructions().append(base->index()); - instructions().append(property->index()); - instructions().append(structureContext->index()->index()); - instructions().append(structureContext->enumerator()->index()); - instructions().append(profile); - return dst; } - UnlinkedArrayProfile arrayProfile = newArrayProfile(); UnlinkedValueProfile profile = emitProfiledOpcode(op_get_by_val); instructions().append(kill(dst)); @@ -2455,7 +1430,6 @@ RegisterID* BytecodeGenerator::emitPutByVal(RegisterID* base, RegisterID* proper instructions().append(property->index()); instructions().append(value->index()); instructions().append(arrayProfile); - return value; } @@ -2488,81 +1462,23 @@ RegisterID* BytecodeGenerator::emitPutByIndex(RegisterID* base, unsigned index, return value; } -RegisterID* BytecodeGenerator::emitAssert(RegisterID* condition, int line) -{ - emitOpcode(op_assert); - instructions().append(condition->index()); - instructions().append(line); - return condition; -} - RegisterID* BytecodeGenerator::emitCreateThis(RegisterID* dst) { - size_t begin = instructions().size(); - m_staticPropertyAnalyzer.createThis(dst->index(), begin + 3); + RefPtr<RegisterID> func = newTemporary(); m_codeBlock->addPropertyAccessInstruction(instructions().size()); - emitOpcode(op_create_this); - instructions().append(dst->index()); - instructions().append(dst->index()); - instructions().append(0); + emitOpcode(op_get_callee); + instructions().append(func->index()); instructions().append(0); - return dst; -} - -void BytecodeGenerator::emitTDZCheck(RegisterID* target) -{ - emitOpcode(op_check_tdz); - instructions().append(target->index()); -} - -bool BytecodeGenerator::needsTDZCheck(const Variable& variable) -{ - for (unsigned i = m_TDZStack.size(); i--;) { - VariableEnvironment& identifiers = m_TDZStack[i].first; - if (identifiers.contains(variable.ident().impl())) - return true; - } - - return false; -} - -void BytecodeGenerator::emitTDZCheckIfNecessary(const Variable& variable, RegisterID* target, RegisterID* scope) -{ - if (needsTDZCheck(variable)) { - if (target) - emitTDZCheck(target); - else { - RELEASE_ASSERT(!variable.isLocal() && scope); - RefPtr<RegisterID> result = emitGetFromScope(newTemporary(), scope, variable, DoNotThrowIfNotFound); - emitTDZCheck(result.get()); - } - } -} -void BytecodeGenerator::liftTDZCheckIfPossible(const Variable& variable) -{ - RefPtr<UniquedStringImpl> identifier(variable.ident().impl()); - for (unsigned i = m_TDZStack.size(); i--;) { - VariableEnvironment& environment = m_TDZStack[i].first; - if (environment.contains(identifier)) { - bool isSyntacticallyAbleToOptimizeTDZ = m_TDZStack[i].second; - if (isSyntacticallyAbleToOptimizeTDZ) { - bool wasRemoved = environment.remove(identifier); - RELEASE_ASSERT(wasRemoved); - } - break; - } - } -} + size_t begin = instructions().size(); + m_staticPropertyAnalyzer.createThis(m_thisRegister.index(), begin + 3); -void BytecodeGenerator::getVariablesUnderTDZ(VariableEnvironment& result) -{ - for (auto& pair : m_TDZStack) { - VariableEnvironment& environment = pair.first; - for (auto entry : environment) - result.add(entry.key.get()); - } + emitOpcode(op_create_this); + instructions().append(m_thisRegister.index()); + instructions().append(func->index()); + instructions().append(0); + return dst; } RegisterID* BytecodeGenerator::emitNewObject(RegisterID* dst) @@ -2592,16 +1508,6 @@ JSString* BytecodeGenerator::addStringConstant(const Identifier& identifier) return stringInMap; } -JSTemplateRegistryKey* BytecodeGenerator::addTemplateRegistryKeyConstant(const TemplateRegistryKey& templateRegistryKey) -{ - JSTemplateRegistryKey*& templateRegistryKeyInMap = m_templateRegistryKeyMap.add(templateRegistryKey, nullptr).iterator->value; - if (!templateRegistryKeyInMap) { - templateRegistryKeyInMap = JSTemplateRegistryKey::create(*vm(), templateRegistryKey); - addConstantValue(templateRegistryKeyInMap); - } - return templateRegistryKeyInMap; -} - RegisterID* BytecodeGenerator::emitNewArray(RegisterID* dst, ElementNode* elements, unsigned length) { #if !ASSERT_DISABLED @@ -2658,98 +1564,82 @@ RegisterID* BytecodeGenerator::emitNewArray(RegisterID* dst, ElementNode* elemen return dst; } -RegisterID* BytecodeGenerator::emitNewArrayWithSize(RegisterID* dst, RegisterID* length) +RegisterID* BytecodeGenerator::emitNewFunction(RegisterID* dst, CaptureMode captureMode, FunctionBodyNode* function) { - emitOpcode(op_new_array_with_size); - instructions().append(dst->index()); - instructions().append(length->index()); - instructions().append(newArrayAllocationProfile()); - - return dst; + return emitNewFunctionInternal(dst, captureMode, m_codeBlock->addFunctionDecl(makeFunction(function)), false); } -RegisterID* BytecodeGenerator::emitNewRegExp(RegisterID* dst, RegExp* regExp) +RegisterID* BytecodeGenerator::emitLazyNewFunction(RegisterID* dst, FunctionBodyNode* function) { - emitOpcode(op_new_regexp); - instructions().append(dst->index()); - instructions().append(addRegExp(regExp)); - return dst; + FunctionOffsetMap::AddResult ptr = m_functionOffsets.add(function, 0); + if (ptr.isNewEntry) + ptr.iterator->value = m_codeBlock->addFunctionDecl(makeFunction(function)); + return emitNewFunctionInternal(dst, NotCaptured, ptr.iterator->value, true); } -void BytecodeGenerator::emitNewFunctionExpressionCommon(RegisterID* dst, BaseFuncExprNode* func) +RegisterID* BytecodeGenerator::emitNewFunctionInternal(RegisterID* dst, CaptureMode captureMode, unsigned index, bool doNullCheck) { - FunctionMetadataNode* function = func->metadata(); - unsigned index = m_codeBlock->addFunctionExpr(makeFunction(function)); - - OpcodeID opcodeID = op_new_func_exp; - switch (function->parseMode()) { - case SourceParseMode::GeneratorWrapperFunctionMode: { - opcodeID = op_new_generator_func_exp; - break; - } - case SourceParseMode::ArrowFunctionMode: { - opcodeID = op_new_arrow_func_exp; - break; - } - default: { - break; - } - } - - emitOpcode(opcodeID); + createActivationIfNecessary(); + emitOpcode(captureMode == IsCaptured ? op_new_captured_func : op_new_func); instructions().append(dst->index()); - instructions().append(scopeRegister()->index()); instructions().append(index); -} - -RegisterID* BytecodeGenerator::emitNewFunctionExpression(RegisterID* dst, FuncExprNode* func) -{ - emitNewFunctionExpressionCommon(dst, func); + if (captureMode == IsCaptured) { + ASSERT(!doNullCheck); + instructions().append(watchableVariable(dst->index())); + } else + instructions().append(doNullCheck); return dst; } -RegisterID* BytecodeGenerator::emitNewArrowFunctionExpression(RegisterID* dst, ArrowFuncExprNode* func) +RegisterID* BytecodeGenerator::emitNewRegExp(RegisterID* dst, RegExp* regExp) { - ASSERT(func->metadata()->parseMode() == SourceParseMode::ArrowFunctionMode); - emitNewFunctionExpressionCommon(dst, func); + emitOpcode(op_new_regexp); + instructions().append(dst->index()); + instructions().append(addRegExp(regExp)); return dst; } -RegisterID* BytecodeGenerator::emitNewDefaultConstructor(RegisterID* dst, ConstructorKind constructorKind, const Identifier& name) +RegisterID* BytecodeGenerator::emitNewFunctionExpression(RegisterID* r0, FuncExprNode* n) { - UnlinkedFunctionExecutable* executable = m_vm->builtinExecutables()->createDefaultConstructor(constructorKind, name); - executable->setInvalidTypeProfilingOffsets(); - - unsigned index = m_codeBlock->addFunctionExpr(executable); - + FunctionBodyNode* function = n->body(); + unsigned index = m_codeBlock->addFunctionExpr(makeFunction(function)); + + createActivationIfNecessary(); emitOpcode(op_new_func_exp); - instructions().append(dst->index()); - instructions().append(scopeRegister()->index()); + instructions().append(r0->index()); instructions().append(index); - return dst; + return r0; } -RegisterID* BytecodeGenerator::emitNewFunction(RegisterID* dst, FunctionMetadataNode* function) +RegisterID* BytecodeGenerator::emitCall(RegisterID* dst, RegisterID* func, ExpectedFunction expectedFunction, CallArguments& callArguments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) { - unsigned index = m_codeBlock->addFunctionDecl(makeFunction(function)); - if (function->parseMode() == SourceParseMode::GeneratorWrapperFunctionMode) - emitOpcode(op_new_generator_func); - else - emitOpcode(op_new_func); - instructions().append(dst->index()); - instructions().append(scopeRegister()->index()); - instructions().append(index); - return dst; + return emitCall(op_call, dst, func, expectedFunction, callArguments, divot, divotStart, divotEnd); } -RegisterID* BytecodeGenerator::emitCall(RegisterID* dst, RegisterID* func, ExpectedFunction expectedFunction, CallArguments& callArguments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) +void BytecodeGenerator::createArgumentsIfNecessary() { - return emitCall(op_call, dst, func, expectedFunction, callArguments, divot, divotStart, divotEnd); + if (m_codeType != FunctionCode) + return; + + if (!m_codeBlock->usesArguments()) + return; + + if (shouldTearOffArgumentsEagerly()) + return; + + emitOpcode(op_create_arguments); + instructions().append(m_codeBlock->argumentsRegister().offset()); + ASSERT(!hasWatchableVariable(m_codeBlock->argumentsRegister().offset())); } -RegisterID* BytecodeGenerator::emitCallInTailPosition(RegisterID* dst, RegisterID* func, ExpectedFunction expectedFunction, CallArguments& callArguments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) +void BytecodeGenerator::createActivationIfNecessary() { - return emitCall(m_inTailPosition ? op_tail_call : op_call, dst, func, expectedFunction, callArguments, divot, divotStart, divotEnd); + if (m_hasCreatedActivation) + return; + if (!m_codeBlock->needsFullScopeChain()) + return; + emitOpcode(op_create_activation); + instructions().append(m_activationRegister->index()); } RegisterID* BytecodeGenerator::emitCallEval(RegisterID* dst, RegisterID* func, CallArguments& callArguments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) @@ -2759,9 +1649,9 @@ RegisterID* BytecodeGenerator::emitCallEval(RegisterID* dst, RegisterID* func, C ExpectedFunction BytecodeGenerator::expectedFunctionForIdentifier(const Identifier& identifier) { - if (identifier == m_vm->propertyNames->Object || identifier == m_vm->propertyNames->ObjectPrivateName) + if (identifier == m_vm->propertyNames->Object) return ExpectObjectConstructor; - if (identifier == m_vm->propertyNames->Array || identifier == m_vm->propertyNames->ArrayPrivateName) + if (identifier == m_vm->propertyNames->Array) return ExpectArrayConstructor; return NoExpectedFunction; } @@ -2802,9 +1692,12 @@ ExpectedFunction BytecodeGenerator::emitExpectedFunctionSnippet(RegisterID* dst, instructions().append(realCall->bind(begin, instructions().size())); if (dst != ignoredResult()) { - if (callArguments.argumentCountIncludingThis() == 2) - emitNewArrayWithSize(dst, callArguments.argumentRegister(0)); - else { + if (callArguments.argumentCountIncludingThis() == 2) { + emitOpcode(op_new_array_with_size); + instructions().append(dst->index()); + instructions().append(callArguments.argumentRegister(0)->index()); + instructions().append(newArrayAllocationProfile()); + } else { ASSERT(callArguments.argumentCountIncludingThis() == 1); emitOpcode(op_new_array); instructions().append(dst->index()); @@ -2831,7 +1724,7 @@ ExpectedFunction BytecodeGenerator::emitExpectedFunctionSnippet(RegisterID* dst, RegisterID* BytecodeGenerator::emitCall(OpcodeID opcodeID, RegisterID* dst, RegisterID* func, ExpectedFunction expectedFunction, CallArguments& callArguments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) { - ASSERT(opcodeID == op_call || opcodeID == op_call_eval || opcodeID == op_tail_call); + ASSERT(opcodeID == op_call || opcodeID == op_call_eval); ASSERT(func->refCount()); if (m_shouldEmitProfileHooks) @@ -2844,10 +1737,8 @@ RegisterID* BytecodeGenerator::emitCall(OpcodeID opcodeID, RegisterID* dst, Regi if (n && n->m_expr->isSpreadExpression()) { RELEASE_ASSERT(!n->m_next); auto expression = static_cast<SpreadExpressionNode*>(n->m_expr)->expression(); - RefPtr<RegisterID> argumentRegister; - argumentRegister = expression->emitBytecode(*this, callArguments.argumentRegister(0)); - RefPtr<RegisterID> thisRegister = emitMove(newTemporary(), callArguments.thisRegister()); - return emitCallVarargs(opcodeID == op_tail_call ? op_tail_call_varargs : op_call_varargs, dst, func, callArguments.thisRegister(), argumentRegister.get(), newTemporary(), 0, callArguments.profileHookRegister(), divot, divotStart, divotEnd); + expression->emitBytecode(*this, callArguments.argumentRegister(0)); + return emitCallVarargs(dst, func, callArguments.thisRegister(), callArguments.argumentRegister(0), newTemporary(), callArguments.profileHookRegister(), divot, divotStart, divotEnd); } for (; n; n = n->m_next) emitNode(callArguments.argumentRegister(argument++), n); @@ -2877,8 +1768,11 @@ RegisterID* BytecodeGenerator::emitCall(OpcodeID opcodeID, RegisterID* dst, Regi instructions().append(func->index()); instructions().append(callArguments.argumentCountIncludingThis()); instructions().append(callArguments.stackOffset()); +#if ENABLE(LLINT) instructions().append(m_codeBlock->addLLIntCallLinkInfo()); +#else instructions().append(0); +#endif instructions().append(arrayProfile); instructions().append(profile); @@ -2893,22 +1787,7 @@ RegisterID* BytecodeGenerator::emitCall(OpcodeID opcodeID, RegisterID* dst, Regi return dst; } -RegisterID* BytecodeGenerator::emitCallVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) -{ - return emitCallVarargs(op_call_varargs, dst, func, thisRegister, arguments, firstFreeRegister, firstVarArgOffset, profileHookRegister, divot, divotStart, divotEnd); -} - -RegisterID* BytecodeGenerator::emitCallVarargsInTailPosition(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) -{ - return emitCallVarargs(m_inTailPosition ? op_tail_call_varargs : op_call_varargs, dst, func, thisRegister, arguments, firstFreeRegister, firstVarArgOffset, profileHookRegister, divot, divotStart, divotEnd); -} - -RegisterID* BytecodeGenerator::emitConstructVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) -{ - return emitCallVarargs(op_construct_varargs, dst, func, thisRegister, arguments, firstFreeRegister, firstVarArgOffset, profileHookRegister, divot, divotStart, divotEnd); -} - -RegisterID* BytecodeGenerator::emitCallVarargs(OpcodeID opcode, RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) +RegisterID* BytecodeGenerator::emitCallVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) { if (m_shouldEmitProfileHooks) { emitMove(profileHookRegister, func); @@ -2920,14 +1799,13 @@ RegisterID* BytecodeGenerator::emitCallVarargs(OpcodeID opcode, RegisterID* dst, // Emit call. UnlinkedArrayProfile arrayProfile = newArrayProfile(); - UnlinkedValueProfile profile = emitProfiledOpcode(opcode); + UnlinkedValueProfile profile = emitProfiledOpcode(op_call_varargs); ASSERT(dst != ignoredResult()); instructions().append(dst->index()); instructions().append(func->index()); - instructions().append(thisRegister ? thisRegister->index() : 0); + instructions().append(thisRegister->index()); instructions().append(arguments->index()); instructions().append(firstFreeRegister->index()); - instructions().append(firstVarArgOffset); instructions().append(arrayProfile); instructions().append(profile); if (m_shouldEmitProfileHooks) { @@ -2937,65 +1815,28 @@ RegisterID* BytecodeGenerator::emitCallVarargs(OpcodeID opcode, RegisterID* dst, return dst; } -void BytecodeGenerator::emitCallDefineProperty(RegisterID* newObj, RegisterID* propertyNameRegister, - RegisterID* valueRegister, RegisterID* getterRegister, RegisterID* setterRegister, unsigned options, const JSTextPosition& position) -{ - RefPtr<RegisterID> descriptorRegister = emitNewObject(newTemporary()); - - RefPtr<RegisterID> trueRegister = emitLoad(newTemporary(), true); - if (options & PropertyConfigurable) - emitDirectPutById(descriptorRegister.get(), propertyNames().configurable, trueRegister.get(), PropertyNode::Unknown); - if (options & PropertyWritable) - emitDirectPutById(descriptorRegister.get(), propertyNames().writable, trueRegister.get(), PropertyNode::Unknown); - else if (valueRegister) { - RefPtr<RegisterID> falseRegister = emitLoad(newTemporary(), false); - emitDirectPutById(descriptorRegister.get(), propertyNames().writable, falseRegister.get(), PropertyNode::Unknown); - } - if (options & PropertyEnumerable) - emitDirectPutById(descriptorRegister.get(), propertyNames().enumerable, trueRegister.get(), PropertyNode::Unknown); - - if (valueRegister) - emitDirectPutById(descriptorRegister.get(), propertyNames().value, valueRegister, PropertyNode::Unknown); - if (getterRegister) - emitDirectPutById(descriptorRegister.get(), propertyNames().get, getterRegister, PropertyNode::Unknown); - if (setterRegister) - emitDirectPutById(descriptorRegister.get(), propertyNames().set, setterRegister, PropertyNode::Unknown); - - RefPtr<RegisterID> definePropertyRegister = emitMoveLinkTimeConstant(newTemporary(), LinkTimeConstant::DefinePropertyFunction); - - CallArguments callArguments(*this, nullptr, 3); - emitLoad(callArguments.thisRegister(), jsUndefined()); - emitMove(callArguments.argumentRegister(0), newObj); - emitMove(callArguments.argumentRegister(1), propertyNameRegister); - emitMove(callArguments.argumentRegister(2), descriptorRegister.get()); - - emitCall(newTemporary(), definePropertyRegister.get(), NoExpectedFunction, callArguments, position, position, position); -} - RegisterID* BytecodeGenerator::emitReturn(RegisterID* src) { - if (isConstructor()) { - bool derived = constructorKind() == ConstructorKind::Derived; - if (derived && src->index() == m_thisRegister.index()) - emitTDZCheck(src); - - RefPtr<Label> isObjectLabel = newLabel(); - emitJumpIfTrue(emitIsObject(newTemporary(), src), isObjectLabel.get()); - - if (derived) { - RefPtr<Label> isUndefinedLabel = newLabel(); - emitJumpIfTrue(emitIsUndefined(newTemporary(), src), isUndefinedLabel.get()); - emitThrowTypeError("Cannot return a non-object type in the constructor of a derived class."); - emitLabel(isUndefinedLabel.get()); - if (constructorKind() == ConstructorKind::Derived) - emitTDZCheck(&m_thisRegister); - } - - emitUnaryNoDstOp(op_ret, &m_thisRegister); + if (m_codeBlock->needsFullScopeChain()) { + emitOpcode(op_tear_off_activation); + instructions().append(m_activationRegister->index()); + } - emitLabel(isObjectLabel.get()); + if (m_codeBlock->usesArguments() && m_codeBlock->numParameters() != 1 && !isStrictMode()) { + emitOpcode(op_tear_off_arguments); + instructions().append(m_codeBlock->argumentsRegister().offset()); + instructions().append(m_activationRegister ? m_activationRegister->index() : emitLoad(0, JSValue())->index()); } + // Constructors use op_ret_object_or_this to check the result is an + // object, unless we can trivially determine the check is not + // necessary (currently, if the return value is 'this'). + if (isConstructor() && (src->index() != m_thisRegister.index())) { + emitOpcode(op_ret_object_or_this); + instructions().append(src->index()); + instructions().append(m_thisRegister.index()); + return src; + } return emitUnaryNoDstOp(op_ret, src); } @@ -3016,16 +1857,6 @@ RegisterID* BytecodeGenerator::emitConstruct(RegisterID* dst, RegisterID* func, // Generate code for arguments. unsigned argument = 0; if (ArgumentsNode* argumentsNode = callArguments.argumentsNode()) { - - ArgumentListNode* n = callArguments.argumentsNode()->m_listNode; - if (n && n->m_expr->isSpreadExpression()) { - RELEASE_ASSERT(!n->m_next); - auto expression = static_cast<SpreadExpressionNode*>(n->m_expr)->expression(); - RefPtr<RegisterID> argumentRegister; - argumentRegister = expression->emitBytecode(*this, callArguments.argumentRegister(0)); - return emitConstructVarargs(dst, func, callArguments.thisRegister(), argumentRegister.get(), newTemporary(), 0, callArguments.profileHookRegister(), divot, divotStart, divotEnd); - } - for (ArgumentListNode* n = argumentsNode->m_listNode; n; n = n->m_next) emitNode(callArguments.argumentRegister(argument++), n); } @@ -3051,8 +1882,11 @@ RegisterID* BytecodeGenerator::emitConstruct(RegisterID* dst, RegisterID* func, instructions().append(func->index()); instructions().append(callArguments.argumentCountIncludingThis()); instructions().append(callArguments.stackOffset()); +#if ENABLE(LLINT) instructions().append(m_codeBlock->addLLIntCallLinkInfo()); +#else instructions().append(0); +#endif instructions().append(0); instructions().append(profile); @@ -3084,50 +1918,25 @@ void BytecodeGenerator::emitToPrimitive(RegisterID* dst, RegisterID* src) instructions().append(src->index()); } -void BytecodeGenerator::emitGetScope() +RegisterID* BytecodeGenerator::emitPushWithScope(RegisterID* scope) { - emitOpcode(op_get_scope); - instructions().append(scopeRegister()->index()); -} - -RegisterID* BytecodeGenerator::emitPushWithScope(RegisterID* objectScope) -{ - pushScopedControlFlowContext(); - RegisterID* newScope = newBlockScopeVariable(); - newScope->ref(); - - emitOpcode(op_push_with_scope); - instructions().append(newScope->index()); - instructions().append(objectScope->index()); - instructions().append(scopeRegister()->index()); - - emitMove(scopeRegister(), newScope); - m_symbolTableStack.append(SymbolTableStackEntry{ Strong<SymbolTable>(), newScope, true, 0 }); + ControlFlowContext context; + context.isFinallyBlock = false; + m_scopeContextStack.append(context); + m_localScopeDepth++; - return newScope; + return emitUnaryNoDstOp(op_push_with_scope, scope); } -RegisterID* BytecodeGenerator::emitGetParentScope(RegisterID* dst, RegisterID* scope) +void BytecodeGenerator::emitPopScope() { - emitOpcode(op_get_parent_scope); - instructions().append(dst->index()); - instructions().append(scope->index()); - return dst; -} + ASSERT(m_scopeContextStack.size()); + ASSERT(!m_scopeContextStack.last().isFinallyBlock); -void BytecodeGenerator::emitPopScope(RegisterID* dst, RegisterID* scope) -{ - RefPtr<RegisterID> parentScope = emitGetParentScope(newTemporary(), scope); - emitMove(dst, parentScope.get()); -} + emitOpcode(op_pop_scope); -void BytecodeGenerator::emitPopWithScope() -{ - emitPopScope(scopeRegister(), scopeRegister()); - popScopedControlFlowContext(); - SymbolTableStackEntry stackEntry = m_symbolTableStack.takeLast(); - stackEntry.m_scope->deref(); - RELEASE_ASSERT(stackEntry.m_isWithScope); + m_scopeContextStack.removeLast(); + m_localScopeDepth--; } void BytecodeGenerator::emitDebugHook(DebugHookID debugHookID, unsigned line, unsigned charOffset, unsigned lineStart) @@ -3156,40 +1965,11 @@ void BytecodeGenerator::pushFinallyContext(StatementNode* finallyBlock) scope.isFinallyBlock = true; FinallyContext context = { finallyBlock, - nullptr, - nullptr, static_cast<unsigned>(m_scopeContextStack.size()), static_cast<unsigned>(m_switchContextStack.size()), static_cast<unsigned>(m_forInContextStack.size()), static_cast<unsigned>(m_tryContextStack.size()), static_cast<unsigned>(m_labelScopes.size()), - static_cast<unsigned>(m_symbolTableStack.size()), - m_finallyDepth, - m_localScopeDepth - }; - scope.finallyContext = context; - m_scopeContextStack.append(scope); - m_finallyDepth++; -} - -void BytecodeGenerator::pushIteratorCloseContext(RegisterID* iterator, ThrowableExpressionData* node) -{ - // Reclaim free label scopes. - while (m_labelScopes.size() && !m_labelScopes.last().refCount()) - m_labelScopes.removeLast(); - - ControlFlowContext scope; - scope.isFinallyBlock = true; - FinallyContext context = { - nullptr, - iterator, - node, - static_cast<unsigned>(m_scopeContextStack.size()), - static_cast<unsigned>(m_switchContextStack.size()), - static_cast<unsigned>(m_forInContextStack.size()), - static_cast<unsigned>(m_tryContextStack.size()), - static_cast<unsigned>(m_labelScopes.size()), - static_cast<unsigned>(m_symbolTableStack.size()), m_finallyDepth, m_localScopeDepth }; @@ -3202,27 +1982,12 @@ void BytecodeGenerator::popFinallyContext() { ASSERT(m_scopeContextStack.size()); ASSERT(m_scopeContextStack.last().isFinallyBlock); - ASSERT(m_scopeContextStack.last().finallyContext.finallyBlock); - ASSERT(!m_scopeContextStack.last().finallyContext.iterator); - ASSERT(!m_scopeContextStack.last().finallyContext.enumerationNode); - ASSERT(m_finallyDepth > 0); - m_scopeContextStack.removeLast(); - m_finallyDepth--; -} - -void BytecodeGenerator::popIteratorCloseContext() -{ - ASSERT(m_scopeContextStack.size()); - ASSERT(m_scopeContextStack.last().isFinallyBlock); - ASSERT(!m_scopeContextStack.last().finallyContext.finallyBlock); - ASSERT(m_scopeContextStack.last().finallyContext.iterator); - ASSERT(m_scopeContextStack.last().finallyContext.enumerationNode); ASSERT(m_finallyDepth > 0); m_scopeContextStack.removeLast(); m_finallyDepth--; } -LabelScopePtr BytecodeGenerator::breakTarget(const Identifier& name) +LabelScope* BytecodeGenerator::breakTarget(const Identifier& name) { // Reclaim free label scopes. // @@ -3238,7 +2003,7 @@ LabelScopePtr BytecodeGenerator::breakTarget(const Identifier& name) } if (!m_labelScopes.size()) - return LabelScopePtr::null(); + return 0; // We special-case the following, which is a syntax error in Firefox: // label: @@ -3248,79 +2013,58 @@ LabelScopePtr BytecodeGenerator::breakTarget(const Identifier& name) LabelScope* scope = &m_labelScopes[i]; if (scope->type() != LabelScope::NamedLabel) { ASSERT(scope->breakTarget()); - return LabelScopePtr(m_labelScopes, i); + return scope; } } - return LabelScopePtr::null(); + return 0; } for (int i = m_labelScopes.size() - 1; i >= 0; --i) { LabelScope* scope = &m_labelScopes[i]; if (scope->name() && *scope->name() == name) { ASSERT(scope->breakTarget()); - return LabelScopePtr(m_labelScopes, i); + return scope; } } - return LabelScopePtr::null(); + return 0; } -LabelScopePtr BytecodeGenerator::continueTarget(const Identifier& name) +LabelScope* BytecodeGenerator::continueTarget(const Identifier& name) { // Reclaim free label scopes. while (m_labelScopes.size() && !m_labelScopes.last().refCount()) m_labelScopes.removeLast(); if (!m_labelScopes.size()) - return LabelScopePtr::null(); + return 0; if (name.isEmpty()) { for (int i = m_labelScopes.size() - 1; i >= 0; --i) { LabelScope* scope = &m_labelScopes[i]; if (scope->type() == LabelScope::Loop) { ASSERT(scope->continueTarget()); - return LabelScopePtr(m_labelScopes, i); + return scope; } } - return LabelScopePtr::null(); + return 0; } // Continue to the loop nested nearest to the label scope that matches // 'name'. - LabelScopePtr result = LabelScopePtr::null(); + LabelScope* result = 0; for (int i = m_labelScopes.size() - 1; i >= 0; --i) { LabelScope* scope = &m_labelScopes[i]; if (scope->type() == LabelScope::Loop) { ASSERT(scope->continueTarget()); - result = LabelScopePtr(m_labelScopes, i); + result = scope; } if (scope->name() && *scope->name() == name) - return result; // may be null. + return result; // may be 0 } - return LabelScopePtr::null(); + return 0; } -void BytecodeGenerator::allocateCalleeSaveSpace() -{ - size_t virtualRegisterCountForCalleeSaves = CodeBlock::llintBaselineCalleeSaveSpaceAsVirtualRegisters(); - - for (size_t i = 0; i < virtualRegisterCountForCalleeSaves; i++) { - RegisterID* localRegister = addVar(); - localRegister->ref(); - m_localRegistersForCalleeSaveRegisters.append(localRegister); - } -} - -void BytecodeGenerator::allocateAndEmitScope() -{ - m_scopeRegister = addVar(); - m_scopeRegister->ref(); - m_codeBlock->setScopeRegister(scopeRegister()->virtualRegister()); - emitGetScope(); - m_topMostScope = addVar(); - emitMove(m_topMostScope, scopeRegister()); -} - -void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowContext* topScope, ControlFlowContext* bottomScope) +void BytecodeGenerator::emitComplexPopScopes(ControlFlowContext* topScope, ControlFlowContext* bottomScope) { while (topScope > bottomScope) { // First we count the number of dynamic scopes we need to remove to get @@ -3336,11 +2080,8 @@ void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowConte if (nNormalScopes) { // We need to remove a number of dynamic scopes to get to the next // finally block - RefPtr<RegisterID> parentScope = newTemporary(); - while (nNormalScopes--) { - parentScope = emitGetParentScope(parentScope.get(), scope); - emitMove(scope, parentScope.get()); - } + while (nNormalScopes--) + emitOpcode(op_pop_scope); // If topScope == bottomScope then there isn't a finally block left to emit. if (topScope == bottomScope) @@ -3349,9 +2090,8 @@ void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowConte Vector<ControlFlowContext> savedScopeContextStack; Vector<SwitchInfo> savedSwitchContextStack; - Vector<std::unique_ptr<ForInContext>> savedForInContextStack; + Vector<ForInContext> savedForInContextStack; Vector<TryContext> poppedTryContexts; - Vector<SymbolTableStackEntry> savedSymbolTableStack; LabelScopeStore savedLabelScopes; while (topScope > bottomScope && topScope->isFinallyBlock) { RefPtr<Label> beforeFinally = emitLabel(newLabel().get()); @@ -3364,7 +2104,6 @@ void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowConte bool flipForIns = finallyContext.forInContextStackSize != m_forInContextStack.size(); bool flipTries = finallyContext.tryContextStackSize != m_tryContextStack.size(); bool flipLabelScopes = finallyContext.labelScopesSize != m_labelScopes.size(); - bool flipSymbolTableStack = finallyContext.symbolTableStackSize != m_symbolTableStack.size(); int topScopeIndex = -1; int bottomScopeIndex = -1; if (flipScopes) { @@ -3378,7 +2117,7 @@ void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowConte m_switchContextStack.shrink(finallyContext.switchContextStackSize); } if (flipForIns) { - savedForInContextStack.swap(m_forInContextStack); + savedForInContextStack = m_forInContextStack; m_forInContextStack.shrink(finallyContext.forInContextStackSize); } if (flipTries) { @@ -3399,24 +2138,14 @@ void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowConte while (m_labelScopes.size() > finallyContext.labelScopesSize) m_labelScopes.removeLast(); } - if (flipSymbolTableStack) { - savedSymbolTableStack = m_symbolTableStack; - m_symbolTableStack.shrink(finallyContext.symbolTableStackSize); - } int savedFinallyDepth = m_finallyDepth; m_finallyDepth = finallyContext.finallyDepth; int savedDynamicScopeDepth = m_localScopeDepth; m_localScopeDepth = finallyContext.dynamicScopeDepth; - if (finallyContext.finallyBlock) { - // Emit the finally block. - emitNode(finallyContext.finallyBlock); - } else { - // Emit the IteratorClose block. - ASSERT(finallyContext.iterator); - emitIteratorClose(finallyContext.iterator, finallyContext.enumerationNode); - } - + // Emit the finally block. + emitNode(finallyContext.finallyBlock); + RefPtr<Label> afterFinally = emitLabel(newLabel().get()); // Restore the state of the world. @@ -3428,7 +2157,7 @@ void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowConte if (flipSwitches) m_switchContextStack = savedSwitchContextStack; if (flipForIns) - m_forInContextStack.swap(savedForInContextStack); + m_forInContextStack = savedForInContextStack; if (flipTries) { ASSERT(m_tryContextStack.size() == finallyContext.tryContextStackSize); for (unsigned i = poppedTryContexts.size(); i--;) { @@ -3440,8 +2169,6 @@ void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowConte } if (flipLabelScopes) m_labelScopes = savedLabelScopes; - if (flipSymbolTableStack) - m_symbolTableStack = savedSymbolTableStack; m_finallyDepth = savedFinallyDepth; m_localScopeDepth = savedDynamicScopeDepth; @@ -3450,32 +2177,56 @@ void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowConte } } -void BytecodeGenerator::emitPopScopes(RegisterID* scope, int targetScopeDepth) +void BytecodeGenerator::emitPopScopes(int targetScopeDepth) { - ASSERT(labelScopeDepth() - targetScopeDepth >= 0); + ASSERT(scopeDepth() - targetScopeDepth >= 0); - size_t scopeDelta = labelScopeDepth() - targetScopeDepth; + size_t scopeDelta = scopeDepth() - targetScopeDepth; ASSERT(scopeDelta <= m_scopeContextStack.size()); if (!scopeDelta) return; if (!m_finallyDepth) { - RefPtr<RegisterID> parentScope = newTemporary(); - while (scopeDelta--) { - parentScope = emitGetParentScope(parentScope.get(), scope); - emitMove(scope, parentScope.get()); - } + while (scopeDelta--) + emitOpcode(op_pop_scope); return; } - emitComplexPopScopes(scope, &m_scopeContextStack.last(), &m_scopeContextStack.last() - scopeDelta); + emitComplexPopScopes(&m_scopeContextStack.last(), &m_scopeContextStack.last() - scopeDelta); +} + +RegisterID* BytecodeGenerator::emitGetPropertyNames(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, Label* breakTarget) +{ + size_t begin = instructions().size(); + + emitOpcode(op_get_pnames); + instructions().append(dst->index()); + instructions().append(base->index()); + instructions().append(i->index()); + instructions().append(size->index()); + instructions().append(breakTarget->bind(begin, instructions().size())); + return dst; +} + +RegisterID* BytecodeGenerator::emitNextPropertyName(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, RegisterID* iter, Label* target) +{ + size_t begin = instructions().size(); + + emitOpcode(op_next_pname); + instructions().append(dst->index()); + instructions().append(base->index()); + instructions().append(i->index()); + instructions().append(size->index()); + instructions().append(iter->index()); + instructions().append(target->bind(begin, instructions().size())); + return dst; } TryData* BytecodeGenerator::pushTry(Label* start) { TryData tryData; tryData.target = newLabel(); - tryData.handlerType = HandlerType::Illegal; + tryData.targetScopeDepth = UINT_MAX; m_tryData.append(tryData); TryData* result = &m_tryData.last(); @@ -3488,7 +2239,7 @@ TryData* BytecodeGenerator::pushTry(Label* start) return result; } -void BytecodeGenerator::popTryAndEmitCatch(TryData* tryData, RegisterID* exceptionRegister, RegisterID* thrownValueRegister, Label* end, HandlerType handlerType) +RegisterID* BytecodeGenerator::popTryAndEmitCatch(TryData* tryData, RegisterID* targetRegister, Label* end) { m_usesExceptions = true; @@ -3502,98 +2253,31 @@ void BytecodeGenerator::popTryAndEmitCatch(TryData* tryData, RegisterID* excepti m_tryContextStack.removeLast(); emitLabel(tryRange.tryData->target.get()); - tryRange.tryData->handlerType = handlerType; + tryRange.tryData->targetScopeDepth = m_localScopeDepth; emitOpcode(op_catch); - instructions().append(exceptionRegister->index()); - instructions().append(thrownValueRegister->index()); - - bool foundLocalScope = false; - for (unsigned i = m_symbolTableStack.size(); i--; ) { - // Note that if we don't find a local scope in the current function/program, - // we must grab the outer-most scope of this bytecode generation. - if (m_symbolTableStack[i].m_scope) { - foundLocalScope = true; - emitMove(scopeRegister(), m_symbolTableStack[i].m_scope); - break; - } - } - if (!foundLocalScope) - emitMove(scopeRegister(), m_topMostScope); -} - -int BytecodeGenerator::localScopeDepth() const -{ - return m_localScopeDepth; -} - -int BytecodeGenerator::labelScopeDepth() const -{ - return localScopeDepth() + m_finallyDepth; + instructions().append(targetRegister->index()); + return targetRegister; } void BytecodeGenerator::emitThrowReferenceError(const String& message) { emitOpcode(op_throw_static_error); - instructions().append(addConstantValue(addStringConstant(Identifier::fromString(m_vm, message)))->index()); + instructions().append(addConstantValue(addStringConstant(Identifier(m_vm, message)))->index()); instructions().append(true); } -void BytecodeGenerator::emitThrowTypeError(const String& message) -{ - emitOpcode(op_throw_static_error); - instructions().append(addConstantValue(addStringConstant(Identifier::fromString(m_vm, message)))->index()); - instructions().append(false); -} - -void BytecodeGenerator::emitPushFunctionNameScope(const Identifier& property, RegisterID* callee, bool isCaptured) -{ - // There is some nuance here: - // If we're in strict mode code, the function name scope variable acts exactly like a "const" variable. - // If we're not in strict mode code, we want to allow bogus assignments to the name scoped variable. - // This means any assignment to the variable won't throw, but it won't actually assign a new value to it. - // To accomplish this, we don't report that this scope is a lexical scope. This will prevent - // any throws when trying to assign to the variable (while still ensuring it keeps its original - // value). There is some ugliness and exploitation of a leaky abstraction here, but it's better than - // having a completely new op code and a class to handle name scopes which are so close in functionality - // to lexical environments. - VariableEnvironment nameScopeEnvironment; - auto addResult = nameScopeEnvironment.add(property); - if (isCaptured) - addResult.iterator->value.setIsCaptured(); - addResult.iterator->value.setIsConst(); // The function name scope name acts like a const variable. - unsigned numVars = m_codeBlock->m_numVars; - pushLexicalScopeInternal(nameScopeEnvironment, TDZCheckOptimization::Optimize, NestedScopeType::IsNotNested, nullptr, TDZRequirement::NotUnderTDZ, ScopeType::FunctionNameScope, ScopeRegisterType::Var); - ASSERT_UNUSED(numVars, m_codeBlock->m_numVars == static_cast<int>(numVars + 1)); // Should have only created one new "var" for the function name scope. - bool shouldTreatAsLexicalVariable = isStrictMode(); - Variable functionVar = variableForLocalEntry(property, m_symbolTableStack.last().m_symbolTable->get(property.impl()), m_symbolTableStack.last().m_symbolTableConstantIndex, shouldTreatAsLexicalVariable); - emitPutToScope(m_symbolTableStack.last().m_scope, functionVar, callee, ThrowIfNotFound, NotInitialization); -} - -void BytecodeGenerator::pushScopedControlFlowContext() +void BytecodeGenerator::emitPushNameScope(const Identifier& property, RegisterID* value, unsigned attributes) { ControlFlowContext context; context.isFinallyBlock = false; m_scopeContextStack.append(context); m_localScopeDepth++; -} - -void BytecodeGenerator::popScopedControlFlowContext() -{ - ASSERT(m_scopeContextStack.size()); - ASSERT(!m_scopeContextStack.last().isFinallyBlock); - m_scopeContextStack.removeLast(); - m_localScopeDepth--; -} - -void BytecodeGenerator::emitPushCatchScope(VariableEnvironment& environment) -{ - pushLexicalScopeInternal(environment, TDZCheckOptimization::Optimize, NestedScopeType::IsNotNested, nullptr, TDZRequirement::NotUnderTDZ, ScopeType::CatchScope, ScopeRegisterType::Block); -} -void BytecodeGenerator::emitPopCatchScope(VariableEnvironment& environment) -{ - popLexicalScopeInternal(environment, TDZRequirement::NotUnderTDZ); + emitOpcode(op_push_name_scope); + instructions().append(addConstant(property)); + instructions().append(value->index()); + instructions().append(attributes); } void BytecodeGenerator::beginSwitch(RegisterID* scrutineeRegister, SwitchInfo::SwitchType type) @@ -3718,632 +2402,81 @@ RegisterID* BytecodeGenerator::emitThrowExpressionTooDeepException() return newTemporary(); } +void BytecodeGenerator::setIsNumericCompareFunction(bool isNumericCompareFunction) +{ + m_codeBlock->setIsNumericCompareFunction(isNumericCompareFunction); +} + bool BytecodeGenerator::isArgumentNumber(const Identifier& ident, int argumentNumber) { - RegisterID* registerID = variable(ident).local(); - if (!registerID) - return false; + RegisterID* registerID = local(ident).get(); + if (!registerID || registerID->index() >= 0) + return 0; return registerID->index() == CallFrame::argumentOffset(argumentNumber); } -bool BytecodeGenerator::emitReadOnlyExceptionIfNeeded(const Variable& variable) +void BytecodeGenerator::emitReadOnlyExceptionIfNeeded() { - // If we're in strict mode, we always throw. - // If we're not in strict mode, we throw for "const" variables but not the function callee. - if (isStrictMode() || variable.isConst()) { - emitOpcode(op_throw_static_error); - instructions().append(addConstantValue(addStringConstant(Identifier::fromString(m_vm, StrictModeReadonlyPropertyWriteError)))->index()); - instructions().append(false); - return true; - } - return false; + if (!isStrictMode()) + return; + emitOpcode(op_throw_static_error); + instructions().append(addConstantValue(addStringConstant(Identifier(m_vm, StrictModeReadonlyPropertyWriteError)))->index()); + instructions().append(false); } -void BytecodeGenerator::emitEnumeration(ThrowableExpressionData* node, ExpressionNode* subjectNode, const std::function<void(BytecodeGenerator&, RegisterID*)>& callBack, VariableEnvironmentNode* forLoopNode, RegisterID* forLoopSymbolTable) +void BytecodeGenerator::emitEnumeration(ThrowableExpressionData* node, ExpressionNode* subjectNode, const std::function<void(BytecodeGenerator&, RegisterID*)>& callBack) { - RefPtr<RegisterID> subject = newTemporary(); - emitNode(subject.get(), subjectNode); - RefPtr<RegisterID> iterator = emitGetById(newTemporary(), subject.get(), propertyNames().iteratorSymbol); - { - CallArguments args(*this, nullptr); - emitMove(args.thisRegister(), subject.get()); - emitCall(iterator.get(), iterator.get(), NoExpectedFunction, args, node->divot(), node->divotStart(), node->divotEnd()); - } + if (subjectNode->isResolveNode() + && willResolveToArguments(static_cast<ResolveNode*>(subjectNode)->identifier()) + && !symbolTable().slowArguments()) { + RefPtr<RegisterID> index = emitLoad(newTemporary(), jsNumber(0)); - RefPtr<Label> loopDone = newLabel(); - // RefPtr<Register> iterator's lifetime must be longer than IteratorCloseContext. - pushIteratorCloseContext(iterator.get(), node); - { LabelScopePtr scope = newLabelScope(LabelScope::Loop); - RefPtr<RegisterID> value = newTemporary(); - emitLoad(value.get(), jsUndefined()); - + RefPtr<RegisterID> value = emitLoad(newTemporary(), jsUndefined()); + emitJump(scope->continueTarget()); - + RefPtr<Label> loopStart = newLabel(); emitLabel(loopStart.get()); emitLoopHint(); - - RefPtr<Label> tryStartLabel = newLabel(); - emitLabel(tryStartLabel.get()); - TryData* tryData = pushTry(tryStartLabel.get()); + emitGetArgumentByVal(value.get(), uncheckedRegisterForArguments(), index.get()); callBack(*this, value.get()); - emitJump(scope->continueTarget()); - - // IteratorClose sequence for throw-ed control flow. - { - RefPtr<Label> catchHere = emitLabel(newLabel().get()); - RefPtr<RegisterID> exceptionRegister = newTemporary(); - RefPtr<RegisterID> thrownValueRegister = newTemporary(); - popTryAndEmitCatch(tryData, exceptionRegister.get(), - thrownValueRegister.get(), catchHere.get(), HandlerType::SynthesizedFinally); - - RefPtr<Label> catchDone = newLabel(); - - RefPtr<RegisterID> returnMethod = emitGetById(newTemporary(), iterator.get(), propertyNames().returnKeyword); - emitJumpIfTrue(emitIsUndefined(newTemporary(), returnMethod.get()), catchDone.get()); - - RefPtr<Label> returnCallTryStart = newLabel(); - emitLabel(returnCallTryStart.get()); - TryData* returnCallTryData = pushTry(returnCallTryStart.get()); - - CallArguments returnArguments(*this, nullptr); - emitMove(returnArguments.thisRegister(), iterator.get()); - emitCall(value.get(), returnMethod.get(), NoExpectedFunction, returnArguments, node->divot(), node->divotStart(), node->divotEnd()); - - emitLabel(catchDone.get()); - emitThrow(exceptionRegister.get()); - - // Absorb exception. - popTryAndEmitCatch(returnCallTryData, newTemporary(), - newTemporary(), catchDone.get(), HandlerType::SynthesizedFinally); - emitThrow(exceptionRegister.get()); - } - + emitInc(index.get()); emitLabel(scope->continueTarget()); - if (forLoopNode) - prepareLexicalScopeForNextForLoopIteration(forLoopNode, forLoopSymbolTable); - - { - emitIteratorNext(value.get(), iterator.get(), node); - emitJumpIfTrue(emitGetById(newTemporary(), value.get(), propertyNames().done), loopDone.get()); - emitGetById(value.get(), value.get(), propertyNames().value); - emitJump(loopStart.get()); - } + RefPtr<RegisterID> length = emitGetArgumentsLength(newTemporary(), uncheckedRegisterForArguments()); + emitJumpIfTrue(emitEqualityOp(op_less, newTemporary(), index.get(), length.get()), loopStart.get()); emitLabel(scope->breakTarget()); + return; } - // IteratorClose sequence for break-ed control flow. - popIteratorCloseContext(); - emitIteratorClose(iterator.get(), node); - emitLabel(loopDone.get()); -} - -RegisterID* BytecodeGenerator::emitGetTemplateObject(RegisterID* dst, TaggedTemplateNode* taggedTemplate) -{ - TemplateRegistryKey::StringVector rawStrings; - TemplateRegistryKey::StringVector cookedStrings; - - TemplateStringListNode* templateString = taggedTemplate->templateLiteral()->templateStrings(); - for (; templateString; templateString = templateString->next()) { - rawStrings.append(templateString->value()->raw().impl()); - cookedStrings.append(templateString->value()->cooked().impl()); - } - - RefPtr<RegisterID> getTemplateObject = nullptr; - Variable var = variable(propertyNames().getTemplateObjectPrivateName); - if (RegisterID* local = var.local()) - getTemplateObject = emitMove(newTemporary(), local); - else { - getTemplateObject = newTemporary(); - RefPtr<RegisterID> scope = newTemporary(); - moveToDestinationIfNeeded(scope.get(), emitResolveScope(scope.get(), var)); - emitGetFromScope(getTemplateObject.get(), scope.get(), var, ThrowIfNotFound); - } - - CallArguments arguments(*this, nullptr); - emitLoad(arguments.thisRegister(), JSValue(addTemplateRegistryKeyConstant(TemplateRegistryKey(rawStrings, cookedStrings)))); - return emitCall(dst, getTemplateObject.get(), NoExpectedFunction, arguments, taggedTemplate->divot(), taggedTemplate->divotStart(), taggedTemplate->divotEnd()); -} - -RegisterID* BytecodeGenerator::emitGetEnumerableLength(RegisterID* dst, RegisterID* base) -{ - emitOpcode(op_get_enumerable_length); - instructions().append(dst->index()); - instructions().append(base->index()); - return dst; -} - -RegisterID* BytecodeGenerator::emitHasGenericProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName) -{ - emitOpcode(op_has_generic_property); - instructions().append(dst->index()); - instructions().append(base->index()); - instructions().append(propertyName->index()); - return dst; -} - -RegisterID* BytecodeGenerator::emitHasIndexedProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName) -{ - UnlinkedArrayProfile arrayProfile = newArrayProfile(); - emitOpcode(op_has_indexed_property); - instructions().append(dst->index()); - instructions().append(base->index()); - instructions().append(propertyName->index()); - instructions().append(arrayProfile); - return dst; -} - -RegisterID* BytecodeGenerator::emitHasStructureProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName, RegisterID* enumerator) -{ - emitOpcode(op_has_structure_property); - instructions().append(dst->index()); - instructions().append(base->index()); - instructions().append(propertyName->index()); - instructions().append(enumerator->index()); - return dst; -} - -RegisterID* BytecodeGenerator::emitGetPropertyEnumerator(RegisterID* dst, RegisterID* base) -{ - emitOpcode(op_get_property_enumerator); - instructions().append(dst->index()); - instructions().append(base->index()); - return dst; -} - -RegisterID* BytecodeGenerator::emitEnumeratorStructurePropertyName(RegisterID* dst, RegisterID* enumerator, RegisterID* index) -{ - emitOpcode(op_enumerator_structure_pname); - instructions().append(dst->index()); - instructions().append(enumerator->index()); - instructions().append(index->index()); - return dst; -} - -RegisterID* BytecodeGenerator::emitEnumeratorGenericPropertyName(RegisterID* dst, RegisterID* enumerator, RegisterID* index) -{ - emitOpcode(op_enumerator_generic_pname); - instructions().append(dst->index()); - instructions().append(enumerator->index()); - instructions().append(index->index()); - return dst; -} - -RegisterID* BytecodeGenerator::emitToIndexString(RegisterID* dst, RegisterID* index) -{ - emitOpcode(op_to_index_string); - instructions().append(dst->index()); - instructions().append(index->index()); - return dst; -} - - -RegisterID* BytecodeGenerator::emitIsObject(RegisterID* dst, RegisterID* src) -{ - emitOpcode(op_is_object); - instructions().append(dst->index()); - instructions().append(src->index()); - return dst; -} - -RegisterID* BytecodeGenerator::emitIsUndefined(RegisterID* dst, RegisterID* src) -{ - emitOpcode(op_is_undefined); - instructions().append(dst->index()); - instructions().append(src->index()); - return dst; -} - -RegisterID* BytecodeGenerator::emitIteratorNext(RegisterID* dst, RegisterID* iterator, const ThrowableExpressionData* node) -{ - { - RefPtr<RegisterID> next = emitGetById(newTemporary(), iterator, propertyNames().next); - CallArguments nextArguments(*this, nullptr); - emitMove(nextArguments.thisRegister(), iterator); - emitCall(dst, next.get(), NoExpectedFunction, nextArguments, node->divot(), node->divotStart(), node->divotEnd()); - } - { - RefPtr<Label> typeIsObject = newLabel(); - emitJumpIfTrue(emitIsObject(newTemporary(), dst), typeIsObject.get()); - emitThrowTypeError(ASCIILiteral("Iterator result interface is not an object.")); - emitLabel(typeIsObject.get()); - } - return dst; -} - -RegisterID* BytecodeGenerator::emitIteratorNextWithValue(RegisterID* dst, RegisterID* iterator, RegisterID* value, const ThrowableExpressionData* node) -{ - { - RefPtr<RegisterID> next = emitGetById(newTemporary(), iterator, propertyNames().next); - CallArguments nextArguments(*this, nullptr, 1); - emitMove(nextArguments.thisRegister(), iterator); - emitMove(nextArguments.argumentRegister(0), value); - emitCall(dst, next.get(), NoExpectedFunction, nextArguments, node->divot(), node->divotStart(), node->divotEnd()); - } + LabelScopePtr scope = newLabelScope(LabelScope::Loop); + RefPtr<RegisterID> subject = newTemporary(); + emitNode(subject.get(), subjectNode); + RefPtr<RegisterID> iterator = emitGetById(newTemporary(), subject.get(), propertyNames().iteratorPrivateName); { - RefPtr<Label> typeIsObject = newLabel(); - emitJumpIfTrue(emitIsObject(newTemporary(), dst), typeIsObject.get()); - emitThrowTypeError(ASCIILiteral("Iterator result interface is not an object.")); - emitLabel(typeIsObject.get()); + CallArguments args(*this, 0); + emitMove(args.thisRegister(), subject.get()); + emitCall(iterator.get(), iterator.get(), NoExpectedFunction, args, node->divot(), node->divotStart(), node->divotEnd()); } - return dst; -} - -void BytecodeGenerator::emitIteratorClose(RegisterID* iterator, const ThrowableExpressionData* node) -{ - RefPtr<Label> done = newLabel(); - RefPtr<RegisterID> returnMethod = emitGetById(newTemporary(), iterator, propertyNames().returnKeyword); - emitJumpIfTrue(emitIsUndefined(newTemporary(), returnMethod.get()), done.get()); - + RefPtr<RegisterID> iteratorNext = emitGetById(newTemporary(), iterator.get(), propertyNames().iteratorNextPrivateName); RefPtr<RegisterID> value = newTemporary(); - CallArguments returnArguments(*this, nullptr); - emitMove(returnArguments.thisRegister(), iterator); - emitCall(value.get(), returnMethod.get(), NoExpectedFunction, returnArguments, node->divot(), node->divotStart(), node->divotEnd()); - emitJumpIfTrue(emitIsObject(newTemporary(), value.get()), done.get()); - emitThrowTypeError(ASCIILiteral("Iterator result interface is not an object.")); - emitLabel(done.get()); -} - -void BytecodeGenerator::pushIndexedForInScope(RegisterID* localRegister, RegisterID* indexRegister) -{ - if (!localRegister) - return; - m_forInContextStack.append(std::make_unique<IndexedForInContext>(localRegister, indexRegister)); -} - -void BytecodeGenerator::popIndexedForInScope(RegisterID* localRegister) -{ - if (!localRegister) - return; - m_forInContextStack.removeLast(); -} - -RegisterID* BytecodeGenerator::emitLoadArrowFunctionLexicalEnvironment() -{ - ASSERT(m_codeBlock->isArrowFunction() || m_codeBlock->isArrowFunctionContext() || constructorKind() == ConstructorKind::Derived); - - return emitResolveScope(nullptr, variable(propertyNames().thisIdentifier, ThisResolutionType::Scoped)); -} - -void BytecodeGenerator::emitLoadThisFromArrowFunctionLexicalEnvironment() -{ - emitGetFromScope(thisRegister(), emitLoadArrowFunctionLexicalEnvironment(), variable(propertyNames().thisIdentifier, ThisResolutionType::Scoped), DoNotThrowIfNotFound); -} - -RegisterID* BytecodeGenerator::emitLoadNewTargetFromArrowFunctionLexicalEnvironment() -{ - m_isNewTargetLoadedInArrowFunction = true; - - Variable newTargetVar = variable(propertyNames().newTargetLocalPrivateName); - emitMove(m_newTargetRegister, emitGetFromScope(newTemporary(), emitLoadArrowFunctionLexicalEnvironment(), newTargetVar, ThrowIfNotFound)); + emitLoad(value.get(), jsUndefined()); - return m_newTargetRegister; -} - -RegisterID* BytecodeGenerator::emitLoadDerivedConstructorFromArrowFunctionLexicalEnvironment() -{ - Variable protoScopeVar = variable(propertyNames().derivedConstructorPrivateName); - return emitGetFromScope(newTemporary(), emitLoadArrowFunctionLexicalEnvironment(), protoScopeVar, ThrowIfNotFound); -} - -void BytecodeGenerator::emitPutNewTargetToArrowFunctionContextScope() -{ - ASSERT(m_arrowFunctionContextLexicalEnvironmentRegister != nullptr); - - Variable newTargetVar = variable(propertyNames().newTargetLocalPrivateName); - emitPutToScope(m_arrowFunctionContextLexicalEnvironmentRegister, newTargetVar, newTarget(), DoNotThrowIfNotFound, Initialization); -} + emitJump(scope->continueTarget()); -void BytecodeGenerator::emitPutDerivedConstructorToArrowFunctionContextScope() -{ - if ((isConstructor() && constructorKind() == ConstructorKind::Derived) || m_codeBlock->isClassContext()) { - ASSERT(m_arrowFunctionContextLexicalEnvironmentRegister); - - Variable protoScope = variable(propertyNames().derivedConstructorPrivateName); - emitPutToScope(m_arrowFunctionContextLexicalEnvironmentRegister, protoScope, &m_calleeRegister, DoNotThrowIfNotFound, Initialization); - } -} - -void BytecodeGenerator::emitPutThisToArrowFunctionContextScope() -{ - ASSERT(isDerivedConstructorContext() || m_arrowFunctionContextLexicalEnvironmentRegister != nullptr); - - Variable thisVar = variable(propertyNames().thisIdentifier, ThisResolutionType::Scoped); - RegisterID* scope = isDerivedConstructorContext() ? emitLoadArrowFunctionLexicalEnvironment() : m_arrowFunctionContextLexicalEnvironmentRegister; - - emitPutToScope(scope, thisVar, thisRegister(), DoNotThrowIfNotFound, NotInitialization); -} - -void BytecodeGenerator::pushStructureForInScope(RegisterID* localRegister, RegisterID* indexRegister, RegisterID* propertyRegister, RegisterID* enumeratorRegister) -{ - if (!localRegister) - return; - m_forInContextStack.append(std::make_unique<StructureForInContext>(localRegister, indexRegister, propertyRegister, enumeratorRegister)); -} - -void BytecodeGenerator::popStructureForInScope(RegisterID* localRegister) -{ - if (!localRegister) - return; - m_forInContextStack.removeLast(); -} - -void BytecodeGenerator::invalidateForInContextForLocal(RegisterID* localRegister) -{ - // Lexically invalidating ForInContexts is kind of weak sauce, but it only occurs if - // either of the following conditions is true: - // - // (1) The loop iteration variable is re-assigned within the body of the loop. - // (2) The loop iteration variable is captured in the lexical scope of the function. - // - // These two situations occur sufficiently rarely that it's okay to use this style of - // "analysis" to make iteration faster. If we didn't want to do this, we would either have - // to perform some flow-sensitive analysis to see if/when the loop iteration variable was - // reassigned, or we'd have to resort to runtime checks to see if the variable had been - // reassigned from its original value. - for (size_t i = m_forInContextStack.size(); i > 0; i--) { - ForInContext* context = m_forInContextStack[i - 1].get(); - if (context->local() != localRegister) - continue; - context->invalidate(); - break; - } -} - -RegisterID* BytecodeGenerator::emitRestParameter(RegisterID* result, unsigned numParametersToSkip) -{ - RefPtr<RegisterID> restArrayLength = newTemporary(); - emitOpcode(op_get_rest_length); - instructions().append(restArrayLength->index()); - instructions().append(numParametersToSkip); - - emitNewArrayWithSize(result, restArrayLength.get()); - - emitOpcode(op_copy_rest); - instructions().append(result->index()); - instructions().append(restArrayLength->index()); - instructions().append(numParametersToSkip); - - return result; -} - -void BytecodeGenerator::emitRequireObjectCoercible(RegisterID* value, const String& error) -{ - // FIXME: op_jneq_null treats "undetectable" objects as null/undefined. RequireObjectCoercible - // thus incorrectly throws a TypeError for interfaces like HTMLAllCollection. - RefPtr<Label> target = newLabel(); - size_t begin = instructions().size(); - emitOpcode(op_jneq_null); - instructions().append(value->index()); - instructions().append(target->bind(begin, instructions().size())); - emitThrowTypeError(error); - emitLabel(target.get()); -} - -void BytecodeGenerator::emitYieldPoint(RegisterID* argument) -{ - RefPtr<Label> mergePoint = newLabel(); - size_t yieldPointIndex = m_generatorResumeLabels.size(); - emitGeneratorStateChange(yieldPointIndex); - // First yield point is used for initial sequence. - unsigned liveCalleeLocalsIndex = yieldPointIndex - 1; - emitSave(mergePoint.get(), liveCalleeLocalsIndex); - emitReturn(argument); - emitResume(mergePoint.get(), liveCalleeLocalsIndex); -} - -void BytecodeGenerator::emitSave(Label* mergePoint, unsigned liveCalleeLocalsIndex) -{ - size_t begin = instructions().size(); - emitOpcode(op_save); - instructions().append(m_generatorRegister->index()); - instructions().append(liveCalleeLocalsIndex); - instructions().append(mergePoint->bind(begin, instructions().size())); -} - -void BytecodeGenerator::emitResume(Label* mergePoint, unsigned liveCalleeLocalsIndex) -{ - emitGeneratorStateLabel(); - emitOpcode(op_resume); - instructions().append(m_generatorRegister->index()); - instructions().append(liveCalleeLocalsIndex); - emitLabel(mergePoint); -} - -RegisterID* BytecodeGenerator::emitYield(RegisterID* argument) -{ - emitYieldPoint(argument); - - RefPtr<Label> normalLabel = newLabel(); - RefPtr<RegisterID> condition = newTemporary(); - emitEqualityOp(op_stricteq, condition.get(), generatorResumeModeRegister(), emitLoad(nullptr, jsNumber(static_cast<int32_t>(JSGeneratorFunction::GeneratorResumeMode::NormalMode)))); - emitJumpIfTrue(condition.get(), normalLabel.get()); - - RefPtr<Label> throwLabel = newLabel(); - emitEqualityOp(op_stricteq, condition.get(), generatorResumeModeRegister(), emitLoad(nullptr, jsNumber(static_cast<int32_t>(JSGeneratorFunction::GeneratorResumeMode::ThrowMode)))); - emitJumpIfTrue(condition.get(), throwLabel.get()); - // Return. - { - RefPtr<RegisterID> returnRegister = generatorValueRegister(); - if (isInFinallyBlock()) { - returnRegister = emitMove(newTemporary(), returnRegister.get()); - emitPopScopes(scopeRegister(), 0); - } - emitReturn(returnRegister.get()); - } - - // Throw. - emitLabel(throwLabel.get()); - emitThrow(generatorValueRegister()); - - // Normal. - emitLabel(normalLabel.get()); - return generatorValueRegister(); -} - -RegisterID* BytecodeGenerator::emitDelegateYield(RegisterID* argument, ThrowableExpressionData* node) -{ - RefPtr<RegisterID> value = newTemporary(); - { - RefPtr<RegisterID> iterator = emitGetById(newTemporary(), argument, propertyNames().iteratorSymbol); - { - CallArguments args(*this, nullptr); - emitMove(args.thisRegister(), argument); - emitCall(iterator.get(), iterator.get(), NoExpectedFunction, args, node->divot(), node->divotStart(), node->divotEnd()); - } - - RefPtr<Label> loopDone = newLabel(); - { - RefPtr<Label> nextElement = newLabel(); - emitLoad(value.get(), jsUndefined()); - - emitJump(nextElement.get()); - - RefPtr<Label> loopStart = newLabel(); - emitLabel(loopStart.get()); - emitLoopHint(); - - { - emitYieldPoint(value.get()); - - RefPtr<Label> normalLabel = newLabel(); - RefPtr<Label> returnLabel = newLabel(); - { - RefPtr<RegisterID> condition = newTemporary(); - emitEqualityOp(op_stricteq, condition.get(), generatorResumeModeRegister(), emitLoad(nullptr, jsNumber(static_cast<int32_t>(JSGeneratorFunction::GeneratorResumeMode::NormalMode)))); - emitJumpIfTrue(condition.get(), normalLabel.get()); - - emitEqualityOp(op_stricteq, condition.get(), generatorResumeModeRegister(), emitLoad(nullptr, jsNumber(static_cast<int32_t>(JSGeneratorFunction::GeneratorResumeMode::ReturnMode)))); - emitJumpIfTrue(condition.get(), returnLabel.get()); - - // Fallthrough to ThrowMode. - } - - RefPtr<Label> returnSequence = newLabel(); - RefPtr<Label> returnWithIteratorResult = newLabel(); - RefPtr<RegisterID> returnIteratorResult = newTemporary(); - // Throw. - { - RefPtr<Label> throwMethodFound = newLabel(); - RefPtr<RegisterID> throwMethod = emitGetById(newTemporary(), iterator.get(), propertyNames().throwKeyword); - emitJumpIfFalse(emitIsUndefined(newTemporary(), throwMethod.get()), throwMethodFound.get()); - - emitIteratorClose(iterator.get(), node); - emitThrowTypeError(ASCIILiteral("Delegated generator does not have a 'throw' method.")); - - emitLabel(throwMethodFound.get()); - CallArguments throwArguments(*this, nullptr, 1); - emitMove(throwArguments.thisRegister(), iterator.get()); - emitMove(throwArguments.argumentRegister(0), generatorValueRegister()); - emitCall(returnIteratorResult.get(), throwMethod.get(), NoExpectedFunction, throwArguments, node->divot(), node->divotStart(), node->divotEnd()); - emitJump(returnWithIteratorResult.get()); - } - - // Return. - emitLabel(returnLabel.get()); - { - RefPtr<Label> returnMethodFound = newLabel(); - RefPtr<RegisterID> returnMethod = emitGetById(newTemporary(), iterator.get(), propertyNames().returnKeyword); - emitJumpIfFalse(emitIsUndefined(newTemporary(), returnMethod.get()), returnMethodFound.get()); - - emitMove(value.get(), generatorValueRegister()); - emitJump(returnSequence.get()); - - emitLabel(returnMethodFound.get()); - CallArguments returnArguments(*this, nullptr, 1); - emitMove(returnArguments.thisRegister(), iterator.get()); - emitMove(returnArguments.argumentRegister(0), generatorValueRegister()); - emitCall(returnIteratorResult.get(), returnMethod.get(), NoExpectedFunction, returnArguments, node->divot(), node->divotStart(), node->divotEnd()); - - // Fallthrough to returnWithIteratorResult. - } - - emitLabel(returnWithIteratorResult.get()); - { - RefPtr<Label> returnIteratorResultIsObject = newLabel(); - emitJumpIfTrue(emitIsObject(newTemporary(), returnIteratorResult.get()), returnIteratorResultIsObject.get()); - emitThrowTypeError(ASCIILiteral("Iterator result interface is not an object.")); - - emitLabel(returnIteratorResultIsObject.get()); - RefPtr<Label> returnFromGenerator = newLabel(); - emitJumpIfTrue(emitGetById(newTemporary(), returnIteratorResult.get(), propertyNames().done), returnFromGenerator.get()); - - emitGetById(value.get(), returnIteratorResult.get(), propertyNames().value); - emitJump(loopStart.get()); - - emitLabel(returnFromGenerator.get()); - emitGetById(value.get(), returnIteratorResult.get(), propertyNames().value); - - // Fallthrough to returnSequence. - } - - emitLabel(returnSequence.get()); - { - if (isInFinallyBlock()) - emitPopScopes(scopeRegister(), 0); - emitReturn(value.get()); - } - - // Normal. - emitLabel(normalLabel.get()); - emitMove(value.get(), generatorValueRegister()); - } - - emitLabel(nextElement.get()); - { - emitIteratorNextWithValue(value.get(), iterator.get(), value.get(), node); - emitJumpIfTrue(emitGetById(newTemporary(), value.get(), propertyNames().done), loopDone.get()); - emitGetById(value.get(), value.get(), propertyNames().value); - emitJump(loopStart.get()); - } - } - emitLabel(loopDone.get()); - } - - emitGetById(value.get(), value.get(), propertyNames().value); - return value.get(); -} - - -void BytecodeGenerator::emitGeneratorStateChange(int32_t state) -{ - RegisterID* completedState = emitLoad(nullptr, jsNumber(state)); - emitPutById(generatorRegister(), propertyNames().generatorStatePrivateName, completedState); -} - -void BytecodeGenerator::emitGeneratorStateLabel() -{ - RefPtr<Label> label = newLabel(); - m_generatorResumeLabels.append(label.get()); - emitLabel(label.get()); -} - -void BytecodeGenerator::beginGenerator(RegisterID* state) -{ - beginSwitch(state, SwitchInfo::SwitchImmediate); -} - -void BytecodeGenerator::endGenerator(Label* defaultLabel) -{ - SwitchInfo switchInfo = m_switchContextStack.last(); - m_switchContextStack.removeLast(); - - instructions()[switchInfo.bytecodeOffset + 1] = m_codeBlock->numberOfSwitchJumpTables(); - instructions()[switchInfo.bytecodeOffset + 2] = defaultLabel->bind(switchInfo.bytecodeOffset, switchInfo.bytecodeOffset + 3); - - UnlinkedSimpleJumpTable& jumpTable = m_codeBlock->addSwitchJumpTable(); - int32_t switchAddress = switchInfo.bytecodeOffset; - jumpTable.min = 0; - jumpTable.branchOffsets.resize(m_generatorResumeLabels.size() + 1); - jumpTable.branchOffsets.fill(0); - for (uint32_t i = 0; i < m_generatorResumeLabels.size(); ++i) { - // We're emitting this after the clause labels should have been fixed, so - // the labels should not be "forward" references - ASSERT(!m_generatorResumeLabels[i]->isForward()); - jumpTable.add(i, m_generatorResumeLabels[i]->bind(switchAddress, switchAddress + 3)); - } + RefPtr<Label> loopStart = newLabel(); + emitLabel(loopStart.get()); + emitLoopHint(); + callBack(*this, value.get()); + emitLabel(scope->continueTarget()); + CallArguments nextArguments(*this, 0, 1); + emitMove(nextArguments.thisRegister(), iterator.get()); + emitMove(nextArguments.argumentRegister(0), value.get()); + emitCall(value.get(), iteratorNext.get(), NoExpectedFunction, nextArguments, node->divot(), node->divotStart(), node->divotEnd()); + RefPtr<RegisterID> result = newTemporary(); + emitJumpIfFalse(emitEqualityOp(op_stricteq, result.get(), value.get(), emitLoad(0, JSValue(vm()->iterationTerminator.get()))), loopStart.get()); + emitLabel(scope->breakTarget()); } } // namespace JSC |