X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/f9bf01c6616d5ddcf65b13b33cedf9e387ff7a63..HEAD:/bytecompiler/BytecodeGenerator.cpp diff --git a/bytecompiler/BytecodeGenerator.cpp b/bytecompiler/BytecodeGenerator.cpp index b0a0877..8b2f8e6 100644 --- a/bytecompiler/BytecodeGenerator.cpp +++ b/bytecompiler/BytecodeGenerator.cpp @@ -1,6 +1,7 @@ /* - * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2012-2015 Apple Inc. All rights reserved. * Copyright (C) 2008 Cameron Zwarich + * Copyright (C) 2012 Igalia, S.L. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -11,7 +12,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -30,481 +31,536 @@ #include "config.h" #include "BytecodeGenerator.h" -#include "BatchedTransitionOptimizer.h" -#include "PrototypeFunction.h" -#include "JSFunction.h" +#include "BuiltinExecutables.h" #include "Interpreter.h" -#include "UString.h" +#include "JSFunction.h" +#include "JSLexicalEnvironment.h" +#include "JSNameScope.h" +#include "JSTemplateRegistryKey.h" +#include "LowLevelInterpreter.h" +#include "JSCInlines.h" +#include "Options.h" +#include "StackAlignment.h" +#include "StrongInlines.h" +#include "UnlinkedCodeBlock.h" +#include "UnlinkedInstructionStream.h" +#include +#include using namespace std; namespace JSC { -/* - The layout of a register frame looks like this: - - For +void Label::setLocation(unsigned location) +{ + m_location = location; + + unsigned size = m_unresolvedJumps.size(); + for (unsigned i = 0; i < size; ++i) + m_generator.instructions()[m_unresolvedJumps[i].second].u.operand = m_location - m_unresolvedJumps[i].first; +} - function f(x, y) { - var v1; - function g() { } - var v2; - return (x) * (y); +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); + + for (size_t i = 0; i < m_destructuringParameters.size(); i++) { + auto& entry = m_destructuringParameters[i]; + entry.second->bindValue(*this, entry.first.get()); } - assuming (x) and (y) generated temporaries t1 and t2, you would have - - ------------------------------------ - | x | y | g | v2 | v1 | t1 | t2 | <-- value held - ------------------------------------ - | -5 | -4 | -3 | -2 | -1 | +0 | +1 | <-- register index - ------------------------------------ - | params->|<-locals | temps-> - - Because temporary registers are allocated in a stack-like fashion, we - can reclaim them with a simple popping algorithm. The same goes for labels. - (We never reclaim parameter or local registers, because parameters and - locals are DontDelete.) - - The register layout before a function call looks like this: - - For - - function f(x, y) { + RefPtr temp = newTemporary(); + RefPtr globalScope = scopeRegister(); // FIXME: With lexical scoping, this won't always be the global object: https://bugs.webkit.org/show_bug.cgi?id=142944 + for (auto functionPair : m_functionsToInitialize) { + FunctionBodyNode* functionBody = functionPair.first; + FunctionVariableType functionType = functionPair.second; + emitNewFunction(temp.get(), functionBody); + if (functionType == NormalFunctionVariable) + initializeVariable(variable(functionBody->ident()) , temp.get()); + else if (functionType == GlobalFunctionVariable) + emitPutToScope(globalScope.get(), Variable(functionBody->ident()), temp.get(), ThrowIfNotFound); + else + RELEASE_ASSERT_NOT_REACHED(); + } } + + bool callingClassConstructor = constructorKind() != ConstructorKind::None && !isConstructor(); + if (!callingClassConstructor) + m_scopeNode->emitBytecode(*this); - f(1); - - > <------------------------------ - < > reserved: call frame | 1 | <-- value held - > >snip< <------------------------------ - < > +0 | +1 | +2 | +3 | +4 | +5 | <-- register index - > <------------------------------ - | params->|<-locals | temps-> - - The call instruction fills in the "call frame" registers. It also pads - missing arguments at the end of the call: - - > <----------------------------------- - < > reserved: call frame | 1 | ? | <-- value held ("?" stands for "undefined") - > >snip< <----------------------------------- - < > +0 | +1 | +2 | +3 | +4 | +5 | +6 | <-- register index - > <----------------------------------- - | params->|<-locals | temps-> - - After filling in missing arguments, the call instruction sets up the new - stack frame to overlap the end of the old stack frame: - - |----------------------------------> < - | reserved: call frame | 1 | ? < > <-- value held ("?" stands for "undefined") - |----------------------------------> >snip< < - | -7 | -6 | -5 | -4 | -3 | -2 | -1 < > <-- register index - |----------------------------------> < - | | params->|<-locals | temps-> + m_staticPropertyAnalyzer.kill(); - That way, arguments are "copied" into the callee's stack frame for free. + for (unsigned i = 0; i < m_tryRanges.size(); ++i) { + TryRange& range = m_tryRanges[i]; + int start = range.start->bind(); + int end = range.end->bind(); + + // This will happen for empty try blocks and for some cases of finally blocks: + // + // try { + // try { + // } finally { + // return 42; + // // *HERE* + // } + // } finally { + // print("things"); + // } + // + // The return will pop scopes to execute the outer finally block. But this includes + // popping the try context for the inner try. The try context is live in the fall-through + // part of the finally block not because we will emit a handler that overlaps the finally, + // but because we haven't yet had a chance to plant the catch target. Then when we finish + // emitting code for the outer finally block, we repush the try contex, this time with a + // new start index. But that means that the start index for the try range corresponding + // to the inner-finally-following-the-return (marked as "*HERE*" above) will be greater + // than the end index of the try block. This is harmless since end < start handlers will + // never get matched in our logic, but we do the runtime a favor and choose to not emit + // such handlers at all. + if (end <= start) + continue; + + ASSERT(range.tryData->targetScopeDepth != UINT_MAX); + ASSERT(range.tryData->handlerType != HandlerType::Illegal); + UnlinkedHandlerInfo info(static_cast(start), static_cast(end), + static_cast(range.tryData->target->bind()), range.tryData->targetScopeDepth, + range.tryData->handlerType); + m_codeBlock->addExceptionHandler(info); + } + + m_codeBlock->setInstructions(std::make_unique(m_instructions)); - If the caller supplies too many arguments, this trick doesn't work. The - extra arguments protrude into space reserved for locals and temporaries. - In that case, the call instruction makes a real copy of the call frame header, - along with just the arguments expected by the callee, leaving the original - call frame header and arguments behind. (The call instruction can't just discard - extra arguments, because the "arguments" object may access them later.) - This copying strategy ensures that all named values will be at the indices - expected by the callee. -*/ + m_codeBlock->shrinkToFit(); -#ifndef NDEBUG -static bool s_dumpsGeneratedCode = false; -#endif + if (m_codeBlock->symbolTable() && !m_codeBlock->vm()->typeProfiler()) + m_codeBlock->setSymbolTable(m_codeBlock->symbolTable()->cloneScopePart(*m_codeBlock->vm())); -void BytecodeGenerator::setDumpsGeneratedCode(bool dumpsGeneratedCode) -{ -#ifndef NDEBUG - s_dumpsGeneratedCode = dumpsGeneratedCode; -#else - UNUSED_PARAM(dumpsGeneratedCode); -#endif + if (m_expressionTooDeep) + return ParserError(ParserError::OutOfMemory); + return ParserError(ParserError::ErrorNone); } -bool BytecodeGenerator::dumpsGeneratedCode() +BytecodeGenerator::BytecodeGenerator(VM& vm, ProgramNode* programNode, UnlinkedProgramCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode) + : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn) + , m_shouldEmitProfileHooks(Options::forceProfilerBytecodeGeneration() || profilerMode == ProfilerOn) + , m_scopeNode(programNode) + , m_codeBlock(vm, codeBlock) + , m_thisRegister(CallFrame::thisArgumentOffset()) + , m_codeType(GlobalCode) + , m_vm(&vm) { -#ifndef NDEBUG - return s_dumpsGeneratedCode; -#else - return false; -#endif -} + for (auto& constantRegister : m_linkTimeConstantRegisters) + constantRegister = nullptr; -void BytecodeGenerator::generate() -{ - m_codeBlock->setThisRegister(m_thisRegister.index()); + m_codeBlock->setNumParameters(1); // Allocate space for "this" - m_scopeNode->emitBytecode(*this); + emitOpcode(op_enter); -#ifndef NDEBUG - m_codeBlock->setInstructionCount(m_codeBlock->instructions().size()); + allocateAndEmitScope(); - if (s_dumpsGeneratedCode) - m_codeBlock->dump(m_scopeChain->globalObject()->globalExec()); -#endif + const VarStack& varStack = programNode->varStack(); + const FunctionStack& functionStack = programNode->functionStack(); - if ((m_codeType == FunctionCode && !m_codeBlock->needsFullScopeChain() && !m_codeBlock->usesArguments()) || m_codeType == EvalCode) - symbolTable().clear(); - - m_codeBlock->setIsNumericCompareFunction(instructions() == m_globalData->numericCompareFunction(m_scopeChain->globalObject()->globalExec())); + for (size_t i = 0; i < functionStack.size(); ++i) { + FunctionBodyNode* function = functionStack[i]; + m_functionsToInitialize.append(std::make_pair(function, GlobalFunctionVariable)); + } -#if !ENABLE(OPCODE_SAMPLING) - if (!m_regeneratingForExceptionInfo && (m_codeType == FunctionCode || m_codeType == EvalCode)) - m_codeBlock->clearExceptionInfo(); -#endif + for (size_t i = 0; i < varStack.size(); ++i) + codeBlock->addVariableDeclaration(varStack[i].first, !!(varStack[i].second & DeclarationStacks::IsConstant)); - m_codeBlock->shrinkToFit(); } -bool BytecodeGenerator::addVar(const Identifier& ident, bool isConstant, RegisterID*& r0) +BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, UnlinkedFunctionCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode) + : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn) + , m_shouldEmitProfileHooks(Options::forceProfilerBytecodeGeneration() || profilerMode == ProfilerOn) + , m_symbolTable(codeBlock->symbolTable()) + , m_scopeNode(functionNode) + , m_codeBlock(vm, codeBlock) + , m_codeType(FunctionCode) + , m_vm(&vm) + , m_isBuiltinFunction(codeBlock->isBuiltinFunction()) { - int index = m_calleeRegisters.size(); - SymbolTableEntry newEntry(index, isConstant ? ReadOnly : 0); - pair result = symbolTable().add(ident.ustring().rep(), newEntry); + for (auto& constantRegister : m_linkTimeConstantRegisters) + constantRegister = nullptr; - if (!result.second) { - r0 = ®isterFor(result.first->second.getIndex()); - return false; + if (m_isBuiltinFunction) + m_shouldEmitDebugHooks = false; + + m_symbolTable->setUsesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode()); + Vector boundParameterProperties; + FunctionParameters& parameters = *functionNode->parameters(); + for (size_t i = 0; i < parameters.size(); i++) { + auto pattern = parameters.at(i); + if (pattern->isBindingNode()) + continue; + pattern->collectBoundIdentifiers(boundParameterProperties); + continue; } - ++m_codeBlock->m_numVars; - r0 = newRegister(); - return true; -} + bool shouldCaptureSomeOfTheThings = m_shouldEmitDebugHooks || m_codeBlock->needsFullScopeChain(); + bool shouldCaptureAllOfTheThings = m_shouldEmitDebugHooks || codeBlock->usesEval(); + bool needsArguments = functionNode->usesArguments() || codeBlock->usesEval(); + + auto captures = [&] (UniquedStringImpl* uid) -> bool { + if (shouldCaptureAllOfTheThings) + return true; + 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; + }; -bool BytecodeGenerator::addGlobalVar(const Identifier& ident, bool isConstant, RegisterID*& r0) -{ - int index = m_nextGlobalIndex; - SymbolTableEntry newEntry(index, isConstant ? ReadOnly : 0); - pair result = symbolTable().add(ident.ustring().rep(), newEntry); + emitOpcode(op_enter); - if (!result.second) - index = result.first->second.getIndex(); - else { - --m_nextGlobalIndex; - m_globals.append(index + m_globalVarStorageOffset); + allocateAndEmitScope(); + + m_calleeRegister.setIndex(JSStack::Callee); + + if (functionNameIsInScope(functionNode->ident(), functionNode->functionMode()) + && functionNameScopeIsDynamic(codeBlock->usesEval(), codeBlock->isStrictMode())) { + // When we do this, we should make our local scope stack know about the function name symbol + // table. Currently this works because bytecode linking creates a phony name scope. + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=141885 + // Also, we could create the scope once per JSFunction instance that needs it. That wouldn't + // be any more correct, but it would be more performant. + // FIXME: https://bugs.webkit.org/show_bug.cgi?id=141887 + emitPushFunctionNameScope(m_scopeRegister, functionNode->ident(), &m_calleeRegister, ReadOnly | DontDelete); } - r0 = ®isterFor(index); - return result.second; -} - -void BytecodeGenerator::preserveLastVar() -{ - if ((m_firstConstantIndex = m_calleeRegisters.size()) != 0) - m_lastVar = &m_calleeRegisters.last(); -} - -BytecodeGenerator::BytecodeGenerator(ProgramNode* programNode, const Debugger* debugger, const ScopeChain& scopeChain, SymbolTable* symbolTable, ProgramCodeBlock* codeBlock) - : m_shouldEmitDebugHooks(!!debugger) - , m_shouldEmitProfileHooks(scopeChain.globalObject()->supportsProfiling()) - , m_scopeChain(&scopeChain) - , m_symbolTable(symbolTable) - , m_scopeNode(programNode) - , m_codeBlock(codeBlock) - , m_thisRegister(RegisterFile::ProgramCodeThisRegister) - , m_finallyDepth(0) - , m_dynamicScopeDepth(0) - , m_baseScopeDepth(0) - , m_codeType(GlobalCode) - , m_nextGlobalIndex(-1) - , m_nextConstantOffset(0) - , m_globalConstantIndex(0) - , m_globalData(&scopeChain.globalObject()->globalExec()->globalData()) - , m_lastOpcodeID(op_end) - , m_emitNodeDepth(0) - , m_regeneratingForExceptionInfo(false) - , m_codeBlockBeingRegeneratedFrom(0) -{ - if (m_shouldEmitDebugHooks) - m_codeBlock->setNeedsFullScopeChain(true); - - emitOpcode(op_enter); - codeBlock->setGlobalData(m_globalData); - - // FIXME: Move code that modifies the global object to Interpreter::execute. + if (shouldCaptureSomeOfTheThings) { + m_lexicalEnvironmentRegister = addVar(); + m_codeBlock->setActivationRegister(m_lexicalEnvironmentRegister->virtualRegister()); + emitOpcode(op_create_lexical_environment); + instructions().append(m_lexicalEnvironmentRegister->index()); + instructions().append(scopeRegister()->index()); + emitOpcode(op_mov); + instructions().append(scopeRegister()->index()); + instructions().append(m_lexicalEnvironmentRegister->index()); + } - m_codeBlock->m_numParameters = 1; // Allocate space for "this" + // Make sure the code block knows about all of our parameters, and make sure that parameters + // needing destructuring are noted. + m_parameters.grow(parameters.size() + 1); // reserve space for "this" + m_thisRegister.setIndex(initializeNextParameter()->index()); // this + for (unsigned i = 0; i < parameters.size(); ++i) { + auto pattern = parameters.at(i); + RegisterID* reg = initializeNextParameter(); + if (!pattern->isBindingNode()) + m_destructuringParameters.append(std::make_pair(reg, pattern)); + } + + // Figure out some interesting facts about our arguments. + bool capturesAnyArgumentByName = false; + if (functionNode->hasCapturedVariables()) { + FunctionParameters& parameters = *functionNode->parameters(); + for (size_t i = 0; i < parameters.size(); ++i) { + auto pattern = parameters.at(i); + if (!pattern->isBindingNode()) + continue; + const Identifier& ident = static_cast(pattern)->boundProperty(); + capturesAnyArgumentByName |= captures(ident.impl()); + } + } - JSGlobalObject* globalObject = scopeChain.globalObject(); - ExecState* exec = globalObject->globalExec(); - RegisterFile* registerFile = &exec->globalData().interpreter->registerFile(); + 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 (FunctionBodyNode* function : functionNode->functionStack()) + m_functions.add(function->ident().impl()); - // Shift register indexes in generated code to elide registers allocated by intermediate stack frames. - m_globalVarStorageOffset = -RegisterFile::CallFrameHeaderSize - m_codeBlock->m_numParameters - registerFile->size(); - - // Add previously defined symbols to bookkeeping. - m_globals.grow(symbolTable->size()); - SymbolTable::iterator end = symbolTable->end(); - for (SymbolTable::iterator it = symbolTable->begin(); it != end; ++it) - registerFor(it->second.getIndex()).setIndex(it->second.getIndex() + m_globalVarStorageOffset); + 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. - BatchedTransitionOptimizer optimizer(globalObject); - - const VarStack& varStack = programNode->varStack(); - const FunctionStack& functionStack = programNode->functionStack(); - bool canOptimizeNewGlobals = symbolTable->size() + functionStack.size() + varStack.size() < registerFile->maxGlobals(); - if (canOptimizeNewGlobals) { - // Shift new symbols so they get stored prior to existing symbols. - m_nextGlobalIndex -= symbolTable->size(); - - for (size_t i = 0; i < functionStack.size(); ++i) { - FunctionBodyNode* function = functionStack[i]; - globalObject->removeDirect(function->ident()); // Make sure our new function is not shadowed by an old property. - emitNewFunction(addGlobalVar(function->ident(), false), function); + m_argumentsRegister = addVar(); + m_argumentsRegister->ref(); + } + + if (needsArguments && !codeBlock->isStrictMode()) { + // 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) { + m_symbolTable->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 = m_symbolTable->takeNextScopeOffset(); + m_symbolTable->setArgumentOffset(vm, i, offset); + if (UniquedStringImpl* name = visibleNameForParameter(parameters.at(i))) { + VarOffset varOffset(offset); + SymbolTableEntry entry(varOffset); + // Stores to these variables via the ScopedArguments object will not do + // notifyWrite(), since that would be cumbersome. Also, watching formal + // parameters when "arguments" is in play is unlikely to be super profitable. + // So, we just disable it. + entry.disableWatching(); + m_symbolTable->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(0); + 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))) + m_symbolTable->set(name, SymbolTableEntry(VarOffset(DirectArgumentsOffset(i)))); + } + + emitOpcode(op_create_direct_arguments); + instructions().append(m_argumentsRegister->index()); } - - Vector newVars; - for (size_t i = 0; i < varStack.size(); ++i) - if (!globalObject->hasProperty(exec, *varStack[i].first)) - newVars.append(addGlobalVar(*varStack[i].first, varStack[i].second & DeclarationStacks::IsConstant)); - - preserveLastVar(); - - for (size_t i = 0; i < newVars.size(); ++i) - emitLoad(newVars[i], jsUndefined()); } else { - for (size_t i = 0; i < functionStack.size(); ++i) { - FunctionBodyNode* function = functionStack[i]; - globalObject->putWithAttributes(exec, function->ident(), new (exec) JSFunction(exec, makeFunction(exec, function), scopeChain.node()), DontDelete); - } - for (size_t i = 0; i < varStack.size(); ++i) { - if (globalObject->hasProperty(exec, *varStack[i].first)) + // Create the formal parameters the normal way. Any of them could be captured, or not. If + // captured, lift them into the scope. + for (unsigned i = 0; i < parameters.size(); ++i) { + UniquedStringImpl* name = visibleNameForParameter(parameters.at(i)); + if (!name) + continue; + + if (!captures(name)) { + // This is the easy case - just tell the symbol table about the argument. It will + // be accessed directly. + m_symbolTable->set(name, SymbolTableEntry(VarOffset(virtualRegisterForArgument(1 + i)))); continue; - int attributes = DontDelete; - if (varStack[i].second & DeclarationStacks::IsConstant) - attributes |= ReadOnly; - globalObject->putWithAttributes(exec, *varStack[i].first, jsUndefined(), attributes); + } + + ScopeOffset offset = m_symbolTable->takeNextScopeOffset(); + const Identifier& ident = + static_cast(parameters.at(i))->boundProperty(); + m_symbolTable->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(0); + instructions().append(offset.offset()); } - - preserveLastVar(); } -} - -BytecodeGenerator::BytecodeGenerator(FunctionBodyNode* functionBody, const Debugger* debugger, const ScopeChain& scopeChain, SymbolTable* symbolTable, CodeBlock* codeBlock) - : m_shouldEmitDebugHooks(!!debugger) - , m_shouldEmitProfileHooks(scopeChain.globalObject()->supportsProfiling()) - , m_scopeChain(&scopeChain) - , m_symbolTable(symbolTable) - , m_scopeNode(functionBody) - , m_codeBlock(codeBlock) - , m_finallyDepth(0) - , m_dynamicScopeDepth(0) - , m_baseScopeDepth(0) - , m_codeType(FunctionCode) - , m_nextConstantOffset(0) - , m_globalConstantIndex(0) - , m_globalData(&scopeChain.globalObject()->globalExec()->globalData()) - , m_lastOpcodeID(op_end) - , m_emitNodeDepth(0) - , m_regeneratingForExceptionInfo(false) - , m_codeBlockBeingRegeneratedFrom(0) -{ - if (m_shouldEmitDebugHooks) - m_codeBlock->setNeedsFullScopeChain(true); - - codeBlock->setGlobalData(m_globalData); - - bool usesArguments = functionBody->usesArguments(); - codeBlock->setUsesArguments(usesArguments); - if (usesArguments) { - m_argumentsRegister.setIndex(RegisterFile::OptionalCalleeArguments); - addVar(propertyNames().arguments, false); - } - - if (m_codeBlock->needsFullScopeChain()) { - ++m_codeBlock->m_numVars; - m_activationRegisterIndex = newRegister()->index(); - emitOpcode(op_enter_with_activation); - instructions().append(m_activationRegisterIndex); - } else - emitOpcode(op_enter); - - if (usesArguments) { - emitOpcode(op_init_arguments); - - // The debugger currently retrieves the arguments object from an activation rather than pulling - // it from a call frame. In the long-term it should stop doing that (), - // but for now we force eager creation of the arguments object when debugging. - if (m_shouldEmitDebugHooks) - emitOpcode(op_create_arguments); + + if (needsArguments && codeBlock->isStrictMode()) { + // Allocate an out-of-bands arguments object. + emitOpcode(op_create_out_of_band_arguments); + instructions().append(m_argumentsRegister->index()); } - - const DeclarationStacks::FunctionStack& functionStack = functionBody->functionStack(); - for (size_t i = 0; i < functionStack.size(); ++i) { - FunctionBodyNode* function = functionStack[i]; + + // Now declare all variables. + for (const Identifier& ident : boundParameterProperties) + createVariable(ident, varKind(ident.impl()), IsVariable); + for (FunctionBodyNode* function : functionNode->functionStack()) { const Identifier& ident = function->ident(); - m_functions.add(ident.ustring().rep()); - emitNewFunction(addVar(ident, false), function); + createVariable(ident, varKind(ident.impl()), IsVariable); + m_functionsToInitialize.append(std::make_pair(function, NormalFunctionVariable)); } - - const DeclarationStacks::VarStack& varStack = functionBody->varStack(); - for (size_t i = 0; i < varStack.size(); ++i) - addVar(*varStack[i].first, varStack[i].second & DeclarationStacks::IsConstant); - - FunctionParameters& parameters = *functionBody->parameters(); - size_t parameterCount = parameters.size(); - m_nextParameterIndex = -RegisterFile::CallFrameHeaderSize - parameterCount - 1; - m_parameters.grow(1 + parameterCount); // reserve space for "this" - - // Add "this" as a parameter - m_thisRegister.setIndex(m_nextParameterIndex); - ++m_nextParameterIndex; - ++m_codeBlock->m_numParameters; - - if (functionBody->usesThis() || m_shouldEmitDebugHooks) { - emitOpcode(op_convert_this); - instructions().append(m_thisRegister.index()); + for (auto& entry : functionNode->varStack()) { + ConstantMode constantMode = modeForIsConstant(entry.second & DeclarationStacks::IsConstant); + // Variables named "arguments" are never const. + if (entry.first == propertyNames().arguments) + constantMode = IsVariable; + createVariable(entry.first, varKind(entry.first.impl()), constantMode, IgnoreExisting); } - for (size_t i = 0; i < parameterCount; ++i) - addParameter(parameters[i]); - - preserveLastVar(); + // 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()) + && m_symbolTable->get(functionNode->ident().impl()).isNull()) { + if (captures(functionNode->ident().impl())) { + ScopeOffset offset; + { + ConcurrentJITLocker locker(m_symbolTable->m_lock); + offset = m_symbolTable->takeNextScopeOffset(locker); + m_symbolTable->add( + locker, functionNode->ident().impl(), + SymbolTableEntry(VarOffset(offset), ReadOnly)); + } + + 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(0); + instructions().append(offset.offset()); + } else { + m_symbolTable->add( + functionNode->ident().impl(), + SymbolTableEntry(VarOffset(m_calleeRegister.virtualRegister()), ReadOnly)); + } + } + + // 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)); + if (name == propertyNames().arguments.impl()) { + haveParameterNamedArguments = true; + break; + } + } + + if (!haveParameterNamedArguments) { + createVariable( + propertyNames().arguments, varKind(propertyNames().arguments.impl()), IsVariable); + m_needToInitializeArguments = true; + } + } + + if (isConstructor()) { + if (constructorKind() == ConstructorKind::Derived) { + m_newTargetRegister = addVar(); + emitMove(m_newTargetRegister, &m_thisRegister); + emitMoveEmptyValue(&m_thisRegister); + } else + emitCreateThis(&m_thisRegister); + } else if (constructorKind() != ConstructorKind::None) { + emitThrowTypeError("Cannot call a class constructor"); + } else if (functionNode->usesThis() || codeBlock->usesEval()) { + m_codeBlock->addPropertyAccessInstruction(instructions().size()); + emitOpcode(op_to_this); + instructions().append(kill(&m_thisRegister)); + instructions().append(0); + instructions().append(0); + } } -BytecodeGenerator::BytecodeGenerator(EvalNode* evalNode, const Debugger* debugger, const ScopeChain& scopeChain, SymbolTable* symbolTable, EvalCodeBlock* codeBlock) - : m_shouldEmitDebugHooks(!!debugger) - , m_shouldEmitProfileHooks(scopeChain.globalObject()->supportsProfiling()) - , m_scopeChain(&scopeChain) - , m_symbolTable(symbolTable) +BytecodeGenerator::BytecodeGenerator(VM& vm, EvalNode* evalNode, UnlinkedEvalCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode) + : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn) + , m_shouldEmitProfileHooks(Options::forceProfilerBytecodeGeneration() || profilerMode == ProfilerOn) + , m_symbolTable(codeBlock->symbolTable()) , m_scopeNode(evalNode) - , m_codeBlock(codeBlock) - , m_thisRegister(RegisterFile::ProgramCodeThisRegister) - , m_finallyDepth(0) - , m_dynamicScopeDepth(0) - , m_baseScopeDepth(codeBlock->baseScopeDepth()) + , m_codeBlock(vm, codeBlock) + , m_thisRegister(CallFrame::thisArgumentOffset()) , m_codeType(EvalCode) - , m_nextConstantOffset(0) - , m_globalConstantIndex(0) - , m_globalData(&scopeChain.globalObject()->globalExec()->globalData()) - , m_lastOpcodeID(op_end) - , m_emitNodeDepth(0) - , m_regeneratingForExceptionInfo(false) - , m_codeBlockBeingRegeneratedFrom(0) + , m_vm(&vm) { - if (m_shouldEmitDebugHooks || m_baseScopeDepth) - m_codeBlock->setNeedsFullScopeChain(true); + for (auto& constantRegister : m_linkTimeConstantRegisters) + constantRegister = nullptr; + + m_symbolTable->setUsesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode()); + m_codeBlock->setNumParameters(1); emitOpcode(op_enter); - codeBlock->setGlobalData(m_globalData); - m_codeBlock->m_numParameters = 1; // Allocate space for "this" + + allocateAndEmitScope(); const DeclarationStacks::FunctionStack& functionStack = evalNode->functionStack(); for (size_t i = 0; i < functionStack.size(); ++i) - m_codeBlock->addFunctionDecl(makeFunction(m_globalData, functionStack[i])); + m_codeBlock->addFunctionDecl(makeFunction(functionStack[i])); const DeclarationStacks::VarStack& varStack = evalNode->varStack(); unsigned numVariables = varStack.size(); - Vector variables; + Vector variables; variables.reserveCapacity(numVariables); - for (size_t i = 0; i < numVariables; ++i) - variables.append(*varStack[i].first); - codeBlock->adoptVariables(variables); - - preserveLastVar(); -} - -RegisterID* BytecodeGenerator::addParameter(const Identifier& ident) -{ - // Parameters overwrite var declarations, but not function declarations. - RegisterID* result = 0; - UString::Rep* rep = ident.ustring().rep(); - if (!m_functions.contains(rep)) { - symbolTable().set(rep, m_nextParameterIndex); - RegisterID& parameter = registerFor(m_nextParameterIndex); - parameter.setIndex(m_nextParameterIndex); - result = ¶meter; + for (size_t i = 0; i < numVariables; ++i) { + ASSERT(varStack[i].first.impl()->isAtomic() || varStack[i].first.impl()->isSymbol()); + variables.append(varStack[i].first); } - - // 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_nextParameterIndex; - ++m_codeBlock->m_numParameters; - return result; -} - -RegisterID* BytecodeGenerator::registerFor(const Identifier& ident) -{ - if (ident == propertyNames().thisIdentifier) - return &m_thisRegister; - - if (!shouldOptimizeLocals()) - return 0; - - SymbolTableEntry entry = symbolTable().get(ident.ustring().rep()); - if (entry.isNull()) - return 0; - - if (ident == propertyNames().arguments) - createArgumentsIfNecessary(); - - return ®isterFor(entry.getIndex()); -} - -bool BytecodeGenerator::willResolveToArguments(const Identifier& ident) -{ - if (ident != propertyNames().arguments) - return false; - - if (!shouldOptimizeLocals()) - return false; - - SymbolTableEntry entry = symbolTable().get(ident.ustring().rep()); - if (entry.isNull()) - return false; - - if (m_codeBlock->usesArguments() && m_codeType == FunctionCode) - return true; - - return false; -} - -RegisterID* BytecodeGenerator::uncheckedRegisterForArguments() -{ - ASSERT(willResolveToArguments(propertyNames().arguments)); - - SymbolTableEntry entry = symbolTable().get(propertyNames().arguments.ustring().rep()); - ASSERT(!entry.isNull()); - return ®isterFor(entry.getIndex()); + codeBlock->adoptVariables(variables); } -RegisterID* BytecodeGenerator::constRegisterFor(const Identifier& ident) +BytecodeGenerator::~BytecodeGenerator() { - if (m_codeType == EvalCode) - return 0; - - SymbolTableEntry entry = symbolTable().get(ident.ustring().rep()); - if (entry.isNull()) - return 0; - - return ®isterFor(entry.getIndex()); } -bool BytecodeGenerator::isLocal(const Identifier& ident) +RegisterID* BytecodeGenerator::initializeNextParameter() { - if (ident == propertyNames().thisIdentifier) - return true; - - return shouldOptimizeLocals() && symbolTable().contains(ident.ustring().rep()); + VirtualRegister reg = virtualRegisterForArgument(m_codeBlock->numParameters()); + RegisterID& parameter = registerFor(reg); + parameter.setIndex(reg.offset()); + m_codeBlock->addParameter(); + return ¶meter; } -bool BytecodeGenerator::isLocalConstant(const Identifier& ident) +UniquedStringImpl* BytecodeGenerator::visibleNameForParameter(DestructuringPatternNode* pattern) { - return symbolTable().get(ident.ustring().rep()).isReadOnly(); + if (pattern->isBindingNode()) { + const Identifier& ident = static_cast(pattern)->boundProperty(); + if (!m_functions.contains(ident.impl())) + return ident.impl(); + } + return nullptr; } RegisterID* BytecodeGenerator::newRegister() { - m_calleeRegisters.append(m_calleeRegisters.size()); - m_codeBlock->m_numCalleeRegisters = max(m_codeBlock->m_numCalleeRegisters, m_calleeRegisters.size()); + m_calleeRegisters.append(virtualRegisterForLocal(m_calleeRegisters.size())); + int numCalleeRegisters = max(m_codeBlock->m_numCalleeRegisters, m_calleeRegisters.size()); + numCalleeRegisters = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), numCalleeRegisters); + m_codeBlock->m_numCalleeRegisters = numCalleeRegisters; return &m_calleeRegisters.last(); } @@ -519,15 +575,7 @@ RegisterID* BytecodeGenerator::newTemporary() return result; } -RegisterID* BytecodeGenerator::highestUsedRegister() -{ - size_t count = m_codeBlock->m_numCalleeRegisters; - while (m_calleeRegisters.size() < count) - newRegister(); - return &m_calleeRegisters.last(); -} - -PassRefPtr BytecodeGenerator::newLabelScope(LabelScope::Type type, const Identifier* name) +LabelScopePtr BytecodeGenerator::newLabelScope(LabelScope::Type type, const Identifier* name) { // Reclaim free label scopes. while (m_labelScopes.size() && !m_labelScopes.last().refCount()) @@ -536,7 +584,7 @@ PassRefPtr BytecodeGenerator::newLabelScope(LabelScope::Type type, c // Allocate new label scope. LabelScope scope(type, name, scopeDepth(), newLabel(), type == LabelScope::Loop ? newLabel() : PassRefPtr