diff options
Diffstat (limited to 'Source/JavaScriptCore/bytecompiler')
-rw-r--r-- | Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp | 2480 | ||||
-rw-r--r-- | Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h | 549 | ||||
-rw-r--r-- | Source/JavaScriptCore/bytecompiler/Label.h | 6 | ||||
-rw-r--r-- | Source/JavaScriptCore/bytecompiler/LabelScope.h | 10 | ||||
-rw-r--r-- | Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp | 1782 | ||||
-rw-r--r-- | Source/JavaScriptCore/bytecompiler/RegisterID.h | 3 | ||||
-rw-r--r-- | Source/JavaScriptCore/bytecompiler/StaticPropertyAnalysis.h | 4 |
7 files changed, 1396 insertions, 3438 deletions
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp index 60e060553..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,13 +31,12 @@ #include "config.h" #include "BytecodeGenerator.h" -#include "BuiltinExecutables.h" #include "Interpreter.h" +#include "JSActivation.h" #include "JSFunction.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" @@ -56,41 +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); + m_codeBlock->setThisRegister(m_thisRegister.virtualRegister()); - pushLexicalScope(m_scopeNode, true); - - { - RefPtr<RegisterID> temp = newTemporary(); - RefPtr<RegisterID> globalScope = m_topMostScope; - 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) - emitPutToScope(globalScope.get(), Variable(metadata->ident()), temp.get(), ThrowIfNotFound); - else - RELEASE_ASSERT_NOT_REACHED(); - } - } - - bool callingClassConstructor = constructorKind() != ConstructorKind::None && !isConstructor(); - if (!callingClassConstructor) - m_scopeNode->emitBytecode(*this); + m_scopeNode->emitBytecode(*this); m_staticPropertyAnalyzer.kill(); @@ -124,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); } @@ -139,524 +116,448 @@ 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_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; + if (m_shouldEmitDebugHooks) + m_codeBlock->setNeedsFullScopeChain(true); m_codeBlock->setNumParameters(1); // Allocate space for "this" emitOpcode(op_enter); - allocateAndEmitScope(); - + 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)); + FunctionBodyNode* function = functionStack[i]; + UnlinkedFunctionExecutable* unlinkedFunction = makeFunction(function); + codeBlock->addFunctionDeclaration(*m_vm, function->ident(), unlinkedFunction); } - if (Options::validateBytecode()) { - for (auto& entry : programNode->varDeclarations()) - RELEASE_ASSERT(entry.value.isVar()); - } - codeBlock->setVariableDeclarations(programNode->varDeclarations()); + + 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()) + , 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; - - 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; } - - bool shouldCaptureSomeOfTheThings = m_shouldEmitDebugHooks || m_codeBlock->needsFullScopeChain(); - bool shouldCaptureAllOfTheThings = m_shouldEmitDebugHooks || codeBlock->usesEval(); - bool needsArguments = functionNode->usesArguments() || codeBlock->usesEval(); - 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; - }; + m_symbolTable->setParameterCountIncludingThis(functionBody->parameters()->size() + 1); emitOpcode(op_enter); - - allocateAndEmitScope(); - - m_calleeRegister.setIndex(JSStack::Callee); - - if (functionNameIsInScope(functionNode->ident(), functionNode->functionMode()) - && functionNameScopeIsDynamic(codeBlock->usesEval(), codeBlock->isStrictMode())) { - emitPushFunctionNameScope(functionNode->ident(), &m_calleeRegister); + if (m_codeBlock->needsFullScopeChain()) { + m_activationRegister = addVar(); + emitInitLazyRegister(m_activationRegister); + m_codeBlock->setActivationRegister(m_activationRegister->virtualRegister()); } - - 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); + + m_symbolTable->setCaptureStart(virtualRegisterForLocal(m_codeBlock->m_numVars).offset()); + + 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'. + + // 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())); + + emitInitLazyRegister(argumentsRegister); + emitInitLazyRegister(unmodifiedArgumentsRegister); + + if (shouldTearOffArgumentsEagerly()) { + emitOpcode(op_create_arguments); + instructions().append(argumentsRegister->index()); + } } - // 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) - initializeNextParameter(); - - // Figure out some interesting facts about our arguments. + bool shouldCaptureAllTheThings = m_shouldEmitDebugHooks || codeBlock->usesEval(); + 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(); - } - - if (needsArguments && !codeBlock->isStrictMode() && !parameters.hasDefaultParameterValues()) { - // 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(ResolveModeAndType(ThrowIfNotFound, LocalClosureVar).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(ResolveModeAndType(ThrowIfNotFound, LocalClosureVar).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() || parameters.hasDefaultParameterValues())) { - // 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); - } - for (FunctionMetadataNode* function : functionNode->functionStack()) { - const Identifier& ident = function->ident(); - createVariable(ident, varKind(ident.impl()), functionSymbolTable); - m_functionsToInitialize.append(std::make_pair(function, NormalFunctionVariable)); - } - for (auto& entry : functionNode->varDeclarations()) { - ASSERT(!entry.value.isLet() && !entry.value.isConst()); - if (!entry.value.isVar()) // This is either a parameter or callee. - continue; - // Variables named "arguments" are never const. - createVariable(Identifier::fromUid(m_vm, entry.key.get()), varKind(entry.key.get()), functionSymbolTable, IgnoreExisting); + bool canLazilyCreateFunctions = !functionBody->needsActivationForMoreThanVariables() && !m_shouldEmitDebugHooks; + if (!canLazilyCreateFunctions && !m_hasCreatedActivation) { + m_hasCreatedActivation = true; + emitOpcode(op_create_activation); + instructions().append(m_activationRegister->index()); } - // There are some variables that need to be preinitialized to something other than Undefined: - // - // - "arguments": unless it's used as a function or parameter, this should refer to the - // arguments object. - // - // - callee: unless it's used as a var, function, or parameter, this should refer to the - // callee (i.e. our function). - // - // - 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 the callee - // first, then "arguments", 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). - // - // We also initialize callee here as well, just because it's so weird. We know whether we want - // to do this because we can just check if it's in the symbol table. - if (functionNameIsInScope(functionNode->ident(), functionNode->functionMode()) - && !functionNameScopeIsDynamic(codeBlock->usesEval(), codeBlock->isStrictMode()) - && functionSymbolTable->get(functionNode->ident().impl()).isNull()) { - if (captures(functionNode->ident().impl())) { - ScopeOffset offset; - { - ConcurrentJITLocker locker(functionSymbolTable->m_lock); - offset = functionSymbolTable->takeNextScopeOffset(locker); - functionSymbolTable->add( - locker, functionNode->ident().impl(), - SymbolTableEntry(VarOffset(offset), ReadOnly)); + m_symbolTable->setCaptureEnd(virtualRegisterForLocal(codeBlock->m_numVars).offset()); + + m_firstLazyFunction = codeBlock->m_numVars; + for (size_t i = 0; i < functionStack.size(); ++i) { + FunctionBodyNode* function = functionStack[i]; + const Identifier& ident = function->ident(); + if (!functionBody->captures(ident)) { + m_functions.add(ident.impl()); + RefPtr<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); } - - emitOpcode(op_put_to_scope); - instructions().append(m_lexicalEnvironmentRegister->index()); - instructions().append(addConstant(functionNode->ident())); - instructions().append(m_calleeRegister.index()); - instructions().append(ResolveModeAndType(ThrowIfNotFound, LocalClosureVar).operand()); - instructions().append(symbolTableConstantIndex); - instructions().append(offset.offset()); - } else { - functionSymbolTable->add( - functionNode->ident().impl(), - SymbolTableEntry(VarOffset(m_calleeRegister.virtualRegister()), ReadOnly)); } } + 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); + } + + if (shouldCaptureAllTheThings) + m_symbolTable->setCaptureEnd(virtualRegisterForLocal(codeBlock->m_numVars).offset()); + + if (m_symbolTable->captureCount()) + emitOpcode(op_touch_entry); - // 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; - } + m_parameters.grow(parameters.size() + 1); // reserve space for "this" + + // 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; } - - if (!haveParameterNamedArguments) { - createVariable( - propertyNames().arguments, varKind(propertyNames().arguments.impl()), functionSymbolTable); - m_needToInitializeArguments = true; + 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(); + + // We declare the callee's name last because it should lose to a var, function, and/or parameter declaration. + addCallee(functionBody, calleeRegister); - m_newTargetRegister = addVar(); 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()) { + 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); } - - // 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); + for (size_t i = 0; i < deconstructedParameters.size(); i++) { + auto& entry = deconstructedParameters[i]; + entry.second->bindValue(*this, entry.first); + } } -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_lastOpcodeID(op_end) +#ifndef NDEBUG + , m_lastOpcodePosition(0) +#endif + , m_usesExceptions(false) + , m_expressionTooDeep(false) { - for (auto& constantRegister : m_linkTimeConstantRegisters) - constantRegister = nullptr; + m_codeBlock->setNeedsFullScopeChain(true); + m_symbolTable->setUsesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode()); m_codeBlock->setNumParameters(1); emitOpcode(op_enter); - allocateAndEmitScope(); - const DeclarationStacks::FunctionStack& functionStack = evalNode->functionStack(); for (size_t i = 0; i < functionStack.size(); ++i) m_codeBlock->addFunctionDecl(makeFunction(functionStack[i])); - const VariableEnvironment& varDeclarations = evalNode->varDeclarations(); - unsigned numVariables = varDeclarations.size(); + 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)); + preserveLastVar(); } BytecodeGenerator::~BytecodeGenerator() { } -void BytecodeGenerator::initializeDefaultParameterValuesAndSetupFunctionScopeStack( - FunctionParameters& parameters, FunctionNode* functionNode, SymbolTable* functionSymbolTable, - int symbolTableConstantIndex, const std::function<bool (UniquedStringImpl*)>& captures) -{ - 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, true, 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); - 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()); - } +RegisterID* BytecodeGenerator::emitInitLazyRegister(RegisterID* reg) +{ + emitOpcode(op_init_lazy_reg); + instructions().append(reg->index()); + ASSERT(!hasWatchableVariable(reg->index())); + return reg; +} - parameter.first->bindValue(*this, temp.get()); - } +RegisterID* BytecodeGenerator::resolveCallee(FunctionBodyNode* functionBodyNode) +{ + if (functionBodyNode->ident().isNull() || !functionBodyNode->functionNameIsInScope()) + return 0; - // 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; + m_calleeRegister.setIndex(JSStack::Callee); - 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)); - } - } + // 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); - // 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); - } + if (!functionBodyNode->captures(functionBodyNode->ident())) + return &m_calleeRegister; - if (m_lexicalEnvironmentRegister) - pushScopedControlFlowContext(); - m_symbolTableStack.append(SymbolTableStackEntry{ Strong<SymbolTable>(*m_vm, functionSymbolTable), m_lexicalEnvironmentRegister, false, symbolTableConstantIndex }); + // Move the callee into the captured section of the stack. + return emitMove(addVar(), IsCaptured, &m_calleeRegister); +} - // 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); - } +void BytecodeGenerator::addCallee(FunctionBodyNode* functionBodyNode, RegisterID* calleeRegister) +{ + if (functionBodyNode->ident().isNull() || !functionBodyNode->functionNameIsInScope()) + return; - 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()) { - RefPtr<RegisterID> parameterValue = ®isterFor(virtualRegisterForArgument(1 + i)); - pattern->bindValue(*this, parameterValue.get()); - } - } - } + // 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; + + 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::initializeVarLexicalEnvironment(int symbolTableConstantIndex) +bool BytecodeGenerator::willResolveToArguments(const Identifier& ident) +{ + if (ident != propertyNames().arguments) + return false; + + if (!shouldOptimizeLocals()) + return false; + + SymbolTableEntry entry = symbolTable().get(ident.impl()); + if (entry.isNull()) + return false; + + if (m_codeBlock->usesArguments() && m_codeType == FunctionCode) + return true; + + return false; +} + +RegisterID* BytecodeGenerator::uncheckedRegisterForArguments() { - RELEASE_ASSERT(m_lexicalEnvironmentRegister); - m_codeBlock->setActivationRegister(m_lexicalEnvironmentRegister->virtualRegister()); - emitOpcode(op_create_lexical_environment); - instructions().append(m_lexicalEnvironmentRegister->index()); - instructions().append(scopeRegister()->index()); - instructions().append(symbolTableConstantIndex); - instructions().append(addConstantValue(jsUndefined())->index()); + ASSERT(willResolveToArguments(propertyNames().arguments)); - emitOpcode(op_mov); - instructions().append(scopeRegister()->index()); - instructions().append(m_lexicalEnvironmentRegister->index()); + SymbolTableEntry entry = symbolTable().get(propertyNames().arguments.impl()); + ASSERT(!entry.isNull()); + return ®isterFor(entry.getIndex()); } -UniquedStringImpl* BytecodeGenerator::visibleNameForParameter(DestructuringPatternNode* pattern) +RegisterID* BytecodeGenerator::createLazyRegisterIfNecessary(RegisterID* reg) { - if (pattern->isBindingNode()) { - const Identifier& ident = static_cast<const BindingNode*>(pattern)->boundProperty(); - if (!m_functions.contains(ident.impl())) - return ident.impl(); - } - return nullptr; + 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::newRegister() @@ -668,23 +569,12 @@ RegisterID* BytecodeGenerator::newRegister() return &m_calleeRegisters.last(); } -void BytecodeGenerator::reclaimFreeRegisters() +RegisterID* BytecodeGenerator::newTemporary() { + // Reclaim free register IDs. while (m_calleeRegisters.size() && !m_calleeRegisters.last().refCount()) m_calleeRegisters.removeLast(); -} - -RegisterID* BytecodeGenerator::newBlockScopeVariable() -{ - reclaimFreeRegisters(); - - return newRegister(); -} - -RegisterID* BytecodeGenerator::newTemporary() -{ - reclaimFreeRegisters(); - + RegisterID* result = newRegister(); result->setTemporary(); return result; @@ -697,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() @@ -709,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(); } @@ -1067,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); @@ -1097,67 +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; - - 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) @@ -1239,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; @@ -1261,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) @@ -1369,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; @@ -1392,329 +1169,41 @@ RegisterID* BytecodeGenerator::emitLoadGlobalObject(RegisterID* dst) return m_globalObjectRegister; } -void BytecodeGenerator::pushLexicalScope(VariableEnvironmentNode* node, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult) -{ - VariableEnvironment& environment = node->lexicalVariables(); - pushLexicalScopeInternal(environment, canOptimizeTDZChecks, constantSymbolTableResult, TDZRequirement::UnderTDZ, ScopeType::LetConstScope, ScopeRegisterType::Block); -} - -void BytecodeGenerator::pushLexicalScopeInternal(VariableEnvironment& environment, bool canOptimizeTDZChecks, - 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; - } - - bool hasCapturedVariables = false; - { - ConcurrentJITLocker locker(symbolTable->m_lock); - for (auto& entry : environment) { - ASSERT(entry.value.isLet() || entry.value.isConst()); - ASSERT(!entry.value.isVar()); - SymbolTableEntry symbolTableEntry = symbolTable->get(locker, entry.key.get()); - ASSERT(symbolTableEntry.isNull()); - - VarKind varKind = entry.value.isCaptured() ? VarKind::Scope : VarKind::Stack; - VarOffset varOffset; - if (varKind == VarKind::Scope) { - varOffset = VarOffset(symbolTable->takeNextScopeOffset(locker)); - hasCapturedVariables = true; - } else { - ASSERT(varKind == VarKind::Stack); - RegisterID* local = newBlockScopeVariable(); - local->ref(); - varOffset = VarOffset(local->virtualRegister()); - } - - SymbolTableEntry newEntry(varOffset, entry.value.isConst() ? ReadOnly : 0); - symbolTable->add(locker, entry.key.get(), newEntry); - } - } - - 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 }); - if (tdzRequirement == TDZRequirement::UnderTDZ) - m_TDZStack.append(std::make_pair(environment, canOptimizeTDZChecks)); - - if (tdzRequirement == TDZRequirement::UnderTDZ) { - // 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 : environment) { - SymbolTableEntry symbolTableEntry = symbolTable->get(entry.key.get()); - ASSERT(!symbolTableEntry.isNull()); - VarOffset offset = symbolTableEntry.varOffset(); - if (offset.isScope()) { - ASSERT(newScope); - continue; - } - ASSERT(offset.isStack()); - emitMoveEmptyValue(®isterFor(offset.stackOffset())); - } - } -} - -void BytecodeGenerator::popLexicalScope(VariableEnvironmentNode* node) +bool BytecodeGenerator::isCaptured(int operand) { - VariableEnvironment& environment = node->lexicalVariables(); - popLexicalScopeInternal(environment, TDZRequirement::UnderTDZ); -} - -void BytecodeGenerator::popLexicalScopeInternal(VariableEnvironment& environment, TDZRequirement tdzRequirement) -{ - 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); - transitionValue->deref(); - } - } + return m_symbolTable && m_symbolTable->isCaptured(operand); } -Variable BytecodeGenerator::variable(const Identifier& property) +Local BytecodeGenerator::local(const Identifier& property) { - if (property == propertyNames().thisIdentifier) { - 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; - if (symbolTable->scopeType() == SymbolTable::ScopeType::FunctionNameScope && 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. - return Variable(property); - } - return variableForLocalEntry(property, symbolTableEntry, stackEntry.m_symbolTableConstantIndex, symbolTable->scopeType() == SymbolTable::ScopeType::LexicalScope); - } + 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_calleeRegisters.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())); } void BytecodeGenerator::emitCheckHasInstance(RegisterID* dst, RegisterID* value, RegisterID* base, Label* target) @@ -1731,158 +1220,62 @@ void BytecodeGenerator::emitCheckHasInstance(RegisterID* dst, RegisterID* value, // 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) { - // What we really want here is something like LocalClosureVarWithVarInjectionsCheck but it's probably - // not worth inventing just for the function name scope. - 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, ResolveModeAndType, Structure, Operand - UnlinkedValueProfile profile = emitProfiledOpcode(op_get_from_scope); - instructions().append(kill(dst)); - instructions().append(scope->index()); - instructions().append(addConstant(variable.ident())); - instructions().append(ResolveModeAndType(resolveMode, variable.offset().isScope() ? LocalClosureVar : resolveType()).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) +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, ResolveModeAndType, 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(ResolveModeAndType(resolveMode, LocalClosureVar).operand()); - instructions().append(variable.symbolTableConstantIndex()); - } else { - ASSERT(resolveType() != LocalClosureVar); - instructions().append(ResolveModeAndType(resolveMode, resolveType()).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); + 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()); @@ -1890,6 +1283,17 @@ RegisterID* BytecodeGenerator::emitInstanceOf(RegisterID* dst, RegisterID* value return dst; } +RegisterID* BytecodeGenerator::emitInitGlobalConst(const Identifier& identifier, RegisterID* value) +{ + ASSERT(m_codeType == GlobalCode); + emitOpcode(op_init_global_const_nop); + instructions().append(0); + instructions().append(value->index()); + instructions().append(0); + instructions().append(addConstant(identifier)); + return value; +} + RegisterID* BytecodeGenerator::emitGetById(RegisterID* dst, RegisterID* base, const Identifier& property) { m_codeBlock->addPropertyAccessInstruction(instructions().size()); @@ -1906,6 +1310,16 @@ RegisterID* BytecodeGenerator::emitGetById(RegisterID* dst, RegisterID* base, co return dst; } +RegisterID* BytecodeGenerator::emitGetArgumentsLength(RegisterID* dst, RegisterID* base) +{ + 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); @@ -1923,13 +1337,11 @@ RegisterID* BytecodeGenerator::emitPutById(RegisterID* base, const Identifier& p 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(!parseIndex(property)); unsigned propertyIndex = addConstant(property); m_staticPropertyAnalyzer.putById(base->index(), propertyIndex); @@ -1944,32 +1356,12 @@ RegisterID* BytecodeGenerator::emitDirectPutById(RegisterID* base, const Identif instructions().append(0); instructions().append(0); instructions().append(0); - instructions().append(putType == PropertyNode::KnownDirect || property != m_vm->propertyNames->underscoreProto); + instructions().append( + property != m_vm->propertyNames->underscoreProto + && PropertyName(property).asIndex() == PropertyName::NotAnIndex); return value; } -void BytecodeGenerator::emitPutGetterById(RegisterID* base, const Identifier& property, 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(getter->index()); -} - -void BytecodeGenerator::emitPutSetterById(RegisterID* base, const Identifier& property, 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(setter->index()); -} - void BytecodeGenerator::emitPutGetterSetter(RegisterID* base, const Identifier& property, RegisterID* getter, RegisterID* setter) { unsigned propertyIndex = addConstant(property); @@ -1992,33 +1384,34 @@ RegisterID* BytecodeGenerator::emitDeleteById(RegisterID* dst, RegisterID* base, return dst; } +RegisterID* BytecodeGenerator::emitGetArgumentByVal(RegisterID* dst, RegisterID* base, RegisterID* property) +{ + 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(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)); @@ -2037,7 +1430,6 @@ RegisterID* BytecodeGenerator::emitPutByVal(RegisterID* base, RegisterID* proper instructions().append(property->index()); instructions().append(value->index()); instructions().append(arrayProfile); - return value; } @@ -2072,73 +1464,23 @@ RegisterID* BytecodeGenerator::emitPutByIndex(RegisterID* base, unsigned index, RegisterID* BytecodeGenerator::emitCreateThis(RegisterID* dst) { + RefPtr<RegisterID> func = newTemporary(); + + m_codeBlock->addPropertyAccessInstruction(instructions().size()); + emitOpcode(op_get_callee); + instructions().append(func->index()); + instructions().append(0); + size_t begin = instructions().size(); m_staticPropertyAnalyzer.createThis(m_thisRegister.index(), begin + 3); - m_codeBlock->addPropertyAccessInstruction(instructions().size()); emitOpcode(op_create_this); instructions().append(m_thisRegister.index()); - instructions().append(m_thisRegister.index()); - instructions().append(0); + 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; - } - } -} - -void BytecodeGenerator::getVariablesUnderTDZ(VariableEnvironment& result) -{ - for (auto& pair : m_TDZStack) { - VariableEnvironment& environment = pair.first; - for (auto entry : environment) - result.add(entry.key.get()); - } -} - RegisterID* BytecodeGenerator::emitNewObject(RegisterID* dst) { size_t begin = instructions().size(); @@ -2166,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 @@ -2232,17 +1564,30 @@ RegisterID* BytecodeGenerator::emitNewArray(RegisterID* dst, ElementNode* elemen return dst; } -RegisterID* BytecodeGenerator::emitNewFunction(RegisterID* dst, FunctionMetadataNode* function) +RegisterID* BytecodeGenerator::emitNewFunction(RegisterID* dst, CaptureMode captureMode, FunctionBodyNode* function) { - return emitNewFunctionInternal(dst, m_codeBlock->addFunctionDecl(makeFunction(function))); + return emitNewFunctionInternal(dst, captureMode, m_codeBlock->addFunctionDecl(makeFunction(function)), false); } -RegisterID* BytecodeGenerator::emitNewFunctionInternal(RegisterID* dst, unsigned index) +RegisterID* BytecodeGenerator::emitLazyNewFunction(RegisterID* dst, FunctionBodyNode* function) { - emitOpcode(op_new_func); + 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); +} + +RegisterID* BytecodeGenerator::emitNewFunctionInternal(RegisterID* dst, CaptureMode captureMode, unsigned index, bool doNullCheck) +{ + createActivationIfNecessary(); + emitOpcode(captureMode == IsCaptured ? op_new_captured_func : op_new_func); instructions().append(dst->index()); - instructions().append(scopeRegister()->index()); instructions().append(index); + if (captureMode == IsCaptured) { + ASSERT(!doNullCheck); + instructions().append(watchableVariable(dst->index())); + } else + instructions().append(doNullCheck); return dst; } @@ -2256,32 +1601,45 @@ RegisterID* BytecodeGenerator::emitNewRegExp(RegisterID* dst, RegExp* regExp) RegisterID* BytecodeGenerator::emitNewFunctionExpression(RegisterID* r0, FuncExprNode* n) { - FunctionMetadataNode* metadata = n->metadata(); - unsigned index = m_codeBlock->addFunctionExpr(makeFunction(metadata)); - + FunctionBodyNode* function = n->body(); + unsigned index = m_codeBlock->addFunctionExpr(makeFunction(function)); + + createActivationIfNecessary(); emitOpcode(op_new_func_exp); instructions().append(r0->index()); - instructions().append(scopeRegister()->index()); instructions().append(index); return r0; } -RegisterID* BytecodeGenerator::emitNewDefaultConstructor(RegisterID* dst, ConstructorKind constructorKind, const Identifier& name) +RegisterID* BytecodeGenerator::emitCall(RegisterID* dst, RegisterID* func, ExpectedFunction expectedFunction, CallArguments& callArguments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) { - UnlinkedFunctionExecutable* executable = m_vm->builtinExecutables()->createDefaultConstructor(constructorKind, name); + return emitCall(op_call, dst, func, expectedFunction, callArguments, divot, divotStart, divotEnd); +} - unsigned index = m_codeBlock->addFunctionExpr(executable); +void BytecodeGenerator::createArgumentsIfNecessary() +{ + if (m_codeType != FunctionCode) + return; + + if (!m_codeBlock->usesArguments()) + return; - emitOpcode(op_new_func_exp); - instructions().append(dst->index()); - instructions().append(scopeRegister()->index()); - instructions().append(index); - return dst; + if (shouldTearOffArgumentsEagerly()) + return; + + emitOpcode(op_create_arguments); + instructions().append(m_codeBlock->argumentsRegister().offset()); + ASSERT(!hasWatchableVariable(m_codeBlock->argumentsRegister().offset())); } -RegisterID* BytecodeGenerator::emitCall(RegisterID* dst, RegisterID* func, ExpectedFunction expectedFunction, CallArguments& callArguments, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) +void BytecodeGenerator::createActivationIfNecessary() { - return emitCall(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) @@ -2291,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; } @@ -2379,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(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); @@ -2412,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); @@ -2428,17 +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::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); @@ -2450,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) { @@ -2467,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); } @@ -2546,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); } @@ -2581,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); @@ -2614,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) @@ -2686,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 }; @@ -2732,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. // @@ -2768,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: @@ -2778,68 +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(); -} - -void BytecodeGenerator::allocateAndEmitScope() -{ - m_scopeRegister = addVar(); - m_scopeRegister->ref(); - m_codeBlock->setScopeRegister(scopeRegister()->virtualRegister()); - emitGetScope(); - m_topMostScope = addVar(); - emitMove(m_topMostScope, scopeRegister()); + return 0; } -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 @@ -2855,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) @@ -2868,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()); @@ -2883,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) { @@ -2897,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) { @@ -2918,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. @@ -2947,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--;) { @@ -2959,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; @@ -2969,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(); @@ -3007,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; @@ -3021,102 +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) -{ - // 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); - 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, true, 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); -} - -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(const Identifier& property, RegisterID* exceptionValue, VariableEnvironment& environment) -{ - RELEASE_ASSERT(environment.contains(property.impl())); - pushLexicalScopeInternal(environment, true, nullptr, TDZRequirement::NotUnderTDZ, ScopeType::CatchScope, ScopeRegisterType::Block); - Variable exceptionVar = variable(property); - RELEASE_ASSERT(exceptionVar.isResolved()); - RefPtr<RegisterID> scope = emitResolveScope(nullptr, exceptionVar); - emitPutToScope(scope.get(), exceptionVar, exceptionValue, ThrowIfNotFound); -} - -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) @@ -3241,304 +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 (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()); -} - -#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) -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()); -} -#endif - -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()); - } + 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(); -} - -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; - } + emitLoad(value.get(), jsUndefined()); + + emitJump(scope->continueTarget()); + + 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 diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h index 33a15c1ff..4e9f213c9 100644 --- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h +++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2012-2015 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2012, 2013 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. * @@ -43,7 +43,6 @@ #include "Debugger.h" #include "Nodes.h" #include "StaticPropertyAnalyzer.h" -#include "TemplateRegistryKey.h" #include "UnlinkedCodeBlock.h" #include <functional> @@ -56,7 +55,7 @@ namespace JSC { class Identifier; - class JSTemplateRegistryKey; + class Label; enum ExpectedFunction { NoExpectedFunction, @@ -84,14 +83,11 @@ namespace JSC { struct FinallyContext { StatementNode* finallyBlock; - RegisterID* iterator; - ThrowableExpressionData* enumerationNode; unsigned scopeContextStackSize; unsigned switchContextStackSize; unsigned forInContextStackSize; unsigned tryContextStackSize; unsigned labelScopesSize; - unsigned symbolTableStackSize; int finallyDepth; int dynamicScopeDepth; }; @@ -101,82 +97,16 @@ namespace JSC { FinallyContext finallyContext; }; - class ForInContext { - WTF_MAKE_FAST_ALLOCATED; - public: - ForInContext(RegisterID* localRegister) - : m_localRegister(localRegister) - , m_isValid(true) - { - } - - virtual ~ForInContext() - { - } - - bool isValid() const { return m_isValid; } - void invalidate() { m_isValid = false; } - - enum ForInContextType { - StructureForInContextType, - IndexedForInContextType - }; - virtual ForInContextType type() const = 0; - - RegisterID* local() const { return m_localRegister.get(); } - - private: - RefPtr<RegisterID> m_localRegister; - bool m_isValid; - }; - - class StructureForInContext : public ForInContext { - public: - StructureForInContext(RegisterID* localRegister, RegisterID* indexRegister, RegisterID* propertyRegister, RegisterID* enumeratorRegister) - : ForInContext(localRegister) - , m_indexRegister(indexRegister) - , m_propertyRegister(propertyRegister) - , m_enumeratorRegister(enumeratorRegister) - { - } - - virtual ForInContextType type() const - { - return StructureForInContextType; - } - - RegisterID* index() const { return m_indexRegister.get(); } - RegisterID* property() const { return m_propertyRegister.get(); } - RegisterID* enumerator() const { return m_enumeratorRegister.get(); } - - private: - RefPtr<RegisterID> m_indexRegister; - RefPtr<RegisterID> m_propertyRegister; - RefPtr<RegisterID> m_enumeratorRegister; - }; - - class IndexedForInContext : public ForInContext { - public: - IndexedForInContext(RegisterID* localRegister, RegisterID* indexRegister) - : ForInContext(localRegister) - , m_indexRegister(indexRegister) - { - } - - virtual ForInContextType type() const - { - return IndexedForInContextType; - } - - RegisterID* index() const { return m_indexRegister.get(); } - - private: - RefPtr<RegisterID> m_indexRegister; + struct ForInContext { + RefPtr<RegisterID> expectedSubscriptRegister; + RefPtr<RegisterID> iterRegister; + RefPtr<RegisterID> indexRegister; + RefPtr<RegisterID> propertyRegister; }; struct TryData { RefPtr<Label> target; - HandlerType handlerType; + unsigned targetScopeDepth; }; struct TryContext { @@ -184,65 +114,39 @@ namespace JSC { TryData* tryData; }; - class Variable { - public: - enum VariableKind { NormalVariable, SpecialVariable }; + enum CaptureMode { + NotCaptured, + IsCaptured + }; - Variable() - : m_offset() - , m_local(nullptr) - , m_attributes(0) - , m_kind(NormalVariable) - , m_symbolTableConstantIndex(0) // This is meaningless here for this kind of Variable. - , m_isLexicallyScoped(false) - { - } - - Variable(const Identifier& ident) - : m_ident(ident) - , m_local(nullptr) + class Local { + public: + Local() + : m_local(0) , m_attributes(0) - , m_kind(NormalVariable) // This is somewhat meaningless here for this kind of Variable. - , m_symbolTableConstantIndex(0) // This is meaningless here for this kind of Variable. - , m_isLexicallyScoped(false) { } - Variable(const Identifier& ident, VarOffset offset, RegisterID* local, unsigned attributes, VariableKind kind, int symbolTableConstantIndex, bool isLexicallyScoped) - : m_ident(ident) - , m_offset(offset) - , m_local(local) + Local(RegisterID* local, unsigned attributes, CaptureMode captureMode) + : m_local(local) , m_attributes(attributes) - , m_kind(kind) - , m_symbolTableConstantIndex(symbolTableConstantIndex) - , m_isLexicallyScoped(isLexicallyScoped) + , m_isCaptured(captureMode == IsCaptured) { } - // If it's unset, then it is a non-locally-scoped variable. If it is set, then it could be - // a stack variable, a scoped variable in a local scope, or a variable captured in the - // direct arguments object. - bool isResolved() const { return !!m_offset; } - int symbolTableConstantIndex() const { ASSERT(isResolved() && !isSpecial()); return m_symbolTableConstantIndex; } - - const Identifier& ident() const { return m_ident; } - - VarOffset offset() const { return m_offset; } - bool isLocal() const { return m_offset.isStack(); } - RegisterID* local() const { return m_local; } + operator bool() { return m_local; } + + RegisterID* get() { return m_local; } - bool isReadOnly() const { return m_attributes & ReadOnly; } - bool isSpecial() const { return m_kind != NormalVariable; } - bool isConst() const { return isReadOnly() && m_isLexicallyScoped; } + bool isReadOnly() { return m_attributes & ReadOnly; } + + bool isCaptured() { return m_isCaptured; } + CaptureMode captureMode() { return isCaptured() ? IsCaptured : NotCaptured; } private: - Identifier m_ident; - VarOffset m_offset; RegisterID* m_local; unsigned m_attributes; - VariableKind m_kind; - int m_symbolTableConstantIndex; - bool m_isLexicallyScoped; + bool m_isCaptured; }; struct TryRange { @@ -251,52 +155,40 @@ namespace JSC { TryData* tryData; }; - enum ProfileTypeBytecodeFlag { - ProfileTypeBytecodeClosureVar, - ProfileTypeBytecodeLocallyResolved, - ProfileTypeBytecodeDoesNotHaveGlobalID, - ProfileTypeBytecodeFunctionArgument, - ProfileTypeBytecodeFunctionReturnStatement - }; - class BytecodeGenerator { WTF_MAKE_FAST_ALLOCATED; - WTF_MAKE_NONCOPYABLE(BytecodeGenerator); public: + typedef DeclarationStacks::VarStack VarStack; typedef DeclarationStacks::FunctionStack FunctionStack; - BytecodeGenerator(VM&, ProgramNode*, UnlinkedProgramCodeBlock*, DebuggerMode, ProfilerMode, const VariableEnvironment*); - BytecodeGenerator(VM&, FunctionNode*, UnlinkedFunctionCodeBlock*, DebuggerMode, ProfilerMode, const VariableEnvironment*); - BytecodeGenerator(VM&, EvalNode*, UnlinkedEvalCodeBlock*, DebuggerMode, ProfilerMode, const VariableEnvironment*); + BytecodeGenerator(VM&, ProgramNode*, UnlinkedProgramCodeBlock*, DebuggerMode, ProfilerMode); + BytecodeGenerator(VM&, FunctionBodyNode*, UnlinkedFunctionCodeBlock*, DebuggerMode, ProfilerMode); + BytecodeGenerator(VM&, EvalNode*, UnlinkedEvalCodeBlock*, DebuggerMode, ProfilerMode); ~BytecodeGenerator(); VM* vm() const { return m_vm; } - ParserArena& parserArena() const { return m_scopeNode->parserArena(); } const CommonIdentifiers& propertyNames() const { return *m_vm->propertyNames; } - bool isConstructor() const { return m_codeBlock->isConstructor(); } -#if ENABLE(ES6_CLASS_SYNTAX) - ConstructorKind constructorKind() const { return m_codeBlock->constructorKind(); } -#else - ConstructorKind constructorKind() const { return ConstructorKind::None; } -#endif + bool isConstructor() { return m_codeBlock->isConstructor(); } ParserError generate(); bool isArgumentNumber(const Identifier&, int); - Variable variable(const Identifier&); - - enum ExistingVariableMode { VerifyExisting, IgnoreExisting }; - void createVariable(const Identifier&, VarKind, SymbolTable*, ExistingVariableMode = VerifyExisting); // Creates the variable, or asserts that the already-created variable is sufficiently compatible. + void setIsNumericCompareFunction(bool isNumericCompareFunction); + + bool willResolveToArguments(const Identifier&); + RegisterID* uncheckedRegisterForArguments(); + + bool isCaptured(int operand); + CaptureMode captureMode(int operand) { return isCaptured(operand) ? IsCaptured : NotCaptured; } + Local local(const Identifier&); + Local constLocal(const Identifier&); + // Returns the register storing "this" RegisterID* thisRegister() { return &m_thisRegister; } - RegisterID* argumentsRegister() { return m_argumentsRegister; } - RegisterID* newTarget() { return m_newTargetRegister; } - - RegisterID* scopeRegister() { return m_scopeRegister; } // Returns the next available temporary register. Registers returned by // newTemporary require a modified form of reference counting: any @@ -314,13 +206,6 @@ namespace JSC { RegisterID* ignoredResult() { return &m_ignoredResultRegister; } - // This will be allocated in the temporary region of registers, but it will - // not be marked as a temporary. This will ensure that finalDestination() does - // not overwrite a block scope variable that it mistakes as a temporary. These - // registers can be (and are) reclaimed when the lexical scope they belong to - // is no longer on the symbol table stack. - RegisterID* newBlockScopeVariable(); - // Returns a place to write intermediate values of an operation // which reuses dst if it is safe to do so. RegisterID* tempDestination(RegisterID* dst) @@ -359,6 +244,8 @@ namespace JSC { { // Node::emitCode assumes that dst, if provided, is either a local or a referenced temporary. ASSERT(!dst || dst == ignoredResult() || !dst->isTemporary() || dst->refCount()); + // Should never store directly into a captured variable. + ASSERT(!dst || dst == ignoredResult() || !isCaptured(dst->index())); if (!m_vm->isSafeToRecurse()) { emitThrowExpressionTooDeepException(); return; @@ -375,6 +262,8 @@ namespace JSC { { // Node::emitCode assumes that dst, if provided, is either a local or a referenced temporary. ASSERT(!dst || dst == ignoredResult() || !dst->isTemporary() || dst->refCount()); + // Should never store directly into a captured variable. + ASSERT(!dst || dst == ignoredResult() || !isCaptured(dst->index())); if (!m_vm->isSafeToRecurse()) return emitThrowExpressionTooDeepException(); return n->emitBytecode(*this, dst); @@ -423,11 +312,9 @@ namespace JSC { unsigned column = divotOffset - lineStart; unsigned instructionOffset = instructions().size(); - if (!m_isBuiltinFunction) - m_codeBlock->addExpressionInfo(instructionOffset, divotOffset, startOffset, endOffset, line, column); + m_codeBlock->addExpressionInfo(instructionOffset, divotOffset, startOffset, endOffset, line, column); } - ALWAYS_INLINE bool leftHandSideNeedsCopy(bool rightHasAssignments, bool rightIsPure) { return (m_codeType != FunctionCode || m_codeBlock->needsFullScopeChain() || rightHasAssignments) && !rightIsPure; @@ -444,24 +331,10 @@ namespace JSC { return emitNode(n); } - private: - void emitTypeProfilerExpressionInfo(const JSTextPosition& startDivot, const JSTextPosition& endDivot); - public: - - // This doesn't emit expression info. If using this, make sure you shouldn't be emitting text offset. - void emitProfileType(RegisterID* registerToProfile, ProfileTypeBytecodeFlag); - // These variables are associated with variables in a program. They could be Locals, LocalClosureVar, or ClosureVar. - void emitProfileType(RegisterID* registerToProfile, const Variable&, const JSTextPosition& startDivot, const JSTextPosition& endDivot); - - void emitProfileType(RegisterID* registerToProfile, ProfileTypeBytecodeFlag, const JSTextPosition& startDivot, const JSTextPosition& endDivot); - // These are not associated with variables and don't have a global id. - void emitProfileType(RegisterID* registerToProfile, const JSTextPosition& startDivot, const JSTextPosition& endDivot); - - void emitProfileControlFlow(int); - RegisterID* emitLoad(RegisterID* dst, bool); + RegisterID* emitLoad(RegisterID* dst, double); RegisterID* emitLoad(RegisterID* dst, const Identifier&); - RegisterID* emitLoad(RegisterID* dst, JSValue, SourceCodeRepresentation = SourceCodeRepresentation::Other); + RegisterID* emitLoad(RegisterID* dst, JSValue); RegisterID* emitLoadGlobalObject(RegisterID* dst); RegisterID* emitUnaryOp(OpcodeID, RegisterID* dst, RegisterID* src); @@ -470,25 +343,19 @@ namespace JSC { RegisterID* emitUnaryNoDstOp(OpcodeID, RegisterID* src); RegisterID* emitCreateThis(RegisterID* dst); - void emitTDZCheck(RegisterID* target); - bool needsTDZCheck(const Variable&); - void emitTDZCheckIfNecessary(const Variable&, RegisterID* target, RegisterID* scope); - void liftTDZCheckIfPossible(const Variable&); RegisterID* emitNewObject(RegisterID* dst); RegisterID* emitNewArray(RegisterID* dst, ElementNode*, unsigned length); // stops at first elision - RegisterID* emitNewFunction(RegisterID* dst, FunctionMetadataNode*); - RegisterID* emitNewFunctionInternal(RegisterID* dst, unsigned index); + RegisterID* emitNewFunction(RegisterID* dst, CaptureMode, FunctionBodyNode*); + RegisterID* emitLazyNewFunction(RegisterID* dst, FunctionBodyNode* body); + RegisterID* emitNewFunctionInternal(RegisterID* dst, CaptureMode, unsigned index, bool shouldNullCheck); RegisterID* emitNewFunctionExpression(RegisterID* dst, FuncExprNode* func); - RegisterID* emitNewDefaultConstructor(RegisterID* dst, ConstructorKind, const Identifier& name); RegisterID* emitNewRegExp(RegisterID* dst, RegExp*); - RegisterID* emitMoveLinkTimeConstant(RegisterID* dst, LinkTimeConstant); - RegisterID* emitMoveEmptyValue(RegisterID* dst); + RegisterID* emitMove(RegisterID* dst, CaptureMode, RegisterID* src); RegisterID* emitMove(RegisterID* dst, RegisterID* src); RegisterID* emitToNumber(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_to_number, dst, src); } - RegisterID* emitToString(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_to_string, dst, src); } RegisterID* emitInc(RegisterID* srcDst); RegisterID* emitDec(RegisterID* srcDst); @@ -497,9 +364,12 @@ namespace JSC { RegisterID* emitTypeOf(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_typeof, dst, src); } RegisterID* emitIn(RegisterID* dst, RegisterID* property, RegisterID* base) { return emitBinaryOp(op_in, dst, property, base, OperandTypes()); } + RegisterID* emitInitGlobalConst(const Identifier&, RegisterID* value); + RegisterID* emitGetById(RegisterID* dst, RegisterID* base, const Identifier& property); + RegisterID* emitGetArgumentsLength(RegisterID* dst, RegisterID* base); RegisterID* emitPutById(RegisterID* base, const Identifier& property, RegisterID* value); - RegisterID* emitDirectPutById(RegisterID* base, const Identifier& property, RegisterID* value, PropertyNode::PutType); + RegisterID* emitDirectPutById(RegisterID* base, const Identifier& property, RegisterID* value); RegisterID* emitDeleteById(RegisterID* dst, RegisterID* base, const Identifier&); RegisterID* emitGetByVal(RegisterID* dst, RegisterID* base, RegisterID* property); RegisterID* emitGetArgumentByVal(RegisterID* dst, RegisterID* base, RegisterID* property); @@ -507,30 +377,15 @@ namespace JSC { RegisterID* emitDirectPutByVal(RegisterID* base, RegisterID* property, RegisterID* value); RegisterID* emitDeleteByVal(RegisterID* dst, RegisterID* base, RegisterID* property); RegisterID* emitPutByIndex(RegisterID* base, unsigned index, RegisterID* value); - - void emitPutGetterById(RegisterID* base, const Identifier& property, RegisterID* getter); - void emitPutSetterById(RegisterID* base, const Identifier& property, RegisterID* setter); void emitPutGetterSetter(RegisterID* base, const Identifier& property, RegisterID* getter, RegisterID* setter); ExpectedFunction expectedFunctionForIdentifier(const Identifier&); RegisterID* emitCall(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); RegisterID* emitCallEval(RegisterID* dst, RegisterID* func, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); - RegisterID* 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); - - enum PropertyDescriptorOption { - PropertyConfigurable = 1, - PropertyWritable = 1 << 1, - PropertyEnumerable = 1 << 2, - }; - void emitCallDefineProperty(RegisterID* newObj, RegisterID* propertyNameRegister, - RegisterID* valueRegister, RegisterID* getterRegister, RegisterID* setterRegister, unsigned options, const JSTextPosition&); - - void emitEnumeration(ThrowableExpressionData* enumerationNode, ExpressionNode* subjectNode, const std::function<void(BytecodeGenerator&, RegisterID*)>& callBack, VariableEnvironmentNode* = nullptr, RegisterID* forLoopSymbolTable = nullptr); - -#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) - RegisterID* emitGetTemplateObject(RegisterID* dst, TaggedTemplateNode*); -#endif + RegisterID* emitCallVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + void emitEnumeration(ThrowableExpressionData* enumerationNode, ExpressionNode* subjectNode, const std::function<void(BytecodeGenerator&, RegisterID*)>& callBack); + RegisterID* emitReturn(RegisterID* src); RegisterID* emitEnd(RegisterID* src) { return emitUnaryNoDstOp(op_end, src); } @@ -539,11 +394,9 @@ namespace JSC { void emitToPrimitive(RegisterID* dst, RegisterID* src); ResolveType resolveType(); - RegisterID* emitResolveConstantLocal(RegisterID* dst, const Variable&); - RegisterID* emitResolveScope(RegisterID* dst, const Variable&); - RegisterID* emitGetFromScope(RegisterID* dst, RegisterID* scope, const Variable&, ResolveMode); - RegisterID* emitPutToScope(RegisterID* scope, const Variable&, RegisterID* value, ResolveMode); - RegisterID* initializeVariable(const Variable&, RegisterID* value); + RegisterID* emitResolveScope(RegisterID* dst, const Identifier&); + RegisterID* emitGetFromScope(RegisterID* dst, RegisterID* scope, const Identifier&, ResolveMode); + RegisterID* emitPutToScope(RegisterID* scope, const Identifier&, RegisterID* value, ResolveMode); PassRefPtr<Label> emitLabel(Label*); void emitLoopHint(); @@ -552,31 +405,17 @@ namespace JSC { PassRefPtr<Label> emitJumpIfFalse(RegisterID* cond, Label* target); PassRefPtr<Label> emitJumpIfNotFunctionCall(RegisterID* cond, Label* target); PassRefPtr<Label> emitJumpIfNotFunctionApply(RegisterID* cond, Label* target); - void emitPopScopes(RegisterID* srcDst, int targetScopeDepth); - - RegisterID* emitHasIndexedProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName); - RegisterID* emitHasStructureProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName, RegisterID* enumerator); - RegisterID* emitHasGenericProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName); - RegisterID* emitGetPropertyEnumerator(RegisterID* dst, RegisterID* base); - RegisterID* emitGetEnumerableLength(RegisterID* dst, RegisterID* base); - RegisterID* emitGetStructurePropertyEnumerator(RegisterID* dst, RegisterID* base, RegisterID* length); - RegisterID* emitGetGenericPropertyEnumerator(RegisterID* dst, RegisterID* base, RegisterID* length, RegisterID* structureEnumerator); - RegisterID* emitEnumeratorStructurePropertyName(RegisterID* dst, RegisterID* enumerator, RegisterID* index); - RegisterID* emitEnumeratorGenericPropertyName(RegisterID* dst, RegisterID* enumerator, RegisterID* index); - RegisterID* emitToIndexString(RegisterID* dst, RegisterID* index); + void emitPopScopes(int targetScopeDepth); - RegisterID* emitIsObject(RegisterID* dst, RegisterID* src); - RegisterID* emitIsUndefined(RegisterID* dst, RegisterID* src); + RegisterID* emitGetPropertyNames(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, Label* breakTarget); + RegisterID* emitNextPropertyName(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, RegisterID* iter, Label* target); - RegisterID* emitIteratorNext(RegisterID* dst, RegisterID* iterator, const ThrowableExpressionData* node); - void emitIteratorClose(RegisterID* iterator, const ThrowableExpressionData* node); - - bool emitReadOnlyExceptionIfNeeded(const Variable&); + void emitReadOnlyExceptionIfNeeded(); // Start a try block. 'start' must have been emitted. TryData* pushTry(Label* start); // End a try block. 'end' must have been emitted. - void popTryAndEmitCatch(TryData*, RegisterID* exceptionRegister, RegisterID* thrownValueRegister, Label* end, HandlerType); + RegisterID* popTryAndEmitCatch(TryData*, RegisterID* targetRegister, Label* end); void emitThrow(RegisterID* exc) { @@ -585,33 +424,33 @@ namespace JSC { } void emitThrowReferenceError(const String& message); - void emitThrowTypeError(const String& message); - void emitPushFunctionNameScope(const Identifier& property, RegisterID* value); - void emitPushCatchScope(const Identifier& property, RegisterID* exceptionValue, VariableEnvironment&); - void emitPopCatchScope(VariableEnvironment&); + void emitPushNameScope(const Identifier& property, RegisterID* value, unsigned attributes); - void emitGetScope(); - RegisterID* emitPushWithScope(RegisterID* objectScope); - void emitPopWithScope(); + RegisterID* emitPushWithScope(RegisterID* scope); + void emitPopScope(); void emitDebugHook(DebugHookID, unsigned line, unsigned charOffset, unsigned lineStart); - bool isInFinallyBlock() { return m_finallyDepth > 0; } + int scopeDepth() { return m_localScopeDepth + m_finallyDepth; } + bool hasFinaliser() { return m_finallyDepth != 0; } void pushFinallyContext(StatementNode* finallyBlock); void popFinallyContext(); - void pushIteratorCloseContext(RegisterID* iterator, ThrowableExpressionData* enumerationNode); - void popIteratorCloseContext(); - void pushIndexedForInScope(RegisterID* local, RegisterID* index); - void popIndexedForInScope(RegisterID* local); - void pushStructureForInScope(RegisterID* local, RegisterID* index, RegisterID* property, RegisterID* enumerator); - void popStructureForInScope(RegisterID* local); - void invalidateForInContextForLocal(RegisterID* local); + void pushOptimisedForIn(RegisterID* expectedSubscript, RegisterID* iter, RegisterID* index, RegisterID* propertyRegister) + { + ForInContext context = { expectedSubscript, iter, index, propertyRegister }; + m_forInContextStack.append(context); + } + + void popOptimisedForIn() + { + m_forInContextStack.removeLast(); + } - LabelScopePtr breakTarget(const Identifier&); - LabelScopePtr continueTarget(const Identifier&); + LabelScope* breakTarget(const Identifier&); + LabelScope* continueTarget(const Identifier&); void beginSwitch(RegisterID*, SwitchInfo::SwitchType); void endSwitch(uint32_t clauseCount, RefPtr<Label>*, ExpressionNode**, Label* defaultLabel, int32_t min, int32_t range); @@ -622,29 +461,10 @@ namespace JSC { bool shouldEmitDebugHooks() { return m_shouldEmitDebugHooks; } bool isStrictMode() const { return m_codeBlock->isStrictMode(); } - - bool isBuiltinFunction() const { return m_isBuiltinFunction; } - - OpcodeID lastOpcodeID() const { return m_lastOpcodeID; } - - private: - enum class TDZRequirement { UnderTDZ, NotUnderTDZ }; - enum class ScopeType { CatchScope, LetConstScope, FunctionNameScope }; - enum class ScopeRegisterType { Var, Block }; - void pushLexicalScopeInternal(VariableEnvironment&, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult, TDZRequirement, ScopeType, ScopeRegisterType); - void popLexicalScopeInternal(VariableEnvironment&, TDZRequirement); - void emitPopScope(RegisterID* dst, RegisterID* scope); - RegisterID* emitGetParentScope(RegisterID* dst, RegisterID* scope); - public: - void pushLexicalScope(VariableEnvironmentNode*, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult = nullptr); - void popLexicalScope(VariableEnvironmentNode*); - void prepareLexicalScopeForNextForLoopIteration(VariableEnvironmentNode*, RegisterID* loopSymbolTable); - int labelScopeDepth() const; private: - void reclaimFreeRegisters(); - Variable variableForLocalEntry(const Identifier&, const SymbolTableEntry&, int symbolTableConstantIndex, bool isLexicallyScoped); - + friend class Label; + void emitOpcode(OpcodeID); UnlinkedArrayAllocationProfile newArrayAllocationProfile(); UnlinkedObjectAllocationProfile newObjectAllocationProfile(); @@ -662,12 +482,10 @@ namespace JSC { ALWAYS_INLINE void rewindBinaryOp(); ALWAYS_INLINE void rewindUnaryOp(); - void allocateAndEmitScope(); - void emitComplexPopScopes(RegisterID*, ControlFlowContext* topScope, ControlFlowContext* bottomScope); + void emitComplexPopScopes(ControlFlowContext* topScope, ControlFlowContext* bottomScope); typedef HashMap<double, JSValue> NumberMap; - typedef HashMap<UniquedStringImpl*, JSString*, IdentifierRepHash> IdentifierStringMap; - typedef HashMap<TemplateRegistryKey, JSTemplateRegistryKey*> TemplateRegistryKeyMap; + typedef HashMap<StringImpl*, JSString*, IdentifierRepHash> IdentifierStringMap; // Helper for emitCall() and emitConstruct(). This works because the set of // expected functions have identical behavior for both call and construct @@ -678,152 +496,187 @@ namespace JSC { RegisterID* newRegister(); - // Adds an anonymous local var slot. To give this slot a name, add it to symbolTable(). + // Adds a var slot and maps it to the name ident in symbolTable(). + enum WatchMode { IsWatchable, NotWatchable }; + RegisterID* addVar(const Identifier& ident, ConstantMode constantMode, WatchMode watchMode) + { + RegisterID* local; + addVar(ident, constantMode, watchMode, local); + return local; + } + + // Ditto. Returns true if a new RegisterID was added, false if a pre-existing RegisterID was re-used. + bool addVar(const Identifier&, ConstantMode, WatchMode, RegisterID*&); + + // Adds an anonymous var slot. To give this slot a name, add it to symbolTable(). RegisterID* addVar() { ++m_codeBlock->m_numVars; - RegisterID* result = newRegister(); - ASSERT(VirtualRegister(result->index()).toLocal() == m_codeBlock->m_numVars - 1); - result->ref(); // We should never free this slot. - return result; + return newRegister(); } - // Initializes the stack form the parameter; does nothing for the symbol table. - RegisterID* initializeNextParameter(); - UniquedStringImpl* visibleNameForParameter(DestructuringPatternNode*); - - RegisterID& registerFor(VirtualRegister reg) + // Returns the index of the added var. + void addParameter(const Identifier&, int parameterIndex); + RegisterID* resolveCallee(FunctionBodyNode*); + void addCallee(FunctionBodyNode*, RegisterID*); + + void preserveLastVar(); + + RegisterID& registerFor(int index) { - if (reg.isLocal()) - return m_calleeRegisters[reg.toLocal()]; + if (operandIsLocal(index)) + return m_calleeRegisters[VirtualRegister(index).toLocal()]; - if (reg.offset() == JSStack::Callee) + if (index == JSStack::Callee) return m_calleeRegister; ASSERT(m_parameters.size()); - return m_parameters[reg.toArgument()]; + return m_parameters[VirtualRegister(index).toArgument()]; } - bool hasConstant(const Identifier&) const; unsigned addConstant(const Identifier&); - RegisterID* addConstantValue(JSValue, SourceCodeRepresentation = SourceCodeRepresentation::Other); + RegisterID* addConstantValue(JSValue); RegisterID* addConstantEmptyValue(); unsigned addRegExp(RegExp*); unsigned addConstantBuffer(unsigned length); - UnlinkedFunctionExecutable* makeFunction(FunctionMetadataNode* metadata) + UnlinkedFunctionExecutable* makeFunction(FunctionBodyNode* body) { - VariableEnvironment variablesUnderTDZ; - getVariablesUnderTDZ(variablesUnderTDZ); + return UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), body); + } + + RegisterID* emitInitLazyRegister(RegisterID*); + + public: + JSString* addStringConstant(const Identifier&); + + Vector<UnlinkedInstruction, 0, UnsafeVectorOverflow>& instructions() { return m_instructions; } - SourceParseMode parseMode = metadata->parseMode(); - ConstructAbility constructAbility = ConstructAbility::CanConstruct; - if (parseMode == SourceParseMode::GetterMode || parseMode == SourceParseMode::SetterMode || parseMode == SourceParseMode::ArrowFunctionMode || (parseMode == SourceParseMode::MethodMode && metadata->constructorKind() == ConstructorKind::None)) - constructAbility = ConstructAbility::CannotConstruct; + SymbolTable& symbolTable() { return *m_symbolTable; } - return UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), metadata, isBuiltinFunction() ? UnlinkedBuiltinFunction : UnlinkedNormalFunction, constructAbility, variablesUnderTDZ); + bool shouldOptimizeLocals() + { + if (m_codeType != FunctionCode) + return false; + + if (m_localScopeDepth) + return false; + + return true; } - void getVariablesUnderTDZ(VariableEnvironment&); + bool canOptimizeNonLocals() + { + if (m_localScopeDepth) + return false; - RegisterID* 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); - RegisterID* emitCallVarargs(OpcodeID, RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); + if (m_codeType == EvalCode) + return false; - void initializeVarLexicalEnvironment(int symbolTableConstantIndex); - void initializeDefaultParameterValuesAndSetupFunctionScopeStack(FunctionParameters&, FunctionNode*, SymbolTable*, int symbolTableConstantIndex, const std::function<bool (UniquedStringImpl*)>& captures); + if (m_codeType == FunctionCode && m_codeBlock->usesEval()) + return false; - public: - JSString* addStringConstant(const Identifier&); - JSTemplateRegistryKey* addTemplateRegistryKeyConstant(const TemplateRegistryKey&); + return true; + } - Vector<UnlinkedInstruction, 0, UnsafeVectorOverflow>& instructions() { return m_instructions; } + bool shouldTearOffArgumentsEagerly() + { + return m_codeType == FunctionCode && isStrictMode() && m_scopeNode->modifiesParameter(); + } RegisterID* emitThrowExpressionTooDeepException(); - private: + void createArgumentsIfNecessary(); + void createActivationIfNecessary(); + RegisterID* createLazyRegisterIfNecessary(RegisterID*); + + unsigned watchableVariable(int operand) + { + VirtualRegister reg(operand); + if (!reg.isLocal()) + return UINT_MAX; + if (static_cast<size_t>(reg.toLocal()) >= m_watchableVariables.size()) + return UINT_MAX; + Identifier& ident = m_watchableVariables[reg.toLocal()]; + if (ident.isNull()) + return UINT_MAX; + return addConstant(ident); + } + + bool hasWatchableVariable(int operand) + { + return watchableVariable(operand) != UINT_MAX; + } + Vector<UnlinkedInstruction, 0, UnsafeVectorOverflow> m_instructions; bool m_shouldEmitDebugHooks; bool m_shouldEmitProfileHooks; - struct SymbolTableStackEntry { - Strong<SymbolTable> m_symbolTable; - RegisterID* m_scope; - bool m_isWithScope; - int m_symbolTableConstantIndex; - }; - Vector<SymbolTableStackEntry> m_symbolTableStack; - Vector<std::pair<VariableEnvironment, bool>> m_TDZStack; + SymbolTable* m_symbolTable; - ScopeNode* const m_scopeNode; + ScopeNode* m_scopeNode; Strong<UnlinkedCodeBlock> m_codeBlock; // Some of these objects keep pointers to one another. They are arranged // to ensure a sane destruction order that avoids references to freed memory. - HashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> m_functions; + HashSet<RefPtr<StringImpl>, IdentifierRepHash> m_functions; RegisterID m_ignoredResultRegister; RegisterID m_thisRegister; RegisterID m_calleeRegister; - RegisterID* m_scopeRegister { nullptr }; - RegisterID* m_topMostScope { nullptr }; - RegisterID* m_argumentsRegister { nullptr }; - RegisterID* m_lexicalEnvironmentRegister { nullptr }; - RegisterID* m_emptyValueRegister { nullptr }; - RegisterID* m_globalObjectRegister { nullptr }; - RegisterID* m_newTargetRegister { nullptr }; - RegisterID* m_linkTimeConstantRegisters[LinkTimeConstantCount]; - + RegisterID* m_activationRegister; + RegisterID* m_emptyValueRegister; + RegisterID* m_globalObjectRegister; + Vector<Identifier, 16> m_watchableVariables; SegmentedVector<RegisterID, 32> m_constantPoolRegisters; SegmentedVector<RegisterID, 32> m_calleeRegisters; SegmentedVector<RegisterID, 32> m_parameters; SegmentedVector<Label, 32> m_labels; LabelScopeStore m_labelScopes; - int m_finallyDepth { 0 }; - int m_localScopeDepth { 0 }; - const CodeType m_codeType; - - int localScopeDepth() const; - void pushScopedControlFlowContext(); - void popScopedControlFlowContext(); + RefPtr<RegisterID> m_lastVar; + int m_finallyDepth; + int m_localScopeDepth; + CodeType m_codeType; Vector<ControlFlowContext, 0, UnsafeVectorOverflow> m_scopeContextStack; Vector<SwitchInfo> m_switchContextStack; - Vector<std::unique_ptr<ForInContext>> m_forInContextStack; + Vector<ForInContext> m_forInContextStack; Vector<TryContext> m_tryContextStack; - enum FunctionVariableType : uint8_t { NormalFunctionVariable, GlobalFunctionVariable }; - Vector<std::pair<FunctionMetadataNode*, FunctionVariableType>> m_functionsToInitialize; - bool m_needToInitializeArguments { false }; Vector<TryRange> m_tryRanges; SegmentedVector<TryData, 8> m_tryData; - int m_nextConstantOffset { 0 }; + int m_firstConstantIndex; + int m_nextConstantOffset; + unsigned m_globalConstantIndex; + + int m_globalVarStorageOffset; - typedef HashMap<FunctionMetadataNode*, unsigned> FunctionOffsetMap; + bool m_hasCreatedActivation; + int m_firstLazyFunction; + int m_lastLazyFunction; + HashMap<unsigned int, FunctionBodyNode*, WTF::IntHash<unsigned int>, WTF::UnsignedWithZeroKeyHashTraits<unsigned int>> m_lazyFunctions; + typedef HashMap<FunctionBodyNode*, unsigned> FunctionOffsetMap; FunctionOffsetMap m_functionOffsets; // Constant pool IdentifierMap m_identifierMap; - - typedef HashMap<EncodedJSValueWithRepresentation, unsigned, EncodedJSValueWithRepresentationHash, EncodedJSValueWithRepresentationHashTraits> JSValueMap; JSValueMap m_jsValueMap; + NumberMap m_numberMap; IdentifierStringMap m_stringMap; - TemplateRegistryKeyMap m_templateRegistryKeyMap; - StaticPropertyAnalyzer m_staticPropertyAnalyzer { &m_instructions }; + StaticPropertyAnalyzer m_staticPropertyAnalyzer; VM* m_vm; - OpcodeID m_lastOpcodeID = op_end; + OpcodeID m_lastOpcodeID; #ifndef NDEBUG - size_t m_lastOpcodePosition { 0 }; + size_t m_lastOpcodePosition; #endif - bool m_usesExceptions { false }; - bool m_expressionTooDeep { false }; - bool m_isBuiltinFunction { false }; - bool m_usesNonStrictEval { false }; + bool m_usesExceptions; + bool m_expressionTooDeep; }; } diff --git a/Source/JavaScriptCore/bytecompiler/Label.h b/Source/JavaScriptCore/bytecompiler/Label.h index b76c648bf..d29ab8ff3 100644 --- a/Source/JavaScriptCore/bytecompiler/Label.h +++ b/Source/JavaScriptCore/bytecompiler/Label.h @@ -10,7 +10,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. * @@ -41,7 +41,7 @@ namespace JSC { class Label { public: - explicit Label(BytecodeGenerator& generator) + explicit Label(BytecodeGenerator* generator) : m_refCount(0) , m_location(invalidLocation) , m_generator(generator) @@ -82,7 +82,7 @@ namespace JSC { int m_refCount; unsigned m_location; - BytecodeGenerator& m_generator; + BytecodeGenerator* m_generator; mutable JumpVector m_unresolvedJumps; }; diff --git a/Source/JavaScriptCore/bytecompiler/LabelScope.h b/Source/JavaScriptCore/bytecompiler/LabelScope.h index 9b84cb3f9..2df6f1b9b 100644 --- a/Source/JavaScriptCore/bytecompiler/LabelScope.h +++ b/Source/JavaScriptCore/bytecompiler/LabelScope.h @@ -10,7 +10,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. * @@ -85,8 +85,8 @@ namespace JSC { , m_index(0) { } - LabelScopePtr(LabelScopeStore& owner, size_t index) - : m_owner(&owner) + LabelScopePtr(LabelScopeStore* owner, size_t index) + : m_owner(owner) , m_index(index) { m_owner->at(index).ref(); @@ -117,15 +117,11 @@ namespace JSC { m_owner->at(m_index).deref(); } - bool operator!() const { return !m_owner; } - LabelScope& operator*() { ASSERT(m_owner); return m_owner->at(m_index); } LabelScope* operator->() { ASSERT(m_owner); return &m_owner->at(m_index); } const LabelScope& operator*() const { ASSERT(m_owner); return m_owner->at(m_index); } const LabelScope* operator->() const { ASSERT(m_owner); return &m_owner->at(m_index); } - static LabelScopePtr null() { return LabelScopePtr(); } - private: LabelScopeStore* m_owner; size_t m_index; diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp index 477fa5c73..1ffd4f311 100644 --- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp +++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2002 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) -* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2012, 2013, 2015 Apple Inc. All rights reserved. +* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2012, 2013 Apple Inc. All rights reserved. * Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) * Copyright (C) 2007 Maks Orlovich * Copyright (C) 2007 Eric Seidel <eric@webkit.org> @@ -28,25 +28,23 @@ #include "Nodes.h" #include "NodeConstructors.h" -#include "BuiltinNames.h" #include "BytecodeGenerator.h" #include "CallFrame.h" #include "Debugger.h" #include "JIT.h" #include "JSFunction.h" #include "JSGlobalObject.h" +#include "JSNameScope.h" #include "JSONObject.h" #include "LabelScope.h" #include "Lexer.h" -#include "JSCInlines.h" -#include "JSTemplateRegistryKey.h" +#include "Operations.h" #include "Parser.h" #include "PropertyNameArray.h" #include "RegExpCache.h" #include "RegExpObject.h" #include "SamplingTool.h" #include "StackAlignment.h" -#include "TemplateRegistryKey.h" #include <wtf/Assertions.h> #include <wtf/RefCountedLeakCounter.h> #include <wtf/Threading.h> @@ -123,15 +121,6 @@ JSValue StringNode::jsValue(BytecodeGenerator& generator) const return generator.addStringConstant(m_value); } -// ------------------------------ NumberNode ---------------------------------- - -RegisterID* NumberNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (dst == generator.ignoredResult()) - return nullptr; - return generator.emitLoad(dst, jsValue(generator), isIntegerNode() ? SourceCodeRepresentation::Integer : SourceCodeRepresentation::Double); -} - // ------------------------------ RegExpNode ----------------------------------- RegisterID* RegExpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) @@ -145,188 +134,31 @@ RegisterID* RegExpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* d RegisterID* ThisNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - if (m_shouldAlwaysEmitTDZCheck || generator.constructorKind() == ConstructorKind::Derived) - generator.emitTDZCheck(generator.thisRegister()); - if (dst == generator.ignoredResult()) return 0; - - RegisterID* result = generator.moveToDestinationIfNeeded(dst, generator.thisRegister()); - static const unsigned thisLength = 4; - generator.emitProfileType(generator.thisRegister(), position(), JSTextPosition(-1, position().offset + thisLength, -1)); - return result; -} - -// ------------------------------ SuperNode ------------------------------------- - -RegisterID* SuperNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (dst == generator.ignoredResult()) - return 0; - - RegisterID callee; - callee.setIndex(JSStack::Callee); - - RefPtr<RegisterID> homeObject = generator.emitGetById(generator.newTemporary(), &callee, generator.propertyNames().homeObjectPrivateName); - RefPtr<RegisterID> protoParent = generator.emitGetById(generator.newTemporary(), homeObject.get(), generator.propertyNames().underscoreProto); - return generator.emitGetById(generator.finalDestination(dst), protoParent.get(), generator.propertyNames().constructor); -} - -static RegisterID* emitSuperBaseForCallee(BytecodeGenerator& generator) -{ - RegisterID callee; - callee.setIndex(JSStack::Callee); - - RefPtr<RegisterID> homeObject = generator.emitGetById(generator.newTemporary(), &callee, generator.propertyNames().homeObjectPrivateName); - return generator.emitGetById(generator.newTemporary(), homeObject.get(), generator.propertyNames().underscoreProto); -} - -// ------------------------------ NewTargetNode ---------------------------------- - -RegisterID* NewTargetNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (dst == generator.ignoredResult()) - return nullptr; - - return generator.moveToDestinationIfNeeded(dst, generator.newTarget()); + return generator.moveToDestinationIfNeeded(dst, generator.thisRegister()); } // ------------------------------ ResolveNode ---------------------------------- bool ResolveNode::isPure(BytecodeGenerator& generator) const { - return generator.variable(m_ident).offset().isStack(); + return generator.local(m_ident).get(); } RegisterID* ResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - Variable var = generator.variable(m_ident); - if (RegisterID* local = var.local()) { - generator.emitTDZCheckIfNecessary(var, local, nullptr); + if (Local local = generator.local(m_ident)) { if (dst == generator.ignoredResult()) - return nullptr; - - generator.emitProfileType(local, var, m_position, JSTextPosition(-1, m_position.offset + m_ident.length(), -1)); - return generator.moveToDestinationIfNeeded(dst, local); + return 0; + return generator.moveToDestinationIfNeeded(dst, local.get()); } JSTextPosition divot = m_start + m_ident.length(); generator.emitExpressionInfo(divot, m_start, divot); - RefPtr<RegisterID> scope = generator.emitResolveScope(dst, var); - RegisterID* finalDest = generator.finalDestination(dst); - RegisterID* result = generator.emitGetFromScope(finalDest, scope.get(), var, ThrowIfNotFound); - generator.emitTDZCheckIfNecessary(var, finalDest, nullptr); - generator.emitProfileType(finalDest, var, m_position, JSTextPosition(-1, m_position.offset + m_ident.length(), -1)); - return result; -} - -#if ENABLE(ES6_TEMPLATE_LITERAL_SYNTAX) -// ------------------------------ TemplateStringNode ----------------------------------- - -RegisterID* TemplateStringNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (dst == generator.ignoredResult()) - return nullptr; - return generator.emitLoad(dst, JSValue(generator.addStringConstant(cooked()))); -} - -// ------------------------------ TemplateLiteralNode ----------------------------------- - -RegisterID* TemplateLiteralNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (!m_templateExpressions) { - TemplateStringNode* templateString = m_templateStrings->value(); - ASSERT_WITH_MESSAGE(!m_templateStrings->next(), "Only one template element exists because there's no expression in a given template literal."); - return generator.emitNode(dst, templateString); - } - - Vector<RefPtr<RegisterID>, 16> temporaryRegisters; - - TemplateStringListNode* templateString = m_templateStrings; - TemplateExpressionListNode* templateExpression = m_templateExpressions; - for (; templateExpression; templateExpression = templateExpression->next(), templateString = templateString->next()) { - // Evaluate TemplateString. - if (!templateString->value()->cooked().isEmpty()) { - temporaryRegisters.append(generator.newTemporary()); - generator.emitNode(temporaryRegisters.last().get(), templateString->value()); - } - - // Evaluate Expression. - temporaryRegisters.append(generator.newTemporary()); - generator.emitNode(temporaryRegisters.last().get(), templateExpression->value()); - generator.emitToString(temporaryRegisters.last().get(), temporaryRegisters.last().get()); - } - - // Evaluate tail TemplateString. - if (!templateString->value()->cooked().isEmpty()) { - temporaryRegisters.append(generator.newTemporary()); - generator.emitNode(temporaryRegisters.last().get(), templateString->value()); - } - - return generator.emitStrcat(generator.finalDestination(dst, temporaryRegisters[0].get()), temporaryRegisters[0].get(), temporaryRegisters.size()); -} - -// ------------------------------ TaggedTemplateNode ----------------------------------- - -RegisterID* TaggedTemplateNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - ExpectedFunction expectedFunction = NoExpectedFunction; - RefPtr<RegisterID> tag = nullptr; - RefPtr<RegisterID> base = nullptr; - if (!m_tag->isLocation()) { - tag = generator.newTemporary(); - tag = generator.emitNode(tag.get(), m_tag); - } else if (m_tag->isResolveNode()) { - ResolveNode* resolve = static_cast<ResolveNode*>(m_tag); - const Identifier& identifier = resolve->identifier(); - expectedFunction = generator.expectedFunctionForIdentifier(identifier); - - Variable var = generator.variable(identifier); - if (RegisterID* local = var.local()) - tag = generator.emitMove(generator.newTemporary(), local); - else { - tag = generator.newTemporary(); - base = generator.newTemporary(); - - JSTextPosition newDivot = divotStart() + identifier.length(); - generator.emitExpressionInfo(newDivot, divotStart(), newDivot); - generator.moveToDestinationIfNeeded(base.get(), generator.emitResolveScope(base.get(), var)); - generator.emitGetFromScope(tag.get(), base.get(), var, ThrowIfNotFound); - } - } else if (m_tag->isBracketAccessorNode()) { - BracketAccessorNode* bracket = static_cast<BracketAccessorNode*>(m_tag); - base = generator.newTemporary(); - base = generator.emitNode(base.get(), bracket->base()); - RefPtr<RegisterID> property = generator.emitNode(bracket->subscript()); - tag = generator.emitGetByVal(generator.newTemporary(), base.get(), property.get()); - } else { - ASSERT(m_tag->isDotAccessorNode()); - DotAccessorNode* dot = static_cast<DotAccessorNode*>(m_tag); - base = generator.newTemporary(); - base = generator.emitNode(base.get(), dot->base()); - tag = generator.emitGetById(generator.newTemporary(), base.get(), dot->identifier()); - } - - RefPtr<RegisterID> templateObject = generator.emitGetTemplateObject(generator.newTemporary(), this); - - unsigned expressionsCount = 0; - for (TemplateExpressionListNode* templateExpression = m_templateLiteral->templateExpressions(); templateExpression; templateExpression = templateExpression->next()) - ++expressionsCount; - - CallArguments callArguments(generator, nullptr, 1 + expressionsCount); - if (base) - generator.emitMove(callArguments.thisRegister(), base.get()); - else - generator.emitLoad(callArguments.thisRegister(), jsUndefined()); - - unsigned argumentIndex = 0; - generator.emitMove(callArguments.argumentRegister(argumentIndex++), templateObject.get()); - for (TemplateExpressionListNode* templateExpression = m_templateLiteral->templateExpressions(); templateExpression; templateExpression = templateExpression->next()) - generator.emitNode(callArguments.argumentRegister(argumentIndex++), templateExpression->value()); - - return generator.emitCall(generator.finalDestination(dst, tag.get()), tag.get(), expectedFunction, callArguments, divot(), divotStart(), divotEnd()); + RefPtr<RegisterID> scope = generator.emitResolveScope(generator.tempDestination(dst), m_ident); + return generator.emitGetFromScope(generator.finalDestination(dst), scope.get(), m_ident, ThrowIfNotFound); } -#endif // ------------------------------ ArrayNode ------------------------------------ @@ -399,7 +231,7 @@ bool ArrayNode::isSimpleArray() const return true; } -ArgumentListNode* ArrayNode::toArgumentList(ParserArena& parserArena, int lineNumber, int startPosition) const +ArgumentListNode* ArrayNode::toArgumentList(VM* vm, int lineNumber, int startPosition) const { ASSERT(!m_elision && !m_optional); ElementNode* ptr = m_element; @@ -408,12 +240,12 @@ ArgumentListNode* ArrayNode::toArgumentList(ParserArena& parserArena, int lineNu JSTokenLocation location; location.line = lineNumber; location.startOffset = startPosition; - ArgumentListNode* head = new (parserArena) ArgumentListNode(location, ptr->value()); + ArgumentListNode* head = new (vm) ArgumentListNode(location, ptr->value()); ArgumentListNode* tail = head; ptr = ptr->next(); for (; ptr; ptr = ptr->next()) { ASSERT(!ptr->elision()); - tail = new (parserArena) ArgumentListNode(location, tail, ptr->value()); + tail = new (vm) ArgumentListNode(location, tail, ptr->value()); } return head; } @@ -422,59 +254,49 @@ ArgumentListNode* ArrayNode::toArgumentList(ParserArena& parserArena, int lineNu RegisterID* ObjectLiteralNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - if (!m_list) { - if (dst == generator.ignoredResult()) - return 0; - return generator.emitNewObject(generator.finalDestination(dst)); - } - RefPtr<RegisterID> newObj = generator.emitNewObject(generator.tempDestination(dst)); - generator.emitNode(newObj.get(), m_list); - return generator.moveToDestinationIfNeeded(dst, newObj.get()); + if (!m_list) { + if (dst == generator.ignoredResult()) + return 0; + return generator.emitNewObject(generator.finalDestination(dst)); + } + return generator.emitNode(dst, m_list); } // ------------------------------ PropertyListNode ----------------------------- -static inline void emitPutHomeObject(BytecodeGenerator& generator, RegisterID* function, RegisterID* homeObject) -{ - generator.emitPutById(function, generator.propertyNames().homeObjectPrivateName, homeObject); -} - RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { + RefPtr<RegisterID> newObj = generator.tempDestination(dst); + + generator.emitNewObject(newObj.get()); + // Fast case: this loop just handles regular value properties. PropertyListNode* p = this; - for (; p && (p->m_node->m_type & PropertyNode::Constant); p = p->m_next) - emitPutConstantProperty(generator, dst, *p->m_node); + for (; p && p->m_node->m_type == PropertyNode::Constant; p = p->m_next) { + if (p->m_node->m_name) { + generator.emitDirectPutById(newObj.get(), *p->m_node->name(), generator.emitNode(p->m_node->m_assign)); + continue; + } + RefPtr<RegisterID> propertyName = generator.emitNode(p->m_node->m_expression); + generator.emitDirectPutByVal(newObj.get(), propertyName.get(), generator.emitNode(p->m_node->m_assign)); + } // Were there any get/set properties? if (p) { - // Build a list of getter/setter pairs to try to put them at the same time. If we encounter - // a computed property, just emit everything as that may override previous values. - bool hasComputedProperty = false; - typedef std::pair<PropertyNode*, PropertyNode*> GetterSetterPair; - typedef HashMap<UniquedStringImpl*, GetterSetterPair, IdentifierRepHash> GetterSetterMap; + typedef HashMap<StringImpl*, GetterSetterPair> GetterSetterMap; GetterSetterMap map; // Build a map, pairing get/set values together. for (PropertyListNode* q = p; q; q = q->m_next) { PropertyNode* node = q->m_node; - if (node->m_type & PropertyNode::Computed) { - hasComputedProperty = true; - break; - } - if (node->m_type & PropertyNode::Constant) + if (node->m_type == PropertyNode::Constant) continue; - // Duplicates are possible. - GetterSetterPair pair(node, static_cast<PropertyNode*>(nullptr)); + GetterSetterPair pair(node, static_cast<PropertyNode*>(0)); GetterSetterMap::AddResult result = map.add(node->name()->impl(), pair); - if (!result.isNewEntry) { - if (result.iterator->value.first->m_type == node->m_type) - result.iterator->value.first = node; - else - result.iterator->value.second = node; - } + if (!result.isNewEntry) + result.iterator->value.second = node; } // Iterate over the remaining properties in the list. @@ -482,28 +304,20 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe PropertyNode* node = p->m_node; // Handle regular values. - if (node->m_type & PropertyNode::Constant) { - emitPutConstantProperty(generator, dst, *node); + if (node->m_type == PropertyNode::Constant) { + if (node->name()) { + generator.emitDirectPutById(newObj.get(), *node->name(), generator.emitNode(node->m_assign)); + continue; + } + RefPtr<RegisterID> propertyName = generator.emitNode(p->m_node->m_expression); + generator.emitDirectPutByVal(newObj.get(), propertyName.get(), generator.emitNode(p->m_node->m_assign)); continue; } - + RegisterID* value = generator.emitNode(node->m_assign); - bool isClassProperty = node->needsSuperBinding(); - if (isClassProperty) - emitPutHomeObject(generator, value, dst); - - ASSERT(node->m_type & (PropertyNode::Getter | PropertyNode::Setter)); - - // This is a get/set property which may be overridden by a computed property later. - if (hasComputedProperty) { - if (node->m_type & PropertyNode::Getter) - generator.emitPutGetterById(dst, *node->name(), value); - else - generator.emitPutSetterById(dst, *node->name(), value); - continue; - } - // This is a get/set property pair. + // This is a get/set property, find its entry in the map. + ASSERT(node->m_type == PropertyNode::Getter || node->m_type == PropertyNode::Setter); GetterSetterMap::iterator it = map.find(node->name()->impl()); ASSERT(it != map.end()); GetterSetterPair& pair = it->value; @@ -511,123 +325,75 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe // Was this already generated as a part of its partner? if (pair.second == node) continue; - + // Generate the paired node now. RefPtr<RegisterID> getterReg; RefPtr<RegisterID> setterReg; - RegisterID* secondReg = nullptr; - if (node->m_type & PropertyNode::Getter) { + if (node->m_type == PropertyNode::Getter) { getterReg = value; if (pair.second) { - ASSERT(pair.second->m_type & PropertyNode::Setter); + ASSERT(pair.second->m_type == PropertyNode::Setter); setterReg = generator.emitNode(pair.second->m_assign); - secondReg = setterReg.get(); } else { setterReg = generator.newTemporary(); generator.emitLoad(setterReg.get(), jsUndefined()); } } else { - ASSERT(node->m_type & PropertyNode::Setter); + ASSERT(node->m_type == PropertyNode::Setter); setterReg = value; if (pair.second) { - ASSERT(pair.second->m_type & PropertyNode::Getter); + ASSERT(pair.second->m_type == PropertyNode::Getter); getterReg = generator.emitNode(pair.second->m_assign); - secondReg = getterReg.get(); } else { getterReg = generator.newTemporary(); generator.emitLoad(getterReg.get(), jsUndefined()); } } - ASSERT(!pair.second || isClassProperty == pair.second->needsSuperBinding()); - if (isClassProperty && pair.second) - emitPutHomeObject(generator, secondReg, dst); - - if (isClassProperty) { - RefPtr<RegisterID> propertyNameRegister = generator.emitLoad(generator.newTemporary(), *node->name()); - generator.emitCallDefineProperty(dst, propertyNameRegister.get(), - nullptr, getterReg.get(), setterReg.get(), BytecodeGenerator::PropertyConfigurable, m_position); - } else - generator.emitPutGetterSetter(dst, *node->name(), getterReg.get(), setterReg.get()); + generator.emitPutGetterSetter(newObj.get(), *node->name(), getterReg.get(), setterReg.get()); } } - return dst; -} - -void PropertyListNode::emitPutConstantProperty(BytecodeGenerator& generator, RegisterID* newObj, PropertyNode& node) -{ - RefPtr<RegisterID> value = generator.emitNode(node.m_assign); - if (node.needsSuperBinding()) { - emitPutHomeObject(generator, value.get(), newObj); - - RefPtr<RegisterID> propertyNameRegister; - if (node.name()) - propertyNameRegister = generator.emitLoad(generator.newTemporary(), *node.name()); - else - propertyNameRegister = generator.emitNode(node.m_expression); - - generator.emitCallDefineProperty(newObj, propertyNameRegister.get(), - value.get(), nullptr, nullptr, BytecodeGenerator::PropertyConfigurable | BytecodeGenerator::PropertyWritable, m_position); - return; - } - if (const auto* identifier = node.name()) { - Optional<uint32_t> optionalIndex = parseIndex(*identifier); - if (!optionalIndex) { - generator.emitDirectPutById(newObj, *identifier, value.get(), node.putType()); - return; - } - - RefPtr<RegisterID> index = generator.emitLoad(generator.newTemporary(), jsNumber(optionalIndex.value())); - generator.emitDirectPutByVal(newObj, index.get(), value.get()); - return; - } - RefPtr<RegisterID> propertyName = generator.emitNode(node.m_expression); - generator.emitDirectPutByVal(newObj, propertyName.get(), value.get()); + return generator.moveToDestinationIfNeeded(dst, newObj.get()); } // ------------------------------ BracketAccessorNode -------------------------------- RegisterID* BracketAccessorNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - if (m_base->isSuperNode()) { - // FIXME: Should we generate the profiler info? - if (m_subscript->isString()) { - const Identifier& id = static_cast<StringNode*>(m_subscript)->value(); - return generator.emitGetById(generator.finalDestination(dst), emitSuperBaseForCallee(generator), id); - } - return generator.emitGetByVal(generator.finalDestination(dst), emitSuperBaseForCallee(generator), generator.emitNode(m_subscript)); - } - - RegisterID* ret; - RegisterID* finalDest = generator.finalDestination(dst); - - if (m_subscript->isString()) { - RefPtr<RegisterID> base = generator.emitNode(m_base); - ret = generator.emitGetById(finalDest, base.get(), static_cast<StringNode*>(m_subscript)->value()); - } else { - RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments, m_subscript->isPure(generator)); + if (m_base->isResolveNode() + && generator.willResolveToArguments(static_cast<ResolveNode*>(m_base)->identifier()) + && !generator.symbolTable().slowArguments()) { RegisterID* property = generator.emitNode(m_subscript); - ret = generator.emitGetByVal(finalDest, base.get(), property); + generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); + return generator.emitGetArgumentByVal(generator.finalDestination(dst), generator.uncheckedRegisterForArguments(), property); } + RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments, m_subscript->isPure(generator)); + RegisterID* property = generator.emitNode(m_subscript); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - - generator.emitProfileType(finalDest, divotStart(), divotEnd()); - return ret; + return generator.emitGetByVal(generator.finalDestination(dst), base.get(), property); } // ------------------------------ DotAccessorNode -------------------------------- RegisterID* DotAccessorNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - RefPtr<RegisterID> base = m_base->isSuperNode() ? emitSuperBaseForCallee(generator) : generator.emitNode(m_base); + if (m_ident == generator.propertyNames().length) { + if (!m_base->isResolveNode()) + goto nonArgumentsPath; + ResolveNode* resolveNode = static_cast<ResolveNode*>(m_base); + if (!generator.willResolveToArguments(resolveNode->identifier())) + goto nonArgumentsPath; + generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); + return generator.emitGetArgumentsLength(generator.finalDestination(dst), generator.uncheckedRegisterForArguments()); + } + +nonArgumentsPath: + RegisterID* base = generator.emitNode(m_base); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - RegisterID* finalDest = generator.finalDestination(dst); - RegisterID* ret = generator.emitGetById(finalDest, base.get(), m_ident); - generator.emitProfileType(finalDest, divotStart(), divotEnd()); - return ret; + return generator.emitGetById(generator.finalDestination(dst), base, m_ident); } // ------------------------------ ArgumentListNode ----------------------------- @@ -650,7 +416,6 @@ RegisterID* NewExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID* RefPtr<RegisterID> func = generator.emitNode(m_expr); RefPtr<RegisterID> returnValue = generator.finalDestination(dst, func.get()); CallArguments callArguments(generator, m_args); - generator.emitMove(callArguments.thisRegister(), func.get()); return generator.emitConstruct(returnValue.get(), func.get(), expectedFunction, callArguments, divot(), divotStart(), divotEnd()); } @@ -683,9 +448,8 @@ CallArguments::CallArguments(BytecodeGenerator& generator, ArgumentsNode* argume RegisterID* EvalFunctionCallNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - Variable var = generator.variable(generator.propertyNames().eval); - if (RegisterID* local = var.local()) { - RefPtr<RegisterID> func = generator.emitMove(generator.tempDestination(dst), local); + if (Local local = generator.local(generator.propertyNames().eval)) { + RefPtr<RegisterID> func = generator.emitMove(generator.tempDestination(dst), local.get()); CallArguments callArguments(generator, m_args); generator.emitLoad(callArguments.thisRegister(), jsUndefined()); return generator.emitCallEval(generator.finalDestination(dst, func.get()), func.get(), callArguments, divot(), divotStart(), divotEnd()); @@ -695,10 +459,8 @@ RegisterID* EvalFunctionCallNode::emitBytecode(BytecodeGenerator& generator, Reg CallArguments callArguments(generator, m_args); JSTextPosition newDivot = divotStart() + 4; generator.emitExpressionInfo(newDivot, divotStart(), newDivot); - generator.moveToDestinationIfNeeded( - callArguments.thisRegister(), - generator.emitResolveScope(callArguments.thisRegister(), var)); - generator.emitGetFromScope(func.get(), callArguments.thisRegister(), var, ThrowIfNotFound); + generator.emitResolveScope(callArguments.thisRegister(), generator.propertyNames().eval); + generator.emitGetFromScope(func.get(), callArguments.thisRegister(), generator.propertyNames().eval, ThrowIfNotFound); return generator.emitCallEval(generator.finalDestination(dst, func.get()), func.get(), callArguments, divot(), divotStart(), divotEnd()); } @@ -709,18 +471,8 @@ RegisterID* FunctionCallValueNode::emitBytecode(BytecodeGenerator& generator, Re RefPtr<RegisterID> func = generator.emitNode(m_expr); RefPtr<RegisterID> returnValue = generator.finalDestination(dst, func.get()); CallArguments callArguments(generator, m_args); - if (m_expr->isSuperNode()) { - ASSERT(generator.isConstructor()); - ASSERT(generator.constructorKind() == ConstructorKind::Derived); - generator.emitMove(callArguments.thisRegister(), generator.newTarget()); - RegisterID* ret = generator.emitConstruct(returnValue.get(), func.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); - generator.emitMove(generator.thisRegister(), ret); - return ret; - } generator.emitLoad(callArguments.thisRegister(), jsUndefined()); - RegisterID* ret = generator.emitCall(returnValue.get(), func.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); - generator.emitProfileType(returnValue.get(), divotStart(), divotEnd()); - return ret; + return generator.emitCall(returnValue.get(), func.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); } // ------------------------------ FunctionCallResolveNode ---------------------------------- @@ -729,18 +481,14 @@ RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator, { ExpectedFunction expectedFunction = generator.expectedFunctionForIdentifier(m_ident); - Variable var = generator.variable(m_ident); - if (RegisterID* local = var.local()) { - generator.emitTDZCheckIfNecessary(var, local, nullptr); - RefPtr<RegisterID> func = generator.emitMove(generator.tempDestination(dst), local); + if (Local local = generator.local(m_ident)) { + RefPtr<RegisterID> func = generator.emitMove(generator.tempDestination(dst), local.get()); RefPtr<RegisterID> returnValue = generator.finalDestination(dst, func.get()); CallArguments callArguments(generator, m_args); generator.emitLoad(callArguments.thisRegister(), jsUndefined()); // This passes NoExpectedFunction because we expect that if the function is in a // local variable, then it's not one of our built-in constructors. - RegisterID* ret = generator.emitCall(returnValue.get(), func.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); - generator.emitProfileType(returnValue.get(), divotStart(), divotEnd()); - return ret; + return generator.emitCall(returnValue.get(), func.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); } RefPtr<RegisterID> func = generator.newTemporary(); @@ -749,82 +497,23 @@ RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator, JSTextPosition newDivot = divotStart() + m_ident.length(); generator.emitExpressionInfo(newDivot, divotStart(), newDivot); - generator.moveToDestinationIfNeeded( - callArguments.thisRegister(), - generator.emitResolveScope(callArguments.thisRegister(), var)); - generator.emitGetFromScope(func.get(), callArguments.thisRegister(), var, ThrowIfNotFound); - generator.emitTDZCheckIfNecessary(var, func.get(), nullptr); - RegisterID* ret = generator.emitCall(returnValue.get(), func.get(), expectedFunction, callArguments, divot(), divotStart(), divotEnd()); - generator.emitProfileType(returnValue.get(), divotStart(), divotEnd()); - return ret; -} - -// ------------------------------ BytecodeIntrinsicNode ---------------------------------- - -RegisterID* BytecodeIntrinsicNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - return (this->*m_emitter)(generator, dst); -} - -RegisterID* BytecodeIntrinsicNode::emit_intrinsic_putByValDirect(BytecodeGenerator& generator, RegisterID* dst) -{ - ArgumentListNode* node = m_args->m_listNode; - RefPtr<RegisterID> base = generator.emitNode(node); - node = node->m_next; - RefPtr<RegisterID> index = generator.emitNode(node); - node = node->m_next; - RefPtr<RegisterID> value = generator.emitNode(node); - - ASSERT(!node->m_next); - - return generator.moveToDestinationIfNeeded(dst, generator.emitDirectPutByVal(base.get(), index.get(), value.get())); -} - -RegisterID* BytecodeIntrinsicNode::emit_intrinsic_toString(BytecodeGenerator& generator, RegisterID* dst) -{ - ArgumentListNode* node = m_args->m_listNode; - RefPtr<RegisterID> src = generator.emitNode(node); - ASSERT(!node->m_next); - - return generator.moveToDestinationIfNeeded(dst, generator.emitToString(generator.tempDestination(dst), src.get())); + generator.emitResolveScope(callArguments.thisRegister(), m_ident); + generator.emitGetFromScope(func.get(), callArguments.thisRegister(), m_ident, ThrowIfNotFound); + return generator.emitCall(returnValue.get(), func.get(), expectedFunction, callArguments, divot(), divotStart(), divotEnd()); } // ------------------------------ FunctionCallBracketNode ---------------------------------- RegisterID* FunctionCallBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - bool baseIsSuper = m_base->isSuperNode(); - bool subscriptIsString = m_subscript->isString(); - - RefPtr<RegisterID> base; - if (baseIsSuper) - base = emitSuperBaseForCallee(generator); - else { - if (subscriptIsString) - base = generator.emitNode(m_base); - else - base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments, m_subscript->isPure(generator)); - } - - RefPtr<RegisterID> function; - if (subscriptIsString) { - generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd()); - function = generator.emitGetById(generator.tempDestination(dst), base.get(), static_cast<StringNode*>(m_subscript)->value()); - } else { - RefPtr<RegisterID> property = generator.emitNode(m_subscript); - generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd()); - function = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property.get()); - } - + RefPtr<RegisterID> base = generator.emitNode(m_base); + RegisterID* property = generator.emitNode(m_subscript); + generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd()); + RefPtr<RegisterID> function = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property); RefPtr<RegisterID> returnValue = generator.finalDestination(dst, function.get()); CallArguments callArguments(generator, m_args); - if (baseIsSuper) - generator.emitMove(callArguments.thisRegister(), generator.thisRegister()); - else - generator.emitMove(callArguments.thisRegister(), base.get()); - RegisterID* ret = generator.emitCall(returnValue.get(), function.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); - generator.emitProfileType(returnValue.get(), divotStart(), divotEnd()); - return ret; + generator.emitMove(callArguments.thisRegister(), base.get()); + return generator.emitCall(returnValue.get(), function.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); } // ------------------------------ FunctionCallDotNode ---------------------------------- @@ -834,16 +523,10 @@ RegisterID* FunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, Regi RefPtr<RegisterID> function = generator.tempDestination(dst); RefPtr<RegisterID> returnValue = generator.finalDestination(dst, function.get()); CallArguments callArguments(generator, m_args); - bool baseIsSuper = m_base->isSuperNode(); - if (baseIsSuper) - generator.emitMove(callArguments.thisRegister(), generator.thisRegister()); - else - generator.emitNode(callArguments.thisRegister(), m_base); + generator.emitNode(callArguments.thisRegister(), m_base); generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd()); - generator.emitGetById(function.get(), baseIsSuper ? emitSuperBaseForCallee(generator) : callArguments.thisRegister(), m_ident); - RegisterID* ret = generator.emitCall(returnValue.get(), function.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); - generator.emitProfileType(returnValue.get(), divotStart(), divotEnd()); - return ret; + generator.emitGetById(function.get(), callArguments.thisRegister(), m_ident); + return generator.emitCall(returnValue.get(), function.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); } RegisterID* CallFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) @@ -852,26 +535,11 @@ RegisterID* CallFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RefPtr<Label> end = generator.newLabel(); RefPtr<RegisterID> base = generator.emitNode(m_base); generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd()); - RefPtr<RegisterID> function; - bool emitCallCheck = !generator.isBuiltinFunction(); - if (emitCallCheck) { - function = generator.emitGetById(generator.tempDestination(dst), base.get(), generator.propertyNames().builtinNames().callPublicName()); - generator.emitJumpIfNotFunctionCall(function.get(), realCall.get()); - } - RefPtr<RegisterID> returnValue = generator.finalDestination(dst); + RefPtr<RegisterID> function = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident); + RefPtr<RegisterID> returnValue = generator.finalDestination(dst, function.get()); + generator.emitJumpIfNotFunctionCall(function.get(), realCall.get()); { - if (m_args->m_listNode && m_args->m_listNode->m_expr && m_args->m_listNode->m_expr->isSpreadExpression()) { - RefPtr<RegisterID> profileHookRegister; - if (generator.shouldEmitProfileHooks()) - profileHookRegister = generator.newTemporary(); - SpreadExpressionNode* spread = static_cast<SpreadExpressionNode*>(m_args->m_listNode->m_expr); - ExpressionNode* subject = spread->expression(); - RefPtr<RegisterID> argumentsRegister; - argumentsRegister = generator.emitNode(subject); - generator.emitExpressionInfo(spread->divot(), spread->divotStart(), spread->divotEnd()); - RefPtr<RegisterID> thisRegister = generator.emitGetByVal(generator.newTemporary(), argumentsRegister.get(), generator.emitLoad(0, jsNumber(0))); - generator.emitCallVarargs(returnValue.get(), base.get(), thisRegister.get(), argumentsRegister.get(), generator.newTemporary(), 1, profileHookRegister.get(), divot(), divotStart(), divotEnd()); - } else if (m_args->m_listNode && m_args->m_listNode->m_expr) { + if (m_args->m_listNode && m_args->m_listNode->m_expr) { ArgumentListNode* oldList = m_args->m_listNode; m_args->m_listNode = m_args->m_listNode->m_next; @@ -879,25 +547,24 @@ RegisterID* CallFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, CallArguments callArguments(generator, m_args); generator.emitNode(callArguments.thisRegister(), oldList->m_expr); generator.emitCall(returnValue.get(), realFunction.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); + generator.emitJump(end.get()); + m_args->m_listNode = oldList; } else { RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get()); CallArguments callArguments(generator, m_args); generator.emitLoad(callArguments.thisRegister(), jsUndefined()); generator.emitCall(returnValue.get(), realFunction.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); + generator.emitJump(end.get()); } } - if (emitCallCheck) { - generator.emitJump(end.get()); - generator.emitLabel(realCall.get()); - { - CallArguments callArguments(generator, m_args); - generator.emitMove(callArguments.thisRegister(), base.get()); - generator.emitCall(returnValue.get(), function.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); - } - generator.emitLabel(end.get()); + generator.emitLabel(realCall.get()); + { + CallArguments callArguments(generator, m_args); + generator.emitMove(callArguments.thisRegister(), base.get()); + generator.emitCall(returnValue.get(), function.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); } - generator.emitProfileType(returnValue.get(), divotStart(), divotEnd()); + generator.emitLabel(end.get()); return returnValue.get(); } @@ -918,94 +585,65 @@ RegisterID* ApplyFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RefPtr<Label> end = generator.newLabel(); RefPtr<RegisterID> base = generator.emitNode(m_base); generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd()); - RefPtr<RegisterID> function; + RefPtr<RegisterID> function = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident); RefPtr<RegisterID> returnValue = generator.finalDestination(dst, function.get()); - bool emitCallCheck = !generator.isBuiltinFunction(); - if (emitCallCheck) { - function = generator.emitGetById(generator.tempDestination(dst), base.get(), generator.propertyNames().builtinNames().applyPublicName()); - generator.emitJumpIfNotFunctionApply(function.get(), realCall.get()); - } - if (mayBeCall) { - if (m_args->m_listNode && m_args->m_listNode->m_expr) { - ArgumentListNode* oldList = m_args->m_listNode; - if (m_args->m_listNode->m_expr->isSpreadExpression()) { - SpreadExpressionNode* spread = static_cast<SpreadExpressionNode*>(m_args->m_listNode->m_expr); - RefPtr<RegisterID> profileHookRegister; - if (generator.shouldEmitProfileHooks()) - profileHookRegister = generator.newTemporary(); - RefPtr<RegisterID> realFunction = generator.emitMove(generator.newTemporary(), base.get()); - RefPtr<RegisterID> index = generator.emitLoad(generator.newTemporary(), jsNumber(0)); - RefPtr<RegisterID> thisRegister = generator.emitLoad(generator.newTemporary(), jsUndefined()); - RefPtr<RegisterID> argumentsRegister = generator.emitLoad(generator.newTemporary(), jsUndefined()); - - auto extractor = [&thisRegister, &argumentsRegister, &index](BytecodeGenerator& generator, RegisterID* value) - { - RefPtr<Label> haveThis = generator.newLabel(); - RefPtr<Label> end = generator.newLabel(); - RefPtr<RegisterID> compareResult = generator.newTemporary(); - RefPtr<RegisterID> indexZeroCompareResult = generator.emitBinaryOp(op_eq, compareResult.get(), index.get(), generator.emitLoad(0, jsNumber(0)), OperandTypes(ResultType::numberTypeIsInt32(), ResultType::numberTypeIsInt32())); - generator.emitJumpIfFalse(indexZeroCompareResult.get(), haveThis.get()); - generator.emitMove(thisRegister.get(), value); - generator.emitLoad(index.get(), jsNumber(1)); - generator.emitJump(end.get()); - generator.emitLabel(haveThis.get()); - RefPtr<RegisterID> indexOneCompareResult = generator.emitBinaryOp(op_eq, compareResult.get(), index.get(), generator.emitLoad(0, jsNumber(1)), OperandTypes(ResultType::numberTypeIsInt32(), ResultType::numberTypeIsInt32())); - generator.emitJumpIfFalse(indexOneCompareResult.get(), end.get()); - generator.emitMove(argumentsRegister.get(), value); - generator.emitLoad(index.get(), jsNumber(2)); - generator.emitLabel(end.get()); - }; - generator.emitEnumeration(this, spread->expression(), extractor); - generator.emitCallVarargs(returnValue.get(), realFunction.get(), thisRegister.get(), argumentsRegister.get(), generator.newTemporary(), 0, profileHookRegister.get(), divot(), divotStart(), divotEnd()); - } else if (m_args->m_listNode->m_next) { - ASSERT(m_args->m_listNode->m_next->m_expr->isSimpleArray()); - ASSERT(!m_args->m_listNode->m_next->m_next); - m_args->m_listNode = static_cast<ArrayNode*>(m_args->m_listNode->m_next->m_expr)->toArgumentList(generator.parserArena(), 0, 0); - RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get()); - CallArguments callArguments(generator, m_args); - generator.emitNode(callArguments.thisRegister(), oldList->m_expr); - generator.emitCall(returnValue.get(), realFunction.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); + generator.emitJumpIfNotFunctionApply(function.get(), realCall.get()); + { + if (mayBeCall) { + if (m_args->m_listNode && m_args->m_listNode->m_expr) { + ArgumentListNode* oldList = m_args->m_listNode; + if (m_args->m_listNode->m_next) { + ASSERT(m_args->m_listNode->m_next->m_expr->isSimpleArray()); + ASSERT(!m_args->m_listNode->m_next->m_next); + m_args->m_listNode = static_cast<ArrayNode*>(m_args->m_listNode->m_next->m_expr)->toArgumentList(generator.vm(), 0, 0); + RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get()); + CallArguments callArguments(generator, m_args); + generator.emitNode(callArguments.thisRegister(), oldList->m_expr); + generator.emitCall(returnValue.get(), realFunction.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); + } else { + m_args->m_listNode = m_args->m_listNode->m_next; + RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get()); + CallArguments callArguments(generator, m_args); + generator.emitNode(callArguments.thisRegister(), oldList->m_expr); + generator.emitCall(returnValue.get(), realFunction.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); + } + m_args->m_listNode = oldList; } else { - m_args->m_listNode = m_args->m_listNode->m_next; RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get()); CallArguments callArguments(generator, m_args); - generator.emitNode(callArguments.thisRegister(), oldList->m_expr); + generator.emitLoad(callArguments.thisRegister(), jsUndefined()); generator.emitCall(returnValue.get(), realFunction.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); } - m_args->m_listNode = oldList; } else { + ASSERT(m_args->m_listNode && m_args->m_listNode->m_next); + RefPtr<RegisterID> profileHookRegister; + if (generator.shouldEmitProfileHooks()) + profileHookRegister = generator.newTemporary(); RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get()); - CallArguments callArguments(generator, m_args); - generator.emitLoad(callArguments.thisRegister(), jsUndefined()); - generator.emitCall(returnValue.get(), realFunction.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); + RefPtr<RegisterID> thisRegister = generator.emitNode(m_args->m_listNode->m_expr); + RefPtr<RegisterID> argsRegister; + ArgumentListNode* args = m_args->m_listNode->m_next; + if (args->m_expr->isResolveNode() && generator.willResolveToArguments(static_cast<ResolveNode*>(args->m_expr)->identifier())) + argsRegister = generator.uncheckedRegisterForArguments(); + else + argsRegister = generator.emitNode(args->m_expr); + + // Function.prototype.apply ignores extra arguments, but we still + // need to evaluate them for side effects. + while ((args = args->m_next)) + generator.emitNode(args->m_expr); + + generator.emitCallVarargs(returnValue.get(), realFunction.get(), thisRegister.get(), argsRegister.get(), generator.newTemporary(), profileHookRegister.get(), divot(), divotStart(), divotEnd()); } - } else { - ASSERT(m_args->m_listNode && m_args->m_listNode->m_next); - RefPtr<RegisterID> profileHookRegister; - if (generator.shouldEmitProfileHooks()) - profileHookRegister = generator.newTemporary(); - RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get()); - RefPtr<RegisterID> thisRegister = generator.emitNode(m_args->m_listNode->m_expr); - RefPtr<RegisterID> argsRegister; - ArgumentListNode* args = m_args->m_listNode->m_next; - argsRegister = generator.emitNode(args->m_expr); - - // Function.prototype.apply ignores extra arguments, but we still - // need to evaluate them for side effects. - while ((args = args->m_next)) - generator.emitNode(args->m_expr); - - generator.emitCallVarargs(returnValue.get(), realFunction.get(), thisRegister.get(), argsRegister.get(), generator.newTemporary(), 0, profileHookRegister.get(), divot(), divotStart(), divotEnd()); - } - if (emitCallCheck) { generator.emitJump(end.get()); - generator.emitLabel(realCall.get()); + } + generator.emitLabel(realCall.get()); + { CallArguments callArguments(generator, m_args); generator.emitMove(callArguments.thisRegister(), base.get()); generator.emitCall(returnValue.get(), function.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd()); - generator.emitLabel(end.get()); } - generator.emitProfileType(returnValue.get(), divotStart(), divotEnd()); + generator.emitLabel(end.get()); return returnValue.get(); } @@ -1034,32 +672,30 @@ RegisterID* PostfixNode::emitResolve(BytecodeGenerator& generator, RegisterID* d ResolveNode* resolve = static_cast<ResolveNode*>(m_expr); const Identifier& ident = resolve->identifier(); - Variable var = generator.variable(ident); - if (RegisterID* local = var.local()) { - generator.emitTDZCheckIfNecessary(var, local, nullptr); - RefPtr<RegisterID> localReg = local; - if (var.isReadOnly()) { - generator.emitReadOnlyExceptionIfNeeded(var); - localReg = generator.emitMove(generator.tempDestination(dst), local); + if (Local local = generator.local(ident)) { + RegisterID* localReg = local.get(); + if (local.isReadOnly()) { + generator.emitReadOnlyExceptionIfNeeded(); + localReg = generator.emitMove(generator.tempDestination(dst), localReg); + } + if (local.isCaptured()) { + RefPtr<RegisterID> tempDst = generator.finalDestination(dst); + ASSERT(dst != localReg); + RefPtr<RegisterID> tempDstSrc = generator.newTemporary(); + generator.emitToNumber(tempDst.get(), localReg); + generator.emitMove(tempDstSrc.get(), localReg); + emitIncOrDec(generator, tempDstSrc.get(), m_operator); + generator.emitMove(localReg, tempDstSrc.get()); + return tempDst.get(); } - RefPtr<RegisterID> oldValue = emitPostIncOrDec(generator, generator.finalDestination(dst), localReg.get(), m_operator); - generator.emitProfileType(localReg.get(), var, divotStart(), divotEnd()); - return oldValue.get(); + return emitPostIncOrDec(generator, generator.finalDestination(dst), localReg, m_operator); } generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var); - RefPtr<RegisterID> value = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, ThrowIfNotFound); - generator.emitTDZCheckIfNecessary(var, value.get(), nullptr); - if (var.isReadOnly()) { - bool threwException = generator.emitReadOnlyExceptionIfNeeded(var); - if (threwException) - return value.get(); - } + RefPtr<RegisterID> scope = generator.emitResolveScope(generator.newTemporary(), ident); + RefPtr<RegisterID> value = generator.emitGetFromScope(generator.newTemporary(), scope.get(), ident, ThrowIfNotFound); RefPtr<RegisterID> oldValue = emitPostIncOrDec(generator, generator.finalDestination(dst), value.get(), m_operator); - generator.emitPutToScope(scope.get(), var, value.get(), ThrowIfNotFound); - generator.emitProfileType(value.get(), var, divotStart(), divotEnd()); - + generator.emitPutToScope(scope.get(), ident, value.get(), ThrowIfNotFound); return oldValue.get(); } @@ -1081,7 +717,6 @@ RegisterID* PostfixNode::emitBracket(BytecodeGenerator& generator, RegisterID* d RegisterID* oldValue = emitPostIncOrDec(generator, generator.tempDestination(dst), value.get(), m_operator); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); generator.emitPutByVal(base.get(), property.get(), value.get()); - generator.emitProfileType(value.get(), divotStart(), divotEnd()); return generator.moveToDestinationIfNeeded(dst, oldValue); } @@ -1102,7 +737,6 @@ RegisterID* PostfixNode::emitDot(BytecodeGenerator& generator, RegisterID* dst) RegisterID* oldValue = emitPostIncOrDec(generator, generator.tempDestination(dst), value.get(), m_operator); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); generator.emitPutById(base.get(), ident, value.get()); - generator.emitProfileType(value.get(), divotStart(), divotEnd()); return generator.moveToDestinationIfNeeded(dst, oldValue); } @@ -1118,23 +752,19 @@ RegisterID* PostfixNode::emitBytecode(BytecodeGenerator& generator, RegisterID* return emitDot(generator, dst); return emitThrowReferenceError(generator, m_operator == OpPlusPlus - ? ASCIILiteral("Postfix ++ operator applied to value that is not a reference.") - : ASCIILiteral("Postfix -- operator applied to value that is not a reference.")); + ? "Postfix ++ operator applied to value that is not a reference." + : "Postfix -- operator applied to value that is not a reference."); } // ------------------------------ DeleteResolveNode ----------------------------------- RegisterID* DeleteResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - Variable var = generator.variable(m_ident); - if (var.local()) { - generator.emitTDZCheckIfNecessary(var, var.local(), nullptr); + if (generator.local(m_ident).get()) return generator.emitLoad(generator.finalDestination(dst), false); - } generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - RefPtr<RegisterID> base = generator.emitResolveScope(dst, var); - generator.emitTDZCheckIfNecessary(var, nullptr, base.get()); + RefPtr<RegisterID> base = generator.emitResolveScope(generator.tempDestination(dst), m_ident); return generator.emitDeleteById(generator.finalDestination(dst, base.get()), base.get(), m_ident); } @@ -1143,24 +773,20 @@ RegisterID* DeleteResolveNode::emitBytecode(BytecodeGenerator& generator, Regist RegisterID* DeleteBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { RefPtr<RegisterID> r0 = generator.emitNode(m_base); - RefPtr<RegisterID> r1 = generator.emitNode(m_subscript); + RegisterID* r1 = generator.emitNode(m_subscript); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - if (m_base->isSuperNode()) - return emitThrowReferenceError(generator, "Cannot delete a super property"); - return generator.emitDeleteByVal(generator.finalDestination(dst), r0.get(), r1.get()); + return generator.emitDeleteByVal(generator.finalDestination(dst), r0.get(), r1); } // ------------------------------ DeleteDotNode ----------------------------------- RegisterID* DeleteDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - RefPtr<RegisterID> r0 = generator.emitNode(m_base); + RegisterID* r0 = generator.emitNode(m_base); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - if (m_base->isSuperNode()) - return emitThrowReferenceError(generator, "Cannot delete a super property"); - return generator.emitDeleteById(generator.finalDestination(dst), r0.get(), m_ident); + return generator.emitDeleteById(generator.finalDestination(dst), r0, m_ident); } // ------------------------------ DeleteValueNode ----------------------------------- @@ -1185,21 +811,18 @@ RegisterID* VoidNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst return generator.emitLoad(dst, jsUndefined()); } -// ------------------------------ TypeOfResolveNode ----------------------------------- +// ------------------------------ TypeOfValueNode ----------------------------------- RegisterID* TypeOfResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - Variable var = generator.variable(m_ident); - if (RegisterID* local = var.local()) { - generator.emitTDZCheckIfNecessary(var, local, nullptr); + if (Local local = generator.local(m_ident)) { if (dst == generator.ignoredResult()) return 0; - return generator.emitTypeOf(generator.finalDestination(dst), local); + return generator.emitTypeOf(generator.finalDestination(dst), local.get()); } - RefPtr<RegisterID> scope = generator.emitResolveScope(dst, var); - RefPtr<RegisterID> value = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, DoNotThrowIfNotFound); - generator.emitTDZCheckIfNecessary(var, value.get(), nullptr); + RefPtr<RegisterID> scope = generator.emitResolveScope(generator.tempDestination(dst), m_ident); + RefPtr<RegisterID> value = generator.emitGetFromScope(generator.newTemporary(), scope.get(), m_ident, DoNotThrowIfNotFound); if (dst == generator.ignoredResult()) return 0; return generator.emitTypeOf(generator.finalDestination(dst, scope.get()), value.get()); @@ -1225,38 +848,28 @@ RegisterID* PrefixNode::emitResolve(BytecodeGenerator& generator, RegisterID* ds ResolveNode* resolve = static_cast<ResolveNode*>(m_expr); const Identifier& ident = resolve->identifier(); - Variable var = generator.variable(ident); - if (RegisterID* local = var.local()) { - generator.emitTDZCheckIfNecessary(var, local, nullptr); - RefPtr<RegisterID> localReg = local; - if (var.isReadOnly()) { - generator.emitReadOnlyExceptionIfNeeded(var); - localReg = generator.emitMove(generator.tempDestination(dst), localReg.get()); - } else if (generator.vm()->typeProfiler()) { + if (Local local = generator.local(ident)) { + RegisterID* localReg = local.get(); + if (local.isReadOnly()) { + generator.emitReadOnlyExceptionIfNeeded(); + localReg = generator.emitMove(generator.tempDestination(dst), localReg); + } + if (local.isCaptured()) { RefPtr<RegisterID> tempDst = generator.tempDestination(dst); - generator.emitMove(tempDst.get(), localReg.get()); + generator.emitMove(tempDst.get(), localReg); emitIncOrDec(generator, tempDst.get(), m_operator); - generator.emitMove(localReg.get(), tempDst.get()); - generator.emitProfileType(localReg.get(), var, divotStart(), divotEnd()); + generator.emitMove(localReg, tempDst.get()); return generator.moveToDestinationIfNeeded(dst, tempDst.get()); } - emitIncOrDec(generator, localReg.get(), m_operator); - return generator.moveToDestinationIfNeeded(dst, localReg.get()); + emitIncOrDec(generator, localReg, m_operator); + return generator.moveToDestinationIfNeeded(dst, localReg); } generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - RefPtr<RegisterID> scope = generator.emitResolveScope(dst, var); - RefPtr<RegisterID> value = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, ThrowIfNotFound); - generator.emitTDZCheckIfNecessary(var, value.get(), nullptr); - if (var.isReadOnly()) { - bool threwException = generator.emitReadOnlyExceptionIfNeeded(var); - if (threwException) - return value.get(); - } - + RefPtr<RegisterID> scope = generator.emitResolveScope(generator.tempDestination(dst), ident); + RefPtr<RegisterID> value = generator.emitGetFromScope(generator.newTemporary(), scope.get(), ident, ThrowIfNotFound); emitIncOrDec(generator, value.get(), m_operator); - generator.emitPutToScope(scope.get(), var, value.get(), ThrowIfNotFound); - generator.emitProfileType(value.get(), var, divotStart(), divotEnd()); + generator.emitPutToScope(scope.get(), ident, value.get(), ThrowIfNotFound); return generator.moveToDestinationIfNeeded(dst, value.get()); } @@ -1276,7 +889,6 @@ RegisterID* PrefixNode::emitBracket(BytecodeGenerator& generator, RegisterID* ds emitIncOrDec(generator, value, m_operator); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); generator.emitPutByVal(base.get(), property.get(), value); - generator.emitProfileType(value, divotStart(), divotEnd()); return generator.moveToDestinationIfNeeded(dst, propDst.get()); } @@ -1295,7 +907,6 @@ RegisterID* PrefixNode::emitDot(BytecodeGenerator& generator, RegisterID* dst) emitIncOrDec(generator, value, m_operator); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); generator.emitPutById(base.get(), ident, value); - generator.emitProfileType(value, divotStart(), divotEnd()); return generator.moveToDestinationIfNeeded(dst, propDst.get()); } @@ -1311,17 +922,17 @@ RegisterID* PrefixNode::emitBytecode(BytecodeGenerator& generator, RegisterID* d return emitDot(generator, dst); return emitThrowReferenceError(generator, m_operator == OpPlusPlus - ? ASCIILiteral("Prefix ++ operator applied to value that is not a reference.") - : ASCIILiteral("Prefix -- operator applied to value that is not a reference.")); + ? "Prefix ++ operator applied to value that is not a reference." + : "Prefix -- operator applied to value that is not a reference."); } // ------------------------------ Unary Operation Nodes ----------------------------------- RegisterID* UnaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - RefPtr<RegisterID> src = generator.emitNode(m_expr); + RegisterID* src = generator.emitNode(m_expr); generator.emitExpressionInfo(position(), position(), position()); - return generator.emitUnaryOp(opcodeID(), generator.finalDestination(dst), src.get()); + return generator.emitUnaryOp(opcodeID(), generator.finalDestination(dst), src); } // ------------------------------ BitwiseNotNode ----------------------------------- @@ -1329,8 +940,8 @@ RegisterID* UnaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* RegisterID* BitwiseNotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { RefPtr<RegisterID> src2 = generator.emitLoad(generator.newTemporary(), jsNumber(-1)); - RefPtr<RegisterID> src1 = generator.emitNode(m_expr); - return generator.emitBinaryOp(op_bitxor, generator.finalDestination(dst, src1.get()), src1.get(), src2.get(), OperandTypes(m_expr->resultDescriptor(), ResultType::numberTypeIsInt32())); + RegisterID* src1 = generator.emitNode(m_expr); + return generator.emitBinaryOp(op_bitxor, generator.finalDestination(dst, src1), src1, src2.get(), OperandTypes(m_expr->resultDescriptor(), ResultType::numberTypeIsInt32())); } // ------------------------------ LogicalNotNode ----------------------------------- @@ -1544,20 +1155,20 @@ RegisterID* BinaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* } RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(left, m_rightHasAssignments, right->isPure(generator)); - bool wasTypeof = generator.lastOpcodeID() == op_typeof; - RefPtr<RegisterID> src2 = generator.emitNode(right); + bool wasTypeof = generator.m_lastOpcodeID == op_typeof; + RegisterID* src2 = generator.emitNode(right); generator.emitExpressionInfo(position(), position(), position()); if (wasTypeof && (opcodeID == op_neq || opcodeID == op_nstricteq)) { RefPtr<RegisterID> tmp = generator.tempDestination(dst); if (opcodeID == op_neq) - generator.emitEqualityOp(op_eq, generator.finalDestination(tmp.get(), src1.get()), src1.get(), src2.get()); + generator.emitEqualityOp(op_eq, generator.finalDestination(tmp.get(), src1.get()), src1.get(), src2); else if (opcodeID == op_nstricteq) - generator.emitEqualityOp(op_stricteq, generator.finalDestination(tmp.get(), src1.get()), src1.get(), src2.get()); + generator.emitEqualityOp(op_stricteq, generator.finalDestination(tmp.get(), src1.get()), src1.get(), src2); else RELEASE_ASSERT_NOT_REACHED(); return generator.emitUnaryOp(op_not, generator.finalDestination(dst, tmp.get()), tmp.get()); } - RegisterID* result = generator.emitBinaryOp(opcodeID, generator.finalDestination(dst, src1.get()), src1.get(), src2.get(), OperandTypes(left->resultDescriptor(), right->resultDescriptor())); + RegisterID* result = generator.emitBinaryOp(opcodeID, generator.finalDestination(dst, src1.get()), src1.get(), src2, OperandTypes(left->resultDescriptor(), right->resultDescriptor())); if (opcodeID == op_urshift && dst != generator.ignoredResult()) return generator.emitUnaryOp(op_unsigned, result, result); return result; @@ -1577,8 +1188,8 @@ RegisterID* EqualNode::emitBytecode(BytecodeGenerator& generator, RegisterID* ds std::swap(left, right); RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(left, m_rightHasAssignments, m_expr2->isPure(generator)); - RefPtr<RegisterID> src2 = generator.emitNode(right); - return generator.emitEqualityOp(op_eq, generator.finalDestination(dst, src1.get()), src1.get(), src2.get()); + RegisterID* src2 = generator.emitNode(right); + return generator.emitEqualityOp(op_eq, generator.finalDestination(dst, src1.get()), src1.get(), src2); } RegisterID* StrictEqualNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) @@ -1589,16 +1200,16 @@ RegisterID* StrictEqualNode::emitBytecode(BytecodeGenerator& generator, Register std::swap(left, right); RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(left, m_rightHasAssignments, m_expr2->isPure(generator)); - RefPtr<RegisterID> src2 = generator.emitNode(right); - return generator.emitEqualityOp(op_stricteq, generator.finalDestination(dst, src1.get()), src1.get(), src2.get()); + RegisterID* src2 = generator.emitNode(right); + return generator.emitEqualityOp(op_stricteq, generator.finalDestination(dst, src1.get()), src1.get(), src2); } RegisterID* ThrowableBinaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); - RefPtr<RegisterID> src2 = generator.emitNode(m_expr2); + RegisterID* src2 = generator.emitNode(m_expr2); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - return generator.emitBinaryOp(opcodeID(), generator.finalDestination(dst, src1.get()), src1.get(), src2.get(), OperandTypes(m_expr1->resultDescriptor(), m_expr2->resultDescriptor())); + return generator.emitBinaryOp(opcodeID(), generator.finalDestination(dst, src1.get()), src1.get(), src2, OperandTypes(m_expr1->resultDescriptor(), m_expr2->resultDescriptor())); } RegisterID* InstanceOfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) @@ -1663,18 +1274,14 @@ RegisterID* ConditionalNode::emitBytecode(BytecodeGenerator& generator, Register generator.emitNodeInConditionContext(m_logical, beforeThen.get(), beforeElse.get(), FallThroughMeansTrue); generator.emitLabel(beforeThen.get()); - generator.emitProfileControlFlow(m_expr1->startOffset()); generator.emitNode(newDst.get(), m_expr1); generator.emitJump(afterElse.get()); generator.emitLabel(beforeElse.get()); - generator.emitProfileControlFlow(m_expr1->endOffset() + 1); generator.emitNode(newDst.get(), m_expr2); generator.emitLabel(afterElse.get()); - generator.emitProfileControlFlow(m_expr2->endOffset() + 1); - return newDst.get(); } @@ -1739,102 +1346,60 @@ static ALWAYS_INLINE RegisterID* emitReadModifyAssignment(BytecodeGenerator& gen RegisterID* ReadModifyResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - JSTextPosition newDivot = divotStart() + m_ident.length(); - Variable var = generator.variable(m_ident); - if (RegisterID* local = var.local()) { - generator.emitTDZCheckIfNecessary(var, local, nullptr); - if (var.isReadOnly()) { - generator.emitReadOnlyExceptionIfNeeded(var); - RegisterID* result = emitReadModifyAssignment(generator, generator.finalDestination(dst), local, m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); - generator.emitProfileType(result, divotStart(), divotEnd()); - return result; + if (Local local = generator.local(m_ident)) { + if (local.isReadOnly()) { + generator.emitReadOnlyExceptionIfNeeded(); + return emitReadModifyAssignment(generator, generator.finalDestination(dst), local.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); } - if (generator.leftHandSideNeedsCopy(m_rightHasAssignments, m_right->isPure(generator))) { + if (local.isCaptured() + || generator.leftHandSideNeedsCopy(m_rightHasAssignments, m_right->isPure(generator))) { RefPtr<RegisterID> result = generator.newTemporary(); - generator.emitMove(result.get(), local); + generator.emitMove(result.get(), local.get()); emitReadModifyAssignment(generator, result.get(), result.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); - generator.emitMove(local, result.get()); - generator.invalidateForInContextForLocal(local); - generator.emitProfileType(local, divotStart(), divotEnd()); + generator.emitMove(local.get(), result.get()); return generator.moveToDestinationIfNeeded(dst, result.get()); } - RegisterID* result = emitReadModifyAssignment(generator, local, local, m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); - generator.invalidateForInContextForLocal(local); - generator.emitProfileType(result, divotStart(), divotEnd()); + RegisterID* result = emitReadModifyAssignment(generator, local.get(), local.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); return generator.moveToDestinationIfNeeded(dst, result); } + JSTextPosition newDivot = divotStart() + m_ident.length(); generator.emitExpressionInfo(newDivot, divotStart(), newDivot); - RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var); - RefPtr<RegisterID> value = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, ThrowIfNotFound); - generator.emitTDZCheckIfNecessary(var, value.get(), nullptr); - if (var.isReadOnly()) { - bool threwException = generator.emitReadOnlyExceptionIfNeeded(var); - if (threwException) - return value.get(); - } + RefPtr<RegisterID> scope = generator.emitResolveScope(generator.newTemporary(), m_ident); + RefPtr<RegisterID> value = generator.emitGetFromScope(generator.newTemporary(), scope.get(), m_ident, ThrowIfNotFound); RefPtr<RegisterID> result = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor()), this); - RegisterID* returnResult = generator.emitPutToScope(scope.get(), var, result.get(), ThrowIfNotFound); - generator.emitProfileType(result.get(), var, divotStart(), divotEnd()); - return returnResult; + return generator.emitPutToScope(scope.get(), m_ident, result.get(), ThrowIfNotFound); } // ------------------------------ AssignResolveNode ----------------------------------- RegisterID* AssignResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - Variable var = generator.variable(m_ident); - if (RegisterID* local = var.local()) { - RegisterID* result = nullptr; - if (m_assignmentContext == AssignmentContext::AssignmentExpression) - generator.emitTDZCheckIfNecessary(var, local, nullptr); - - if (var.isReadOnly() && m_assignmentContext != AssignmentContext::ConstDeclarationStatement) { - result = generator.emitNode(dst, m_right); // Execute side effects first. - generator.emitReadOnlyExceptionIfNeeded(var); - generator.emitProfileType(result, var, divotStart(), divotEnd()); - } else if (var.isSpecial()) { + if (Local local = generator.local(m_ident)) { + if (local.isReadOnly()) { + generator.emitReadOnlyExceptionIfNeeded(); + return generator.emitNode(dst, m_right); + } + if (local.isCaptured()) { RefPtr<RegisterID> tempDst = generator.tempDestination(dst); generator.emitNode(tempDst.get(), m_right); - generator.emitMove(local, tempDst.get()); - generator.emitProfileType(local, var, divotStart(), divotEnd()); - generator.invalidateForInContextForLocal(local); - result = generator.moveToDestinationIfNeeded(dst, tempDst.get()); - } else { - RegisterID* right = generator.emitNode(local, m_right); - generator.emitProfileType(right, var, divotStart(), divotEnd()); - generator.invalidateForInContextForLocal(local); - result = generator.moveToDestinationIfNeeded(dst, right); + generator.emitMove(local.get(), tempDst.get()); + return generator.moveToDestinationIfNeeded(dst, tempDst.get()); } - - if (m_assignmentContext == AssignmentContext::DeclarationStatement || m_assignmentContext == AssignmentContext::ConstDeclarationStatement) - generator.liftTDZCheckIfPossible(var); - return result; + RegisterID* result = generator.emitNode(local.get(), m_right); + return generator.moveToDestinationIfNeeded(dst, result); } if (generator.isStrictMode()) generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var); - if (m_assignmentContext == AssignmentContext::AssignmentExpression) - generator.emitTDZCheckIfNecessary(var, nullptr, scope.get()); + RefPtr<RegisterID> scope = generator.emitResolveScope(generator.newTemporary(), m_ident); if (dst == generator.ignoredResult()) dst = 0; RefPtr<RegisterID> result = generator.emitNode(dst, m_right); - if (var.isReadOnly() && m_assignmentContext != AssignmentContext::ConstDeclarationStatement) { - RegisterID* result = generator.emitNode(dst, m_right); // Execute side effects first. - bool threwException = generator.emitReadOnlyExceptionIfNeeded(var); - if (threwException) - return result; - } generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - RegisterID* returnResult = generator.emitPutToScope(scope.get(), var, result.get(), generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound); - generator.emitProfileType(result.get(), var, divotStart(), divotEnd()); - - if (m_assignmentContext == AssignmentContext::DeclarationStatement || m_assignmentContext == AssignmentContext::ConstDeclarationStatement) - generator.liftTDZCheckIfPossible(var); - return returnResult; + return generator.emitPutToScope(scope.get(), m_ident, result.get(), generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound); } // ------------------------------ AssignDotNode ----------------------------------- @@ -1843,11 +1408,10 @@ RegisterID* AssignDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID { RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_rightHasAssignments, m_right->isPure(generator)); RefPtr<RegisterID> value = generator.destinationForAssignResult(dst); - RefPtr<RegisterID> result = generator.emitNode(value.get(), m_right); + RegisterID* result = generator.emitNode(value.get(), m_right); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - RegisterID* forwardResult = (dst == generator.ignoredResult()) ? result.get() : generator.moveToDestinationIfNeeded(generator.tempDestination(result.get()), result.get()); + RegisterID* forwardResult = (dst == generator.ignoredResult()) ? result : generator.moveToDestinationIfNeeded(generator.tempDestination(result), result); generator.emitPutById(base.get(), m_ident, forwardResult); - generator.emitProfileType(forwardResult, divotStart(), divotEnd()); return generator.moveToDestinationIfNeeded(dst, forwardResult); } @@ -1859,19 +1423,17 @@ RegisterID* ReadModifyDotNode::emitBytecode(BytecodeGenerator& generator, Regist generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd()); RefPtr<RegisterID> value = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident); - RegisterID* updatedValue = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), m_right, static_cast<JSC::Operator>(m_operator), OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); + RegisterID* updatedValue = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - RegisterID* ret = generator.emitPutById(base.get(), m_ident, updatedValue); - generator.emitProfileType(updatedValue, divotStart(), divotEnd()); - return ret; + return generator.emitPutById(base.get(), m_ident, updatedValue); } // ------------------------------ AssignErrorNode ----------------------------------- RegisterID* AssignErrorNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) { - return emitThrowReferenceError(generator, ASCIILiteral("Left side of assignment is not a reference.")); + return emitThrowReferenceError(generator, "Left side of assignment is not a reference."); } // ------------------------------ AssignBracketNode ----------------------------------- @@ -1881,17 +1443,11 @@ RegisterID* AssignBracketNode::emitBytecode(BytecodeGenerator& generator, Regist RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments || m_rightHasAssignments, m_subscript->isPure(generator) && m_right->isPure(generator)); RefPtr<RegisterID> property = generator.emitNodeForLeftHandSide(m_subscript, m_rightHasAssignments, m_right->isPure(generator)); RefPtr<RegisterID> value = generator.destinationForAssignResult(dst); - RefPtr<RegisterID> result = generator.emitNode(value.get(), m_right); + RegisterID* result = generator.emitNode(value.get(), m_right); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - RegisterID* forwardResult = (dst == generator.ignoredResult()) ? result.get() : generator.moveToDestinationIfNeeded(generator.tempDestination(result.get()), result.get()); - - if (m_subscript->isString()) - generator.emitPutById(base.get(), static_cast<StringNode*>(m_subscript)->value(), forwardResult); - else - generator.emitPutByVal(base.get(), property.get(), forwardResult); - - generator.emitProfileType(forwardResult, divotStart(), divotEnd()); + RegisterID* forwardResult = (dst == generator.ignoredResult()) ? result : generator.moveToDestinationIfNeeded(generator.tempDestination(result), result); + generator.emitPutByVal(base.get(), property.get(), forwardResult); return generator.moveToDestinationIfNeeded(dst, forwardResult); } @@ -1904,11 +1460,10 @@ RegisterID* ReadModifyBracketNode::emitBytecode(BytecodeGenerator& generator, Re generator.emitExpressionInfo(subexpressionDivot(), subexpressionStart(), subexpressionEnd()); RefPtr<RegisterID> value = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property.get()); - RegisterID* updatedValue = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), m_right, static_cast<JSC::Operator>(m_operator), OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); + RegisterID* updatedValue = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); generator.emitPutByVal(base.get(), property.get(), updatedValue); - generator.emitProfileType(updatedValue, divotStart(), divotEnd()); return updatedValue; } @@ -1917,10 +1472,58 @@ RegisterID* ReadModifyBracketNode::emitBytecode(BytecodeGenerator& generator, Re RegisterID* CommaNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - CommaNode* node = this; - for (; node && node->next(); node = node->next()) - generator.emitNode(generator.ignoredResult(), node->m_expr); - return generator.emitNode(dst, node->m_expr); + ASSERT(m_expressions.size() > 1); + for (size_t i = 0; i < m_expressions.size() - 1; i++) + generator.emitNode(generator.ignoredResult(), m_expressions[i]); + return generator.emitNode(dst, m_expressions.last()); +} + +// ------------------------------ ConstDeclNode ------------------------------------ + +RegisterID* ConstDeclNode::emitCodeSingle(BytecodeGenerator& generator) +{ + // FIXME: This code does not match the behavior of const in Firefox. + if (Local local = generator.constLocal(m_ident)) { + if (!m_init) + return local.get(); + + if (local.isCaptured()) { + RefPtr<RegisterID> tempDst = generator.newTemporary(); + generator.emitNode(tempDst.get(), m_init); + return generator.emitMove(local.get(), tempDst.get()); + } + + return generator.emitNode(local.get(), m_init); + } + + RefPtr<RegisterID> value = m_init ? generator.emitNode(m_init) : generator.emitLoad(0, jsUndefined()); + + if (generator.codeType() == GlobalCode) + return generator.emitInitGlobalConst(m_ident, value.get()); + + if (generator.codeType() != EvalCode) + return value.get(); + + // FIXME: This will result in incorrect assignment if m_ident exists in an intervening with scope. + RefPtr<RegisterID> scope = generator.emitResolveScope(generator.newTemporary(), m_ident); + return generator.emitPutToScope(scope.get(), m_ident, value.get(), DoNotThrowIfNotFound); +} + +RegisterID* ConstDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + RegisterID* result = 0; + for (ConstDeclNode* n = this; n; n = n->m_next) + result = n->emitCodeSingle(generator); + + return result; +} + +// ------------------------------ ConstStatementNode ----------------------------- + +void ConstStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset()); + generator.emitNode(m_next); } // ------------------------------ SourceElements ------------------------------- @@ -1928,13 +1531,15 @@ RegisterID* CommaNode::emitBytecode(BytecodeGenerator& generator, RegisterID* ds inline StatementNode* SourceElements::lastStatement() const { - return m_tail; + size_t size = m_statements.size(); + return size ? m_statements[size - 1] : 0; } inline void SourceElements::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - for (StatementNode* statement = m_head; statement; statement = statement->next()) - generator.emitNode(dst, statement); + size_t size = m_statements.size(); + for (size_t i = 0; i < size; ++i) + generator.emitNode(dst, m_statements[i]); } // ------------------------------ BlockNode ------------------------------------ @@ -1944,7 +1549,7 @@ inline StatementNode* BlockNode::lastStatement() const return m_statements ? m_statements->lastStatement() : 0; } -StatementNode* BlockNode::singleStatement() const +inline StatementNode* BlockNode::singleStatement() const { return m_statements ? m_statements->singleStatement() : 0; } @@ -1953,9 +1558,7 @@ void BlockNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (!m_statements) return; - generator.pushLexicalScope(this, true); m_statements->emitBytecode(generator, dst); - generator.popLexicalScope(this); } // ------------------------------ EmptyStatementNode --------------------------- @@ -1981,56 +1584,15 @@ void ExprStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID* d generator.emitNode(dst, m_expr); } -// ------------------------------ DeclarationStatement ---------------------------- +// ------------------------------ VarStatementNode ---------------------------- -void DeclarationStatement::emitBytecode(BytecodeGenerator& generator, RegisterID*) +void VarStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) { ASSERT(m_expr); generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset()); generator.emitNode(m_expr); } -// ------------------------------ EmptyVarExpression ---------------------------- - -RegisterID* EmptyVarExpression::emitBytecode(BytecodeGenerator& generator, RegisterID*) -{ - // It's safe to return null here because this node will always be a child node of DeclarationStatement which ignores our return value. - if (!generator.vm()->typeProfiler()) - return nullptr; - - Variable var = generator.variable(m_ident); - if (RegisterID* local = var.local()) - generator.emitProfileType(local, var, position(), JSTextPosition(-1, position().offset + m_ident.length(), -1)); - else { - RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var); - RefPtr<RegisterID> value = generator.emitGetFromScope(generator.newTemporary(), scope.get(), var, DoNotThrowIfNotFound); - generator.emitProfileType(value.get(), var, position(), JSTextPosition(-1, position().offset + m_ident.length(), -1)); - } - - return nullptr; -} - -// ------------------------------ EmptyLetExpression ---------------------------- - -RegisterID* EmptyLetExpression::emitBytecode(BytecodeGenerator& generator, RegisterID*) -{ - // Lexical declarations like 'let' must move undefined into their variables so we don't - // get TDZ errors for situations like this: `let x; x;` - Variable var = generator.variable(m_ident); - if (RegisterID* local = var.local()) { - generator.emitLoad(local, jsUndefined()); - generator.emitProfileType(local, var, position(), JSTextPosition(-1, position().offset + m_ident.length(), -1)); - } else { - RefPtr<RegisterID> scope = generator.emitResolveScope(nullptr, var); - RefPtr<RegisterID> value = generator.emitLoad(nullptr, jsUndefined()); - generator.emitPutToScope(scope.get(), var, value.get(), generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound); - generator.emitProfileType(value.get(), var, position(), JSTextPosition(-1, position().offset + m_ident.length(), -1)); - } - - // It's safe to return null here because this node will always be a child node of DeclarationStatement which ignores our return value. - return nullptr; -} - // ------------------------------ IfElseNode --------------------------------------- static inline StatementNode* singleStatement(StatementNode* statementNode) @@ -2085,7 +1647,6 @@ void IfElseNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) generator.emitNodeInConditionContext(m_condition, trueTarget, falseTarget, fallThroughMode); generator.emitLabel(beforeThen.get()); - generator.emitProfileControlFlow(m_ifBlock->startOffset()); if (!didFoldIfBlock) { generator.emitNode(dst, m_ifBlock); @@ -2095,14 +1656,10 @@ void IfElseNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) generator.emitLabel(beforeElse.get()); - if (m_elseBlock) { - generator.emitProfileControlFlow(m_ifBlock->endOffset() + (m_ifBlock->isBlock() ? 1 : 0)); + if (m_elseBlock) generator.emitNode(dst, m_elseBlock); - } generator.emitLabel(afterElse.get()); - StatementNode* endingBlock = m_elseBlock ? m_elseBlock : m_ifBlock; - generator.emitProfileControlFlow(endingBlock->endOffset() + (endingBlock->isBlock() ? 1 : 0)); } // ------------------------------ DoWhileNode ---------------------------------- @@ -2132,13 +1689,12 @@ void WhileNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) LabelScopePtr scope = generator.newLabelScope(LabelScope::Loop); RefPtr<Label> topOfLoop = generator.newLabel(); - generator.emitDebugHook(WillExecuteStatement, m_expr->firstLine(), m_expr->startOffset(), m_expr->lineStartOffset()); + generator.emitDebugHook(WillExecuteStatement, m_expr->lineNo(), m_expr->startOffset(), m_expr->lineStartOffset()); generator.emitNodeInConditionContext(m_expr, topOfLoop.get(), scope->breakTarget(), FallThroughMeansTrue); generator.emitLabel(topOfLoop.get()); generator.emitLoopHint(); - generator.emitProfileControlFlow(m_statement->startOffset()); generator.emitNode(dst, m_statement); generator.emitLabel(scope->continueTarget()); @@ -2147,8 +1703,6 @@ void WhileNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) generator.emitNodeInConditionContext(m_expr, topOfLoop.get(), scope->breakTarget(), FallThroughMeansFalse); generator.emitLabel(scope->breakTarget()); - - generator.emitProfileControlFlow(m_statement->endOffset() + (m_statement->isBlock() ? 1 : 0)); } // ------------------------------ ForNode -------------------------------------- @@ -2157,9 +1711,6 @@ void ForNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { LabelScopePtr scope = generator.newLabelScope(LabelScope::Loop); - RegisterID* forLoopSymbolTable = nullptr; - generator.pushLexicalScope(this, true, &forLoopSymbolTable); - generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset()); if (m_expr1) @@ -2171,13 +1722,11 @@ void ForNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) generator.emitLabel(topOfLoop.get()); generator.emitLoopHint(); - generator.emitProfileControlFlow(m_statement->startOffset()); generator.emitNode(dst, m_statement); generator.emitLabel(scope->continueTarget()); generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset()); - generator.prepareLexicalScopeForNextForLoopIteration(this, forLoopSymbolTable); if (m_expr3) generator.emitNode(generator.ignoredResult(), m_expr3); @@ -2187,272 +1736,131 @@ void ForNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) generator.emitJump(topOfLoop.get()); generator.emitLabel(scope->breakTarget()); - generator.popLexicalScope(this); - generator.emitProfileControlFlow(m_statement->endOffset() + (m_statement->isBlock() ? 1 : 0)); } // ------------------------------ ForInNode ------------------------------------ -RegisterID* ForInNode::tryGetBoundLocal(BytecodeGenerator& generator) +void ForInNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - if (m_lexpr->isResolveNode()) { - const Identifier& ident = static_cast<ResolveNode*>(m_lexpr)->identifier(); - return generator.variable(ident).local(); + LabelScopePtr scope = generator.newLabelScope(LabelScope::Loop); + + if (!m_lexpr->isLocation()) { + emitThrowReferenceError(generator, "Left side of for-in statement is not a reference."); + return; } - if (m_lexpr->isDestructuringNode()) { - DestructuringAssignmentNode* assignNode = static_cast<DestructuringAssignmentNode*>(m_lexpr); - auto binding = assignNode->bindings(); - if (!binding->isBindingNode()) - return nullptr; + generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset()); - auto simpleBinding = static_cast<BindingNode*>(binding); - const Identifier& ident = simpleBinding->boundProperty(); - Variable var = generator.variable(ident); - if (var.isSpecial()) - return nullptr; - return var.local(); - } + RefPtr<RegisterID> base = generator.newTemporary(); + generator.emitNode(base.get(), m_expr); + RefPtr<RegisterID> i = generator.newTemporary(); + RefPtr<RegisterID> size = generator.newTemporary(); + RefPtr<RegisterID> expectedSubscript; + RefPtr<RegisterID> iter = generator.emitGetPropertyNames(generator.newTemporary(), base.get(), i.get(), size.get(), scope->breakTarget()); + generator.emitJump(scope->continueTarget()); - return nullptr; -} + RefPtr<Label> loopStart = generator.newLabel(); + generator.emitLabel(loopStart.get()); + generator.emitLoopHint(); -void ForInNode::emitLoopHeader(BytecodeGenerator& generator, RegisterID* propertyName) -{ + RegisterID* propertyName; + bool optimizedForinAccess = false; if (m_lexpr->isResolveNode()) { const Identifier& ident = static_cast<ResolveNode*>(m_lexpr)->identifier(); - Variable var = generator.variable(ident); - if (RegisterID* local = var.local()) - generator.emitMove(local, propertyName); - else { + Local local = generator.local(ident); + if (!local.get()) { + propertyName = generator.newTemporary(); + RefPtr<RegisterID> protect = propertyName; if (generator.isStrictMode()) generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - RegisterID* scope = generator.emitResolveScope(nullptr, var); + RegisterID* scope = generator.emitResolveScope(generator.newTemporary(), ident); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - generator.emitPutToScope(scope, var, propertyName, generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound); + generator.emitPutToScope(scope, ident, propertyName, generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound); + } else { + expectedSubscript = generator.newTemporary(); + propertyName = expectedSubscript.get(); + generator.emitMove(local.get(), propertyName); + generator.pushOptimisedForIn(expectedSubscript.get(), iter.get(), i.get(), local.get()); + optimizedForinAccess = true; } - generator.emitProfileType(propertyName, var, m_lexpr->position(), JSTextPosition(-1, m_lexpr->position().offset + ident.length(), -1)); - return; - } - if (m_lexpr->isDotAccessorNode()) { + } else if (m_lexpr->isDotAccessorNode()) { DotAccessorNode* assignNode = static_cast<DotAccessorNode*>(m_lexpr); const Identifier& ident = assignNode->identifier(); + propertyName = generator.newTemporary(); + RefPtr<RegisterID> protect = propertyName; RegisterID* base = generator.emitNode(assignNode->base()); + generator.emitExpressionInfo(assignNode->divot(), assignNode->divotStart(), assignNode->divotEnd()); generator.emitPutById(base, ident, propertyName); - generator.emitProfileType(propertyName, assignNode->divotStart(), assignNode->divotEnd()); - return; - } - if (m_lexpr->isBracketAccessorNode()) { + } else if (m_lexpr->isBracketAccessorNode()) { BracketAccessorNode* assignNode = static_cast<BracketAccessorNode*>(m_lexpr); + propertyName = generator.newTemporary(); + RefPtr<RegisterID> protect = propertyName; RefPtr<RegisterID> base = generator.emitNode(assignNode->base()); RegisterID* subscript = generator.emitNode(assignNode->subscript()); + generator.emitExpressionInfo(assignNode->divot(), assignNode->divotStart(), assignNode->divotEnd()); generator.emitPutByVal(base.get(), subscript, propertyName); - generator.emitProfileType(propertyName, assignNode->divotStart(), assignNode->divotEnd()); - return; - } - - if (m_lexpr->isDestructuringNode()) { - DestructuringAssignmentNode* assignNode = static_cast<DestructuringAssignmentNode*>(m_lexpr); + } else { + ASSERT(m_lexpr->isDeconstructionNode()); + DeconstructingAssignmentNode* assignNode = static_cast<DeconstructingAssignmentNode*>(m_lexpr); auto binding = assignNode->bindings(); - if (!binding->isBindingNode()) { - assignNode->bindings()->bindValue(generator, propertyName); - return; - } - - auto simpleBinding = static_cast<BindingNode*>(binding); - const Identifier& ident = simpleBinding->boundProperty(); - Variable var = generator.variable(ident); - if (!var.local() || var.isSpecial()) { + if (binding->isBindingNode()) { + auto simpleBinding = static_cast<BindingNode*>(binding); + Identifier ident = simpleBinding->boundProperty(); + Local local = generator.local(ident); + propertyName = local.get(); + if (!propertyName || local.isCaptured()) + goto genericBinding; + expectedSubscript = generator.emitMove(generator.newTemporary(), propertyName); + generator.pushOptimisedForIn(expectedSubscript.get(), iter.get(), i.get(), propertyName); + optimizedForinAccess = true; + goto completedSimpleBinding; + } else { + genericBinding: + propertyName = generator.newTemporary(); + RefPtr<RegisterID> protect(propertyName); assignNode->bindings()->bindValue(generator, propertyName); - return; } - generator.emitMove(var.local(), propertyName); - generator.emitProfileType(propertyName, var, simpleBinding->divotStart(), simpleBinding->divotEnd()); - return; - } - - RELEASE_ASSERT_NOT_REACHED(); -} - -void ForInNode::emitMultiLoopBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (!m_lexpr->isAssignmentLocation()) { - emitThrowReferenceError(generator, ASCIILiteral("Left side of for-in statement is not a reference.")); - return; - } - - RefPtr<Label> end = generator.newLabel(); - - RegisterID* forLoopSymbolTable = nullptr; - generator.pushLexicalScope(this, true, &forLoopSymbolTable); - - generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset()); - - RefPtr<RegisterID> base = generator.newTemporary(); - RefPtr<RegisterID> length; - RefPtr<RegisterID> enumerator; - generator.emitNode(base.get(), m_expr); - RefPtr<RegisterID> local = this->tryGetBoundLocal(generator); - RefPtr<RegisterID> enumeratorIndex; - - int profilerStartOffset = m_statement->startOffset(); - int profilerEndOffset = m_statement->endOffset() + (m_statement->isBlock() ? 1 : 0); - - enumerator = generator.emitGetPropertyEnumerator(generator.newTemporary(), base.get()); - - // Indexed property loop. - { - LabelScopePtr scope = generator.newLabelScope(LabelScope::Loop); - RefPtr<Label> loopStart = generator.newLabel(); - RefPtr<Label> loopEnd = generator.newLabel(); - - length = generator.emitGetEnumerableLength(generator.newTemporary(), enumerator.get()); - RefPtr<RegisterID> i = generator.emitLoad(generator.newTemporary(), jsNumber(0)); - RefPtr<RegisterID> propertyName = generator.newTemporary(); - - generator.emitLabel(loopStart.get()); - generator.emitLoopHint(); - - RefPtr<RegisterID> result = generator.emitEqualityOp(op_less, generator.newTemporary(), i.get(), length.get()); - generator.emitJumpIfFalse(result.get(), loopEnd.get()); - generator.emitHasIndexedProperty(result.get(), base.get(), i.get()); - generator.emitJumpIfFalse(result.get(), scope->continueTarget()); - - generator.emitToIndexString(propertyName.get(), i.get()); - this->emitLoopHeader(generator, propertyName.get()); - - generator.emitProfileControlFlow(profilerStartOffset); - - generator.pushIndexedForInScope(local.get(), i.get()); - generator.emitNode(dst, m_statement); - generator.popIndexedForInScope(local.get()); - - generator.emitProfileControlFlow(profilerEndOffset); - - generator.emitLabel(scope->continueTarget()); - generator.prepareLexicalScopeForNextForLoopIteration(this, forLoopSymbolTable); - generator.emitInc(i.get()); - generator.emitJump(loopStart.get()); - - generator.emitLabel(scope->breakTarget()); - generator.emitJump(end.get()); - generator.emitLabel(loopEnd.get()); - } - - // Structure property loop. - { - LabelScopePtr scope = generator.newLabelScope(LabelScope::Loop); - RefPtr<Label> loopStart = generator.newLabel(); - RefPtr<Label> loopEnd = generator.newLabel(); - - enumeratorIndex = generator.emitLoad(generator.newTemporary(), jsNumber(0)); - RefPtr<RegisterID> propertyName = generator.newTemporary(); - generator.emitEnumeratorStructurePropertyName(propertyName.get(), enumerator.get(), enumeratorIndex.get()); - - generator.emitLabel(loopStart.get()); - generator.emitLoopHint(); - - RefPtr<RegisterID> result = generator.emitUnaryOp(op_eq_null, generator.newTemporary(), propertyName.get()); - generator.emitJumpIfTrue(result.get(), loopEnd.get()); - generator.emitHasStructureProperty(result.get(), base.get(), propertyName.get(), enumerator.get()); - generator.emitJumpIfFalse(result.get(), scope->continueTarget()); - - this->emitLoopHeader(generator, propertyName.get()); - - generator.emitProfileControlFlow(profilerStartOffset); - - generator.pushStructureForInScope(local.get(), enumeratorIndex.get(), propertyName.get(), enumerator.get()); - generator.emitNode(dst, m_statement); - generator.popStructureForInScope(local.get()); - - generator.emitProfileControlFlow(profilerEndOffset); - - generator.emitLabel(scope->continueTarget()); - generator.prepareLexicalScopeForNextForLoopIteration(this, forLoopSymbolTable); - generator.emitInc(enumeratorIndex.get()); - generator.emitEnumeratorStructurePropertyName(propertyName.get(), enumerator.get(), enumeratorIndex.get()); - generator.emitJump(loopStart.get()); - - generator.emitLabel(scope->breakTarget()); - generator.emitJump(end.get()); - generator.emitLabel(loopEnd.get()); + completedSimpleBinding: + ; } - // Generic property loop. - { - LabelScopePtr scope = generator.newLabelScope(LabelScope::Loop); - RefPtr<Label> loopStart = generator.newLabel(); - RefPtr<Label> loopEnd = generator.newLabel(); - - RefPtr<RegisterID> propertyName = generator.newTemporary(); - - generator.emitEnumeratorGenericPropertyName(propertyName.get(), enumerator.get(), enumeratorIndex.get()); - - generator.emitLabel(loopStart.get()); - generator.emitLoopHint(); - - RefPtr<RegisterID> result = generator.emitUnaryOp(op_eq_null, generator.newTemporary(), propertyName.get()); - generator.emitJumpIfTrue(result.get(), loopEnd.get()); - - generator.emitHasGenericProperty(result.get(), base.get(), propertyName.get()); - generator.emitJumpIfFalse(result.get(), scope->continueTarget()); - - this->emitLoopHeader(generator, propertyName.get()); - - generator.emitProfileControlFlow(profilerStartOffset); - - generator.emitNode(dst, m_statement); - - generator.emitLabel(scope->continueTarget()); - generator.prepareLexicalScopeForNextForLoopIteration(this, forLoopSymbolTable); - generator.emitInc(enumeratorIndex.get()); - generator.emitEnumeratorGenericPropertyName(propertyName.get(), enumerator.get(), enumeratorIndex.get()); - generator.emitJump(loopStart.get()); + generator.emitNode(dst, m_statement); - generator.emitLabel(scope->breakTarget()); - generator.emitJump(end.get()); - generator.emitLabel(loopEnd.get()); - } + if (optimizedForinAccess) + generator.popOptimisedForIn(); + generator.emitLabel(scope->continueTarget()); + generator.emitNextPropertyName(propertyName, base.get(), i.get(), size.get(), iter.get(), loopStart.get()); generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset()); - generator.emitLabel(end.get()); - generator.popLexicalScope(this); - generator.emitProfileControlFlow(profilerEndOffset); -} - -void ForInNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - this->emitMultiLoopBytecode(generator, dst); + generator.emitLabel(scope->breakTarget()); } // ------------------------------ ForOfNode ------------------------------------ void ForOfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - if (!m_lexpr->isAssignmentLocation()) { - emitThrowReferenceError(generator, ASCIILiteral("Left side of for-of statement is not a reference.")); + if (!m_lexpr->isLocation()) { + emitThrowReferenceError(generator, "Left side of for-of statement is not a reference."); return; } - - RegisterID* forLoopSymbolTable = nullptr; - generator.pushLexicalScope(this, true, &forLoopSymbolTable); + + LabelScopePtr scope = generator.newLabelScope(LabelScope::Loop); + generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset()); auto extractor = [this, dst](BytecodeGenerator& generator, RegisterID* value) { if (m_lexpr->isResolveNode()) { const Identifier& ident = static_cast<ResolveNode*>(m_lexpr)->identifier(); - Variable var = generator.variable(ident); - if (RegisterID* local = var.local()) - generator.emitMove(local, value); + if (Local local = generator.local(ident)) + generator.emitMove(local.get(), value); else { if (generator.isStrictMode()) generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - RegisterID* scope = generator.emitResolveScope(nullptr, var); + RegisterID* scope = generator.emitResolveScope(generator.newTemporary(), ident); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); - generator.emitPutToScope(scope, var, value, generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound); + generator.emitPutToScope(scope, ident, value, generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound); } - generator.emitProfileType(value, var, m_lexpr->position(), JSTextPosition(-1, m_lexpr->position().offset + ident.length(), -1)); } else if (m_lexpr->isDotAccessorNode()) { DotAccessorNode* assignNode = static_cast<DotAccessorNode*>(m_lexpr); const Identifier& ident = assignNode->identifier(); @@ -2460,7 +1868,6 @@ void ForOfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) generator.emitExpressionInfo(assignNode->divot(), assignNode->divotStart(), assignNode->divotEnd()); generator.emitPutById(base.get(), ident, value); - generator.emitProfileType(value, assignNode->divotStart(), assignNode->divotEnd()); } else if (m_lexpr->isBracketAccessorNode()) { BracketAccessorNode* assignNode = static_cast<BracketAccessorNode*>(m_lexpr); RefPtr<RegisterID> base = generator.emitNode(assignNode->base()); @@ -2468,18 +1875,14 @@ void ForOfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) generator.emitExpressionInfo(assignNode->divot(), assignNode->divotStart(), assignNode->divotEnd()); generator.emitPutByVal(base.get(), subscript, value); - generator.emitProfileType(value, assignNode->divotStart(), assignNode->divotEnd()); } else { - ASSERT(m_lexpr->isDestructuringNode()); - DestructuringAssignmentNode* assignNode = static_cast<DestructuringAssignmentNode*>(m_lexpr); + ASSERT(m_lexpr->isDeconstructionNode()); + DeconstructingAssignmentNode* assignNode = static_cast<DeconstructingAssignmentNode*>(m_lexpr); assignNode->bindings()->bindValue(generator, value); } - generator.emitProfileControlFlow(m_statement->startOffset()); generator.emitNode(dst, m_statement); }; - generator.emitEnumeration(this, m_expr, extractor, this, forLoopSymbolTable); - generator.popLexicalScope(this); - generator.emitProfileControlFlow(m_statement->endOffset() + (m_statement->isBlock() ? 1 : 0)); + generator.emitEnumeration(this, m_expr, extractor); } // ------------------------------ ContinueNode --------------------------------- @@ -2489,10 +1892,10 @@ Label* ContinueNode::trivialTarget(BytecodeGenerator& generator) if (generator.shouldEmitDebugHooks()) return 0; - LabelScopePtr scope = generator.continueTarget(m_ident); + LabelScope* scope = generator.continueTarget(m_ident); ASSERT(scope); - if (generator.labelScopeDepth() != scope->scopeDepth()) + if (generator.scopeDepth() != scope->scopeDepth()) return 0; return scope->continueTarget(); @@ -2502,13 +1905,11 @@ void ContinueNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) { generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset()); - LabelScopePtr scope = generator.continueTarget(m_ident); + LabelScope* scope = generator.continueTarget(m_ident); ASSERT(scope); - generator.emitPopScopes(generator.scopeRegister(), scope->scopeDepth()); + generator.emitPopScopes(scope->scopeDepth()); generator.emitJump(scope->continueTarget()); - - generator.emitProfileControlFlow(endOffset()); } // ------------------------------ BreakNode ------------------------------------ @@ -2518,10 +1919,10 @@ Label* BreakNode::trivialTarget(BytecodeGenerator& generator) if (generator.shouldEmitDebugHooks()) return 0; - LabelScopePtr scope = generator.breakTarget(m_ident); + LabelScope* scope = generator.breakTarget(m_ident); ASSERT(scope); - if (generator.labelScopeDepth() != scope->scopeDepth()) + if (generator.scopeDepth() != scope->scopeDepth()) return 0; return scope->breakTarget(); @@ -2531,13 +1932,11 @@ void BreakNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) { generator.emitDebugHook(WillExecuteStatement, firstLine(), startOffset(), lineStartOffset()); - LabelScopePtr scope = generator.breakTarget(m_ident); + LabelScope* scope = generator.breakTarget(m_ident); ASSERT(scope); - generator.emitPopScopes(generator.scopeRegister(), scope->scopeDepth()); + generator.emitPopScopes(scope->scopeDepth()); generator.emitJump(scope->breakTarget()); - - generator.emitProfileControlFlow(endOffset()); } // ------------------------------ ReturnNode ----------------------------------- @@ -2551,19 +1950,13 @@ void ReturnNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) dst = 0; RefPtr<RegisterID> returnRegister = m_value ? generator.emitNode(dst, m_value) : generator.emitLoad(dst, jsUndefined()); - generator.emitProfileType(returnRegister.get(), ProfileTypeBytecodeFunctionReturnStatement, divotStart(), divotEnd()); - if (generator.isInFinallyBlock()) { + if (generator.scopeDepth()) { returnRegister = generator.emitMove(generator.newTemporary(), returnRegister.get()); - generator.emitPopScopes(generator.scopeRegister(), 0); + generator.emitPopScopes(0); } generator.emitDebugHook(WillLeaveCallFrame, lastLine(), startOffset(), lineStartOffset()); generator.emitReturn(returnRegister.get()); - generator.emitProfileControlFlow(endOffset()); - // Emitting an unreachable return here is needed in case this op_profile_control_flow is the - // last opcode in a CodeBlock because a CodeBlock's instructions must end with a terminal opcode. - if (generator.vm()->controlFlowProfiler()) - generator.emitReturn(generator.emitLoad(nullptr, jsUndefined())); } // ------------------------------ WithNode ------------------------------------- @@ -2576,14 +1969,13 @@ void WithNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) generator.emitExpressionInfo(m_divot, m_divot - m_expressionLength, m_divot); generator.emitPushWithScope(scope.get()); generator.emitNode(dst, m_statement); - generator.emitPopWithScope(); + generator.emitPopScope(); } // ------------------------------ CaseClauseNode -------------------------------- inline void CaseClauseNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { - generator.emitProfileControlFlow(m_startOffset); if (!m_statements) return; m_statements->emitBytecode(generator, dst); @@ -2750,13 +2142,9 @@ void SwitchNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) LabelScopePtr scope = generator.newLabelScope(LabelScope::Switch); RefPtr<RegisterID> r0 = generator.emitNode(m_expr); - - generator.pushLexicalScope(this, false); m_block->emitBytecodeForBlock(generator, r0.get(), dst); - generator.popLexicalScope(this); generator.emitLabel(scope->breakTarget()); - generator.emitProfileControlFlow(endOffset()); } // ------------------------------ LabelNode ------------------------------------ @@ -2784,8 +2172,6 @@ void ThrowNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) RefPtr<RegisterID> expr = generator.emitNode(m_expr); generator.emitExpressionInfo(divot(), divotStart(), divotEnd()); generator.emitThrow(expr.get()); - - generator.emitProfileControlFlow(endOffset()); } // ------------------------------ TryNode -------------------------------------- @@ -2816,20 +2202,17 @@ void TryNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) // Uncaught exception path: the catch block. RefPtr<Label> here = generator.emitLabel(generator.newLabel().get()); - RefPtr<RegisterID> exceptionRegister = generator.newTemporary(); - RefPtr<RegisterID> thrownValueRegister = generator.newTemporary(); - generator.popTryAndEmitCatch(tryData, exceptionRegister.get(), thrownValueRegister.get(), here.get(), HandlerType::Catch); + RefPtr<RegisterID> exceptionRegister = generator.popTryAndEmitCatch(tryData, generator.newTemporary(), here.get()); if (m_finallyBlock) { // If the catch block throws an exception and we have a finally block, then the finally // block should "catch" that exception. tryData = generator.pushTry(here.get()); } - - generator.emitPushCatchScope(m_thrownValueIdent, thrownValueRegister.get(), m_catchEnvironment); - generator.emitProfileControlFlow(m_tryBlock->endOffset() + 1); + + generator.emitPushNameScope(m_exceptionIdent, exceptionRegister.get(), DontDelete); generator.emitNode(dst, m_catchBlock); - generator.emitPopCatchScope(m_catchEnvironment); + generator.emitPopScope(); generator.emitLabel(catchEndLabel.get()); } @@ -2840,27 +2223,17 @@ void TryNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) RefPtr<Label> finallyEndLabel = generator.newLabel(); - int finallyStartOffset = m_catchBlock ? m_catchBlock->endOffset() + 1 : m_tryBlock->endOffset() + 1; - // Normal path: run the finally code, and jump to the end. - generator.emitProfileControlFlow(finallyStartOffset); generator.emitNode(dst, m_finallyBlock); - generator.emitProfileControlFlow(m_finallyBlock->endOffset() + 1); generator.emitJump(finallyEndLabel.get()); // Uncaught exception path: invoke the finally block, then re-throw the exception. - RefPtr<RegisterID> exceptionRegister = generator.newTemporary(); - RefPtr<RegisterID> thrownValueRegister = generator.newTemporary(); - generator.popTryAndEmitCatch(tryData, exceptionRegister.get(), thrownValueRegister.get(), preFinallyLabel.get(), HandlerType::Finally); - generator.emitProfileControlFlow(finallyStartOffset); + RefPtr<RegisterID> tempExceptionRegister = generator.popTryAndEmitCatch(tryData, generator.newTemporary(), preFinallyLabel.get()); generator.emitNode(dst, m_finallyBlock); - generator.emitThrow(exceptionRegister.get()); + generator.emitThrow(tempExceptionRegister.get()); generator.emitLabel(finallyEndLabel.get()); - generator.emitProfileControlFlow(m_finallyBlock->endOffset() + 1); - } else - generator.emitProfileControlFlow(m_catchBlock->endOffset() + 1); - + } } // ------------------------------ ScopeNode ----------------------------- @@ -2880,19 +2253,12 @@ void ProgramNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) RefPtr<RegisterID> dstRegister = generator.newTemporary(); generator.emitLoad(dstRegister.get(), jsUndefined()); - generator.emitProfileControlFlow(startStartOffset()); emitStatementsBytecode(generator, dstRegister.get()); generator.emitDebugHook(DidExecuteProgram, lastLine(), startOffset(), lineStartOffset()); generator.emitEnd(dstRegister.get()); } -// ------------------------------ ModuleProgramNode -------------------- - -void ModuleProgramNode::emitBytecode(BytecodeGenerator&, RegisterID*) -{ -} - // ------------------------------ EvalNode ----------------------------- void EvalNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) @@ -2907,22 +2273,10 @@ void EvalNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) generator.emitEnd(dstRegister.get()); } -// ------------------------------ FunctionNode ----------------------------- +// ------------------------------ FunctionBodyNode ----------------------------- -void FunctionNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +void FunctionBodyNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) { - if (generator.vm()->typeProfiler()) { - for (size_t i = 0; i < m_parameters->size(); i++) { - // Destructuring parameters are handled in destructuring nodes. - if (!m_parameters->at(i).first->isBindingNode()) - continue; - BindingNode* parameter = static_cast<BindingNode*>(m_parameters->at(i).first); - RegisterID reg(CallFrame::argumentOffset(i)); - generator.emitProfileType(®, ProfileTypeBytecodeFunctionArgument, parameter->divotStart(), parameter->divotEnd()); - } - } - - generator.emitProfileControlFlow(startStartOffset()); generator.emitDebugHook(DidEnterCallFrame, startLine(), startStartOffset(), startLineStartOffset()); emitStatementsBytecode(generator, generator.ignoredResult()); @@ -2939,12 +2293,27 @@ void FunctionNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) // If there is no return we must automatically insert one. if (!returnNode) { RegisterID* r0 = generator.isConstructor() ? generator.thisRegister() : generator.emitLoad(0, jsUndefined()); - generator.emitProfileType(r0, ProfileTypeBytecodeFunctionReturnStatement); // Do not emit expression info for this profile because it's not in the user's source code. ASSERT(startOffset() >= lineStartOffset()); generator.emitDebugHook(WillLeaveCallFrame, lastLine(), startOffset(), lineStartOffset()); generator.emitReturn(r0); return; } + + // If there is a return statment, and it is the only statement in the function, check if this is a numeric compare. + if (static_cast<BlockNode*>(singleStatement)->singleStatement()) { + ExpressionNode* returnValueExpression = returnNode->value(); + if (returnValueExpression && returnValueExpression->isSubtract()) { + ExpressionNode* lhsExpression = static_cast<SubNode*>(returnValueExpression)->lhs(); + ExpressionNode* rhsExpression = static_cast<SubNode*>(returnValueExpression)->rhs(); + if (lhsExpression->isResolveNode() + && rhsExpression->isResolveNode() + && generator.isArgumentNumber(static_cast<ResolveNode*>(lhsExpression)->identifier(), 0) + && generator.isArgumentNumber(static_cast<ResolveNode*>(rhsExpression)->identifier(), 1)) { + + generator.setIsNumericCompareFunction(true); + } + } + } } // ------------------------------ FuncDeclNode --------------------------------- @@ -2959,119 +2328,9 @@ RegisterID* FuncExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID* { return generator.emitNewFunctionExpression(generator.finalDestination(dst), this); } - -#if ENABLE(ES6_CLASS_SYNTAX) -// ------------------------------ ClassDeclNode --------------------------------- - -void ClassDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - generator.emitNode(dst, m_classDeclaration); -} - -// ------------------------------ ClassExprNode --------------------------------- - -RegisterID* ClassExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> superclass; - if (m_classHeritage) { - superclass = generator.newTemporary(); - generator.emitNode(superclass.get(), m_classHeritage); - } - - RefPtr<RegisterID> constructor; - - // FIXME: Make the prototype non-configurable & non-writable. - if (m_constructorExpression) - constructor = generator.emitNode(dst, m_constructorExpression); - else { - constructor = generator.emitNewDefaultConstructor(generator.finalDestination(dst), - m_classHeritage ? ConstructorKind::Derived : ConstructorKind::Base, m_name); - } - - const auto& propertyNames = generator.propertyNames(); - RefPtr<RegisterID> prototype = generator.emitNewObject(generator.newTemporary()); - - if (superclass) { - RefPtr<RegisterID> protoParent = generator.newTemporary(); - generator.emitLoad(protoParent.get(), jsNull()); - - RefPtr<RegisterID> tempRegister = generator.newTemporary(); - - // FIXME: Throw TypeError if it's a generator function. - RefPtr<Label> superclassIsUndefinedLabel = generator.newLabel(); - generator.emitJumpIfTrue(generator.emitIsUndefined(tempRegister.get(), superclass.get()), superclassIsUndefinedLabel.get()); - - RefPtr<Label> superclassIsNullLabel = generator.newLabel(); - generator.emitJumpIfTrue(generator.emitUnaryOp(op_eq_null, tempRegister.get(), superclass.get()), superclassIsNullLabel.get()); - - RefPtr<Label> superclassIsObjectLabel = generator.newLabel(); - generator.emitJumpIfTrue(generator.emitIsObject(tempRegister.get(), superclass.get()), superclassIsObjectLabel.get()); - generator.emitLabel(superclassIsUndefinedLabel.get()); - generator.emitThrowTypeError(ASCIILiteral("The superclass is not an object.")); - generator.emitLabel(superclassIsObjectLabel.get()); - generator.emitGetById(protoParent.get(), superclass.get(), generator.propertyNames().prototype); - - RefPtr<Label> protoParentIsObjectOrNullLabel = generator.newLabel(); - generator.emitJumpIfTrue(generator.emitUnaryOp(op_is_object_or_null, tempRegister.get(), protoParent.get()), protoParentIsObjectOrNullLabel.get()); - generator.emitThrowTypeError(ASCIILiteral("The superclass's prototype is not an object.")); - generator.emitLabel(protoParentIsObjectOrNullLabel.get()); - - generator.emitDirectPutById(constructor.get(), generator.propertyNames().underscoreProto, superclass.get(), PropertyNode::Unknown); - generator.emitLabel(superclassIsNullLabel.get()); - generator.emitDirectPutById(prototype.get(), generator.propertyNames().underscoreProto, protoParent.get(), PropertyNode::Unknown); - - emitPutHomeObject(generator, constructor.get(), prototype.get()); - } - - RefPtr<RegisterID> constructorNameRegister = generator.emitLoad(generator.newTemporary(), propertyNames.constructor); - generator.emitCallDefineProperty(prototype.get(), constructorNameRegister.get(), constructor.get(), nullptr, nullptr, - BytecodeGenerator::PropertyConfigurable | BytecodeGenerator::PropertyWritable, m_position); - - RefPtr<RegisterID> prototypeNameRegister = generator.emitLoad(generator.newTemporary(), propertyNames.prototype); - generator.emitCallDefineProperty(constructor.get(), prototypeNameRegister.get(), prototype.get(), nullptr, nullptr, 0, m_position); - - if (m_staticMethods) - generator.emitNode(constructor.get(), m_staticMethods); - - if (m_instanceMethods) - generator.emitNode(prototype.get(), m_instanceMethods); - - return generator.moveToDestinationIfNeeded(dst, constructor.get()); -} -#endif - -// ------------------------------ ImportDeclarationNode ----------------------- - -void ImportDeclarationNode::emitBytecode(BytecodeGenerator&, RegisterID*) -{ -} - -// ------------------------------ ExportAllDeclarationNode -------------------- - -void ExportAllDeclarationNode::emitBytecode(BytecodeGenerator&, RegisterID*) -{ -} - -// ------------------------------ ExportDefaultDeclarationNode ---------------- - -void ExportDefaultDeclarationNode::emitBytecode(BytecodeGenerator&, RegisterID*) -{ -} - -// ------------------------------ ExportLocalDeclarationNode ------------------ - -void ExportLocalDeclarationNode::emitBytecode(BytecodeGenerator&, RegisterID*) -{ -} - -// ------------------------------ ExportNamedDeclarationNode ------------------ - -void ExportNamedDeclarationNode::emitBytecode(BytecodeGenerator&, RegisterID*) -{ -} - -// ------------------------------ DestructuringAssignmentNode ----------------- -RegisterID* DestructuringAssignmentNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) + +// ------------------------------ DeconstructingAssignmentNode ----------------- +RegisterID* DeconstructingAssignmentNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) { if (RegisterID* result = m_bindings->emitDirectBinding(generator, dst, m_initializer)) return result; @@ -3081,102 +2340,43 @@ RegisterID* DestructuringAssignmentNode::emitBytecode(BytecodeGenerator& generat return generator.moveToDestinationIfNeeded(dst, initializer.get()); } -static void assignDefaultValueIfUndefined(BytecodeGenerator& generator, RegisterID* maybeUndefined, ExpressionNode* defaultValue) +DeconstructionPatternNode::~DeconstructionPatternNode() { - ASSERT(defaultValue); - RefPtr<Label> isNotUndefined = generator.newLabel(); - generator.emitJumpIfFalse(generator.emitIsUndefined(generator.newTemporary(), maybeUndefined), isNotUndefined.get()); - generator.emitNode(maybeUndefined, defaultValue); - generator.emitLabel(isNotUndefined.get()); } - + void ArrayPatternNode::bindValue(BytecodeGenerator& generator, RegisterID* rhs) const { - RefPtr<RegisterID> iterator = generator.newTemporary(); - { - generator.emitGetById(iterator.get(), rhs, generator.propertyNames().iteratorSymbol); - CallArguments args(generator, nullptr); - generator.emitMove(args.thisRegister(), rhs); - generator.emitCall(iterator.get(), iterator.get(), NoExpectedFunction, args, divot(), divotStart(), divotEnd()); - } - - if (m_targetPatterns.isEmpty()) { - generator.emitIteratorClose(iterator.get(), this); - return; - } - - RefPtr<RegisterID> done; - for (auto& target : m_targetPatterns) { - switch (target.bindingType) { - case BindingType::Elision: - case BindingType::Element: { - RefPtr<Label> iterationSkipped = generator.newLabel(); - if (!done) - done = generator.newTemporary(); - else - generator.emitJumpIfTrue(done.get(), iterationSkipped.get()); - - RefPtr<RegisterID> value = generator.newTemporary(); - generator.emitIteratorNext(value.get(), iterator.get(), this); - generator.emitGetById(done.get(), value.get(), generator.propertyNames().done); - generator.emitJumpIfTrue(done.get(), iterationSkipped.get()); - generator.emitGetById(value.get(), value.get(), generator.propertyNames().value); - - { - RefPtr<Label> valueIsSet = generator.newLabel(); - generator.emitJump(valueIsSet.get()); - generator.emitLabel(iterationSkipped.get()); - generator.emitLoad(value.get(), jsUndefined()); - generator.emitLabel(valueIsSet.get()); - } - - if (target.bindingType == BindingType::Element) { - if (target.defaultValue) - assignDefaultValueIfUndefined(generator, value.get(), target.defaultValue); - target.pattern->bindValue(generator, value.get()); - } - break; - } - - case BindingType::RestElement: { - RefPtr<RegisterID> array = generator.emitNewArray(generator.newTemporary(), 0, 0); - - RefPtr<Label> iterationDone = generator.newLabel(); - if (!done) - done = generator.newTemporary(); - else - generator.emitJumpIfTrue(done.get(), iterationDone.get()); - - RefPtr<RegisterID> index = generator.newTemporary(); - generator.emitLoad(index.get(), jsNumber(0)); - RefPtr<Label> loopStart = generator.newLabel(); - generator.emitLabel(loopStart.get()); - - RefPtr<RegisterID> value = generator.newTemporary(); - generator.emitIteratorNext(value.get(), iterator.get(), this); - generator.emitGetById(done.get(), value.get(), generator.propertyNames().done); - generator.emitJumpIfTrue(done.get(), iterationDone.get()); - generator.emitGetById(value.get(), value.get(), generator.propertyNames().value); - - generator.emitDirectPutByVal(array.get(), index.get(), value.get()); - generator.emitInc(index.get()); - generator.emitJump(loopStart.get()); - - generator.emitLabel(iterationDone.get()); - target.pattern->bindValue(generator, array.get()); - break; - } - } + for (size_t i = 0; i < m_targetPatterns.size(); i++) { + auto target = m_targetPatterns[i]; + if (!target) + continue; + RefPtr<RegisterID> temp = generator.newTemporary(); + generator.emitLoad(temp.get(), jsNumber(i)); + generator.emitGetByVal(temp.get(), rhs, temp.get()); + target->bindValue(generator, temp.get()); } - - RefPtr<Label> iteratorClosed = generator.newLabel(); - generator.emitJumpIfTrue(done.get(), iteratorClosed.get()); - generator.emitIteratorClose(iterator.get(), this); - generator.emitLabel(iteratorClosed.get()); } RegisterID* ArrayPatternNode::emitDirectBinding(BytecodeGenerator& generator, RegisterID* dst, ExpressionNode* rhs) { + if (rhs->isResolveNode() + && generator.willResolveToArguments(static_cast<ResolveNode*>(rhs)->identifier()) + && !generator.symbolTable().slowArguments()) { + for (size_t i = 0; i < m_targetPatterns.size(); i++) { + auto target = m_targetPatterns[i]; + if (!target) + continue; + + RefPtr<RegisterID> temp = generator.newTemporary(); + generator.emitLoad(temp.get(), jsNumber(i)); + generator.emitGetArgumentByVal(temp.get(), generator.uncheckedRegisterForArguments(), temp.get()); + target->bindValue(generator, temp.get()); + } + if (dst == generator.ignoredResult() || !dst) + return generator.emitLoad(generator.finalDestination(dst), jsUndefined()); + Local local = generator.local(generator.vm()->propertyNames->arguments); + return generator.moveToDestinationIfNeeded(dst, local.get()); + } if (!rhs->isSimpleArray()) return 0; @@ -3194,15 +2394,13 @@ RegisterID* ArrayPatternNode::emitDirectBinding(BytecodeGenerator& generator, Re for (size_t i = 0; i < m_targetPatterns.size(); i++) { registers.uncheckedAppend(generator.newTemporary()); generator.emitNode(registers.last().get(), elements[i]); - if (m_targetPatterns[i].defaultValue) - assignDefaultValueIfUndefined(generator, registers.last().get(), m_targetPatterns[i].defaultValue); if (resultRegister) generator.emitPutByIndex(resultRegister.get(), i, registers.last().get()); } for (size_t i = 0; i < m_targetPatterns.size(); i++) { - if (m_targetPatterns[i].pattern) - m_targetPatterns[i].pattern->bindValue(generator, registers[i].get()); + if (m_targetPatterns[i]) + m_targetPatterns[i]->bindValue(generator, registers[i].get()); } if (resultRegister) return generator.moveToDestinationIfNeeded(dst, resultRegister.get()); @@ -3213,24 +2411,13 @@ void ArrayPatternNode::toString(StringBuilder& builder) const { builder.append('['); for (size_t i = 0; i < m_targetPatterns.size(); i++) { - const auto& target = m_targetPatterns[i]; - - switch (target.bindingType) { - case BindingType::Elision: + if (!m_targetPatterns[i]) { builder.append(','); - break; - - case BindingType::Element: - target.pattern->toString(builder); - if (i < m_targetPatterns.size() - 1) - builder.append(','); - break; - - case BindingType::RestElement: - builder.append("..."); - target.pattern->toString(builder); - break; + continue; } + m_targetPatterns[i]->toString(builder); + if (i < m_targetPatterns.size() - 1) + builder.append(','); } builder.append(']'); } @@ -3238,7 +2425,7 @@ void ArrayPatternNode::toString(StringBuilder& builder) const void ArrayPatternNode::collectBoundIdentifiers(Vector<Identifier>& identifiers) const { for (size_t i = 0; i < m_targetPatterns.size(); i++) { - if (DestructuringPatternNode* node = m_targetPatterns[i].pattern) + if (DeconstructionPatternNode* node = m_targetPatterns[i].get()) node->collectBoundIdentifiers(identifiers); } } @@ -3247,11 +2434,13 @@ void ObjectPatternNode::toString(StringBuilder& builder) const { builder.append('{'); for (size_t i = 0; i < m_targetPatterns.size(); i++) { - if (m_targetPatterns[i].wasString) - builder.appendQuotedJSONString(m_targetPatterns[i].propertyName.string()); - else + if (m_targetPatterns[i].wasString) { + builder.append('"'); + escapeStringToBuilder(builder, m_targetPatterns[i].propertyName.string()); + builder.append('"'); + } else builder.append(m_targetPatterns[i].propertyName.string()); - builder.append(':'); + builder.append(":"); m_targetPatterns[i].pattern->toString(builder); if (i < m_targetPatterns.size() - 1) builder.append(','); @@ -3265,8 +2454,6 @@ void ObjectPatternNode::bindValue(BytecodeGenerator& generator, RegisterID* rhs) auto& target = m_targetPatterns[i]; RefPtr<RegisterID> temp = generator.newTemporary(); generator.emitGetById(temp.get(), rhs, target.propertyName); - if (target.defaultValue) - assignDefaultValueIfUndefined(generator, temp.get(), target.defaultValue); target.pattern->bindValue(generator, temp.get()); } } @@ -3279,36 +2466,19 @@ void ObjectPatternNode::collectBoundIdentifiers(Vector<Identifier>& identifiers) void BindingNode::bindValue(BytecodeGenerator& generator, RegisterID* value) const { - Variable var = generator.variable(m_boundProperty); - if (RegisterID* local = var.local()) { - if (m_bindingContext == AssignmentContext::AssignmentExpression) - generator.emitTDZCheckIfNecessary(var, local, nullptr); - if (var.isReadOnly() && m_bindingContext != AssignmentContext::ConstDeclarationStatement) { - bool threwException = generator.emitReadOnlyExceptionIfNeeded(var); - if (threwException) - return; + if (Local local = generator.local(m_boundProperty)) { + if (local.isReadOnly()) { + generator.emitReadOnlyExceptionIfNeeded(); + return; } - generator.emitMove(local, value); - generator.emitProfileType(local, var, divotStart(), divotEnd()); - if (m_bindingContext == AssignmentContext::DeclarationStatement || m_bindingContext == AssignmentContext::ConstDeclarationStatement) - generator.liftTDZCheckIfPossible(var); + generator.emitMove(local.get(), value); return; } if (generator.isStrictMode()) generator.emitExpressionInfo(divotEnd(), divotStart(), divotEnd()); - RegisterID* scope = generator.emitResolveScope(nullptr, var); + RegisterID* scope = generator.emitResolveScope(generator.newTemporary(), m_boundProperty); generator.emitExpressionInfo(divotEnd(), divotStart(), divotEnd()); - if (m_bindingContext == AssignmentContext::AssignmentExpression) - generator.emitTDZCheckIfNecessary(var, nullptr, scope); - if (var.isReadOnly() && m_bindingContext != AssignmentContext::ConstDeclarationStatement) { - bool threwException = generator.emitReadOnlyExceptionIfNeeded(var); - if (threwException) - return; - } - generator.emitPutToScope(scope, var, value, generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound); - generator.emitProfileType(value, var, divotStart(), divotEnd()); - if (m_bindingContext == AssignmentContext::DeclarationStatement || m_bindingContext == AssignmentContext::ConstDeclarationStatement) - generator.liftTDZCheckIfPossible(var); + generator.emitPutToScope(scope, m_boundProperty, value, generator.isStrictMode() ? ThrowIfNotFound : DoNotThrowIfNotFound); return; } diff --git a/Source/JavaScriptCore/bytecompiler/RegisterID.h b/Source/JavaScriptCore/bytecompiler/RegisterID.h index 688c8b9c8..83216f613 100644 --- a/Source/JavaScriptCore/bytecompiler/RegisterID.h +++ b/Source/JavaScriptCore/bytecompiler/RegisterID.h @@ -10,7 +10,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. * @@ -70,6 +70,7 @@ namespace JSC { void setIndex(int index) { + ASSERT(!m_refCount); #ifndef NDEBUG m_didSetIndex = true; #endif diff --git a/Source/JavaScriptCore/bytecompiler/StaticPropertyAnalysis.h b/Source/JavaScriptCore/bytecompiler/StaticPropertyAnalysis.h index 5a9918dd1..293c22414 100644 --- a/Source/JavaScriptCore/bytecompiler/StaticPropertyAnalysis.h +++ b/Source/JavaScriptCore/bytecompiler/StaticPropertyAnalysis.h @@ -35,9 +35,9 @@ namespace JSC { // Reference count indicates number of live registers that alias this object. class StaticPropertyAnalysis : public RefCounted<StaticPropertyAnalysis> { public: - static Ref<StaticPropertyAnalysis> create(Vector<UnlinkedInstruction, 0, UnsafeVectorOverflow>* instructions, unsigned target) + static PassRefPtr<StaticPropertyAnalysis> create(Vector<UnlinkedInstruction, 0, UnsafeVectorOverflow>* instructions, unsigned target) { - return adoptRef(*new StaticPropertyAnalysis(instructions, target)); + return adoptRef(new StaticPropertyAnalysis(instructions, target)); } void addPropertyIndex(unsigned propertyIndex) { m_propertyIndexes.add(propertyIndex); } |