X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/6fe7ccc865dc7d7541b93c5bcaf6368d2c98a174..ef99ff287df9046eb88937225e0554eabb00e33c:/bytecompiler/BytecodeGenerator.cpp diff --git a/bytecompiler/BytecodeGenerator.cpp b/bytecompiler/BytecodeGenerator.cpp index f43b4e6..734546a 100644 --- a/bytecompiler/BytecodeGenerator.cpp +++ b/bytecompiler/BytecodeGenerator.cpp @@ -1,6 +1,7 @@ /* - * Copyright (C) 2008, 2009, 2012 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2012, 2013, 2014 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,95 +31,24 @@ #include "config.h" #include "BytecodeGenerator.h" -#include "BatchedTransitionOptimizer.h" -#include "JSFunction.h" #include "Interpreter.h" +#include "JSActivation.h" +#include "JSFunction.h" +#include "JSNameScope.h" #include "LowLevelInterpreter.h" -#include "ScopeChain.h" +#include "JSCInlines.h" +#include "Options.h" +#include "StackAlignment.h" #include "StrongInlines.h" -#include "UString.h" +#include "UnlinkedCodeBlock.h" +#include "UnlinkedInstructionStream.h" +#include +#include using namespace std; namespace JSC { -/* - The layout of a register frame looks like this: - - For - - function f(x, y) { - var v1; - function g() { } - var v2; - return (x) * (y); - } - - 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) - { - } - - 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-> - - That way, arguments are "copied" into the callee's stack frame for free. - - 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. -*/ - -static bool s_dumpsGeneratedCode = false; - void Label::setLocation(unsigned location) { m_location = location; @@ -128,364 +58,444 @@ void Label::setLocation(unsigned location) m_generator->m_instructions[m_unresolvedJumps[i].second].u.operand = m_location - m_unresolvedJumps[i].first; } -void BytecodeGenerator::setDumpsGeneratedCode(bool dumpsGeneratedCode) -{ - s_dumpsGeneratedCode = dumpsGeneratedCode; -} - -bool BytecodeGenerator::dumpsGeneratedCode() -{ - return s_dumpsGeneratedCode; -} - -JSObject* BytecodeGenerator::generate() +ParserError BytecodeGenerator::generate() { SamplingRegion samplingRegion("Bytecode Generation"); - m_codeBlock->setThisRegister(m_thisRegister.index()); + m_codeBlock->setThisRegister(m_thisRegister.virtualRegister()); + for (size_t i = 0; i < m_deconstructedParameters.size(); i++) { + auto& entry = m_deconstructedParameters[i]; + entry.second->bindValue(*this, entry.first.get()); + } m_scopeNode->emitBytecode(*this); - - m_codeBlock->instructions() = RefCountedArray(m_instructions); - if (s_dumpsGeneratedCode) - m_codeBlock->dump(m_scopeChain->globalObject->globalExec()); + m_staticPropertyAnalyzer.kill(); - if ((m_codeType == FunctionCode && !m_codeBlock->needsFullScopeChain() && !m_codeBlock->usesArguments()) || m_codeType == EvalCode) - symbolTable().clear(); + 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); + UnlinkedHandlerInfo info = { + static_cast(start), static_cast(end), + static_cast(range.tryData->target->bind()), + range.tryData->targetScopeDepth + }; + m_codeBlock->addExceptionHandler(info); + } + + m_codeBlock->setInstructions(std::make_unique(m_instructions)); m_codeBlock->shrinkToFit(); + if (m_codeBlock->symbolTable()) + m_codeBlock->setSymbolTable(m_codeBlock->symbolTable()->cloneCapturedNames(*m_codeBlock->vm())); + if (m_expressionTooDeep) - return createOutOfMemoryError(m_scopeChain->globalObject.get()); - return 0; + return ParserError(ParserError::OutOfMemory); + return ParserError(ParserError::ErrorNone); } -bool BytecodeGenerator::addVar(const Identifier& ident, bool isConstant, RegisterID*& r0) +bool BytecodeGenerator::addVar( + const Identifier& ident, ConstantMode constantMode, WatchMode watchMode, RegisterID*& r0) { - int index = m_calleeRegisters.size(); - SymbolTableEntry newEntry(index, isConstant ? ReadOnly : 0); - SymbolTable::AddResult result = symbolTable().add(ident.impl(), newEntry); + ASSERT(static_cast(m_codeBlock->m_numVars) == m_calleeRegisters.size()); + + ConcurrentJITLocker locker(symbolTable().m_lock); + int index = virtualRegisterForLocal(m_calleeRegisters.size()).offset(); + SymbolTableEntry newEntry(index, constantMode == IsConstant ? ReadOnly : 0); + SymbolTable::Map::AddResult result = symbolTable().add(locker, ident.impl(), newEntry); if (!result.isNewEntry) { - r0 = ®isterFor(result.iterator->second.getIndex()); + r0 = ®isterFor(result.iterator->value.getIndex()); return false; } - + + if (watchMode == IsWatchable) { + while (m_watchableVariables.size() < static_cast(m_codeBlock->m_numVars)) + m_watchableVariables.append(Identifier()); + m_watchableVariables.append(ident); + } + r0 = addVar(); + + ASSERT(watchMode == NotWatchable || static_cast(m_codeBlock->m_numVars) == m_watchableVariables.size()); + return true; } -int BytecodeGenerator::addGlobalVar(const Identifier& ident, bool isConstant) -{ - int index = symbolTable().size(); - SymbolTableEntry newEntry(index, isConstant ? ReadOnly : 0); - SymbolTable::AddResult result = symbolTable().add(ident.impl(), newEntry); - if (!result.isNewEntry) - index = result.iterator->second.getIndex(); - return index; -} - void BytecodeGenerator::preserveLastVar() { if ((m_firstConstantIndex = m_calleeRegisters.size()) != 0) m_lastVar = &m_calleeRegisters.last(); } -BytecodeGenerator::BytecodeGenerator(ProgramNode* programNode, ScopeChainNode* scopeChain, SymbolTable* symbolTable, ProgramCodeBlock* codeBlock, CompilationKind compilationKind) - : m_shouldEmitDebugHooks(scopeChain->globalObject->debugger()) - , m_shouldEmitProfileHooks(scopeChain->globalObject->globalObjectMethodTable()->supportsProfiling(scopeChain->globalObject.get())) - , m_shouldEmitRichSourceInfo(scopeChain->globalObject->globalObjectMethodTable()->supportsRichSourceInfo(scopeChain->globalObject.get())) - , m_scopeChain(*scopeChain->globalData, scopeChain) - , m_symbolTable(symbolTable) +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_symbolTable(0) , m_scopeNode(programNode) - , m_codeBlock(codeBlock) + , m_codeBlock(vm, codeBlock) , m_thisRegister(CallFrame::thisArgumentOffset()) + , m_activationRegister(0) + , m_emptyValueRegister(0) + , m_globalObjectRegister(0) , m_finallyDepth(0) - , m_dynamicScopeDepth(0) - , m_baseScopeDepth(0) + , m_localScopeDepth(0) , m_codeType(GlobalCode) , m_nextConstantOffset(0) , m_globalConstantIndex(0) - , m_hasCreatedActivation(true) , m_firstLazyFunction(0) , m_lastLazyFunction(0) - , m_globalData(scopeChain->globalData) + , m_staticPropertyAnalyzer(&m_instructions) + , m_vm(&vm) , m_lastOpcodeID(op_end) #ifndef NDEBUG , m_lastOpcodePosition(0) #endif - , m_stack(wtfThreadData().stack()) , m_usesExceptions(false) , m_expressionTooDeep(false) + , m_isBuiltinFunction(false) { - m_globalData->startedCompiling(m_codeBlock); - 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. - m_codeBlock->setNumParameters(1); // Allocate space for "this" - codeBlock->m_numCapturedVars = codeBlock->m_numVars; - - if (compilationKind == OptimizingCompilation) - return; - JSGlobalObject* globalObject = scopeChain->globalObject.get(); - ExecState* exec = globalObject->globalExec(); - - BatchedTransitionOptimizer optimizer(*m_globalData, globalObject); + emitOpcode(op_enter); const VarStack& varStack = programNode->varStack(); const FunctionStack& functionStack = programNode->functionStack(); - size_t newGlobals = varStack.size() + functionStack.size(); - if (!newGlobals) - return; - globalObject->resizeRegisters(symbolTable->size() + newGlobals); - for (size_t i = 0; i < functionStack.size(); ++i) { FunctionBodyNode* function = functionStack[i]; - globalObject->removeDirect(*m_globalData, function->ident()); // Newly declared functions overwrite existing properties. - - JSValue value = JSFunction::create(exec, makeFunction(exec, function), scopeChain); - int index = addGlobalVar(function->ident(), false); - globalObject->registerAt(index).set(*m_globalData, globalObject, value); + UnlinkedFunctionExecutable* unlinkedFunction = makeFunction(function); + codeBlock->addFunctionDeclaration(*m_vm, function->ident(), unlinkedFunction); } - for (size_t i = 0; i < varStack.size(); ++i) { - if (globalObject->hasProperty(exec, *varStack[i].first)) - continue; - addGlobalVar(*varStack[i].first, varStack[i].second & DeclarationStacks::IsConstant); - } + for (size_t i = 0; i < varStack.size(); ++i) + codeBlock->addVariableDeclaration(varStack[i].first, !!(varStack[i].second & DeclarationStacks::IsConstant)); + } -BytecodeGenerator::BytecodeGenerator(FunctionBodyNode* functionBody, ScopeChainNode* scopeChain, SymbolTable* symbolTable, CodeBlock* codeBlock, CompilationKind) - : m_shouldEmitDebugHooks(scopeChain->globalObject->debugger()) - , m_shouldEmitProfileHooks(scopeChain->globalObject->globalObjectMethodTable()->supportsProfiling(scopeChain->globalObject.get())) - , m_shouldEmitRichSourceInfo(scopeChain->globalObject->globalObjectMethodTable()->supportsRichSourceInfo(scopeChain->globalObject.get())) - , m_scopeChain(*scopeChain->globalData, scopeChain) - , m_symbolTable(symbolTable) +BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionBodyNode* functionBody, UnlinkedFunctionCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode) + : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn) + , m_shouldEmitProfileHooks(Options::forceProfilerBytecodeGeneration() || profilerMode == ProfilerOn) + , m_symbolTable(codeBlock->symbolTable()) , m_scopeNode(functionBody) - , m_codeBlock(codeBlock) + , m_codeBlock(vm, codeBlock) , m_activationRegister(0) + , m_emptyValueRegister(0) + , m_globalObjectRegister(0) , m_finallyDepth(0) - , m_dynamicScopeDepth(0) - , m_baseScopeDepth(0) + , m_localScopeDepth(0) , m_codeType(FunctionCode) , m_nextConstantOffset(0) , m_globalConstantIndex(0) - , m_hasCreatedActivation(false) , m_firstLazyFunction(0) , m_lastLazyFunction(0) - , m_globalData(scopeChain->globalData) + , m_staticPropertyAnalyzer(&m_instructions) + , m_vm(&vm) , m_lastOpcodeID(op_end) #ifndef NDEBUG , m_lastOpcodePosition(0) #endif - , m_stack(wtfThreadData().stack()) , m_usesExceptions(false) , m_expressionTooDeep(false) + , m_isBuiltinFunction(codeBlock->isBuiltinFunction()) { - m_globalData->startedCompiling(m_codeBlock); - if (m_shouldEmitDebugHooks) - m_codeBlock->setNeedsFullScopeChain(true); + if (m_isBuiltinFunction) + m_shouldEmitDebugHooks = false; + + m_symbolTable->setUsesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode()); + Vector boundParameterProperties; + FunctionParameters& parameters = *functionBody->parameters(); + for (size_t i = 0; i < parameters.size(); i++) { + auto pattern = parameters.at(i); + if (pattern->isBindingNode()) + continue; + pattern->collectBoundIdentifiers(boundParameterProperties); + continue; + } + m_symbolTable->setParameterCountIncludingThis(functionBody->parameters()->size() + 1); - codeBlock->setGlobalData(m_globalData); - emitOpcode(op_enter); - if (m_codeBlock->needsFullScopeChain()) { + if (m_codeBlock->needsFullScopeChain() || m_shouldEmitDebugHooks) { m_activationRegister = addVar(); emitInitLazyRegister(m_activationRegister); - m_codeBlock->setActivationRegister(m_activationRegister->index()); + m_codeBlock->setActivationRegister(m_activationRegister->virtualRegister()); } - // Both op_tear_off_activation and op_tear_off_arguments tear off the 'arguments' - // object, if created. - if (m_codeBlock->needsFullScopeChain() || functionBody->usesArguments()) { + 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, false); // Can be changed by assigning to 'arguments'. + 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->index()); - ASSERT_UNUSED(unmodifiedArgumentsRegister, unmodifiedArgumentsRegister->index() == JSC::unmodifiedArgumentsRegister(codeBlock->argumentsRegister())); + codeBlock->setArgumentsRegister(argumentsRegister->virtualRegister()); + ASSERT_UNUSED(unmodifiedArgumentsRegister, unmodifiedArgumentsRegister->virtualRegister() == JSC::unmodifiedArgumentsRegister(codeBlock->argumentsRegister())); emitInitLazyRegister(argumentsRegister); emitInitLazyRegister(unmodifiedArgumentsRegister); - if (m_codeBlock->isStrictMode()) { + if (shouldTearOffArgumentsEagerly()) { emitOpcode(op_create_arguments); instructions().append(argumentsRegister->index()); } + } - // 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); - instructions().append(argumentsRegister->index()); + bool shouldCaptureAllTheThings = m_shouldEmitDebugHooks || codeBlock->usesEval(); + + bool capturesAnyArgumentByName = false; + Vector capturedArguments; + if (functionBody->hasCapturedVariables() || shouldCaptureAllTheThings) { + FunctionParameters& parameters = *functionBody->parameters(); + capturedArguments.resize(parameters.size()); + for (size_t i = 0; i < parameters.size(); ++i) { + capturedArguments[i] = 0; + auto pattern = parameters.at(i); + if (!pattern->isBindingNode()) + continue; + const Identifier& ident = static_cast(pattern)->boundProperty(); + if (!functionBody->captures(ident) && !shouldCaptureAllTheThings) + continue; + capturesAnyArgumentByName = true; + capturedArguments[i] = addVar(); + } + } + + if (capturesAnyArgumentByName && !shouldTearOffArgumentsEagerly()) { + size_t parameterCount = m_symbolTable->parameterCount(); + auto slowArguments = std::make_unique(parameterCount); + for (size_t i = 0; i < parameterCount; ++i) { + if (!capturedArguments[i]) { + ASSERT(slowArguments[i].status == SlowArgument::Normal); + slowArguments[i].index = CallFrame::argumentOffset(i); + continue; + } + slowArguments[i].status = SlowArgument::Captured; + slowArguments[i].index = capturedArguments[i]->index(); } + m_symbolTable->setSlowArguments(WTF::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(); + IdentifierSet test; // 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 < boundParameterProperties.size(); i++) { + const Identifier& ident = boundParameterProperties[i]; + if (functionBody->captures(ident)) + addVar(ident, IsVariable, IsWatchable); + } 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, false), function); + emitNewFunction(addVar(ident, IsVariable, IsWatchable), IsCaptured, function); } } for (size_t i = 0; i < varStack.size(); ++i) { - const Identifier& ident = *varStack[i].first; + const Identifier& ident = varStack[i].first; if (functionBody->captures(ident)) - addVar(ident, varStack[i].second & DeclarationStacks::IsConstant); + addVar(ident, (varStack[i].second & DeclarationStacks::IsConstant) ? IsConstant : IsVariable, IsWatchable); } } - bool canLazilyCreateFunctions = !functionBody->needsActivationForMoreThanVariables() && !m_shouldEmitDebugHooks; - if (!canLazilyCreateFunctions && !m_hasCreatedActivation) { - m_hasCreatedActivation = true; - emitOpcode(op_create_activation); - instructions().append(m_activationRegister->index()); - } - codeBlock->m_numCapturedVars = codeBlock->m_numVars; + m_symbolTable->setCaptureEnd(virtualRegisterForLocal(codeBlock->m_numVars).offset()); + + bool canLazilyCreateFunctions = !functionBody->needsActivationForMoreThanVariables() && !m_shouldEmitDebugHooks; m_firstLazyFunction = codeBlock->m_numVars; for (size_t i = 0; i < functionStack.size(); ++i) { FunctionBodyNode* function = functionStack[i]; const Identifier& ident = function->ident(); if (!functionBody->captures(ident)) { m_functions.add(ident.impl()); - RefPtr reg = addVar(ident, false); + RefPtr reg = addVar(ident, IsVariable, NotWatchable); // Don't lazily create functions that override the name 'arguments' // as this would complicate lazy instantiation of actual arguments. if (!canLazilyCreateFunctions || ident == propertyNames().arguments) - emitNewFunction(reg.get(), function); + emitNewFunction(reg.get(), NotCaptured, function); else { emitInitLazyRegister(reg.get()); - m_lazyFunctions.set(reg->index(), function); + m_lazyFunctions.set(reg->virtualRegister().toLocal(), function); } } } m_lastLazyFunction = canLazilyCreateFunctions ? codeBlock->m_numVars : m_firstLazyFunction; + for (size_t i = 0; i < boundParameterProperties.size(); i++) { + const Identifier& ident = boundParameterProperties[i]; + if (!functionBody->captures(ident)) + addVar(ident, IsVariable, IsWatchable); + } for (size_t i = 0; i < varStack.size(); ++i) { - const Identifier& ident = *varStack[i].first; + const Identifier& ident = varStack[i].first; if (!functionBody->captures(ident)) - addVar(ident, varStack[i].second & DeclarationStacks::IsConstant); + addVar(ident, (varStack[i].second & DeclarationStacks::IsConstant) ? IsConstant : IsVariable, NotWatchable); } - if (m_shouldEmitDebugHooks) - codeBlock->m_numCapturedVars = codeBlock->m_numVars; + if (shouldCaptureAllTheThings) + m_symbolTable->setCaptureEnd(virtualRegisterForLocal(codeBlock->m_numVars).offset()); - FunctionParameters& parameters = *functionBody->parameters(); + if (m_symbolTable->captureCount()) + emitOpcode(op_touch_entry); + m_parameters.grow(parameters.size() + 1); // reserve space for "this" // Add "this" as a parameter int nextParameterIndex = CallFrame::thisArgumentOffset(); - m_thisRegister.setIndex(nextParameterIndex--); + m_thisRegister.setIndex(nextParameterIndex++); m_codeBlock->addParameter(); - - for (size_t i = 0; i < parameters.size(); ++i) - addParameter(parameters[i], nextParameterIndex--); - + 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); + m_deconstructedParameters.append(std::make_pair(¶meter, pattern)); + continue; + } + auto simpleParameter = static_cast(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(); - if (isConstructor()) { - RefPtr func = newTemporary(); - RefPtr funcProto = newTemporary(); - - emitOpcode(op_get_callee); - instructions().append(func->index()); - // Load prototype. - emitGetById(funcProto.get(), func.get(), globalData()->propertyNames->prototype); + // We declare the callee's name last because it should lose to a var, function, and/or parameter declaration. + addCallee(functionBody, calleeRegister); - emitOpcode(op_create_this); - instructions().append(m_thisRegister.index()); - instructions().append(funcProto->index()); - } else if (!codeBlock->isStrictMode() && (functionBody->usesThis() || codeBlock->usesEval() || m_shouldEmitDebugHooks)) { - emitOpcode(op_convert_this); - instructions().append(m_thisRegister.index()); + if (isConstructor()) { + emitCreateThis(&m_thisRegister); + } else if (functionBody->usesThis() || codeBlock->usesEval()) { + m_codeBlock->addPropertyAccessInstruction(instructions().size()); + emitOpcode(op_to_this); + instructions().append(kill(&m_thisRegister)); + instructions().append(0); } } -BytecodeGenerator::BytecodeGenerator(EvalNode* evalNode, ScopeChainNode* scopeChain, SymbolTable* symbolTable, EvalCodeBlock* codeBlock, CompilationKind) - : m_shouldEmitDebugHooks(scopeChain->globalObject->debugger()) - , m_shouldEmitProfileHooks(scopeChain->globalObject->globalObjectMethodTable()->supportsProfiling(scopeChain->globalObject.get())) - , m_shouldEmitRichSourceInfo(scopeChain->globalObject->globalObjectMethodTable()->supportsRichSourceInfo(scopeChain->globalObject.get())) - , m_scopeChain(*scopeChain->globalData, 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_codeBlock(vm, codeBlock) , m_thisRegister(CallFrame::thisArgumentOffset()) + , m_activationRegister(0) + , m_emptyValueRegister(0) + , m_globalObjectRegister(0) , m_finallyDepth(0) - , m_dynamicScopeDepth(0) - , m_baseScopeDepth(codeBlock->baseScopeDepth()) + , m_localScopeDepth(0) , m_codeType(EvalCode) , m_nextConstantOffset(0) , m_globalConstantIndex(0) - , m_hasCreatedActivation(true) , m_firstLazyFunction(0) , m_lastLazyFunction(0) - , m_globalData(scopeChain->globalData) + , m_staticPropertyAnalyzer(&m_instructions) + , m_vm(&vm) , m_lastOpcodeID(op_end) #ifndef NDEBUG , m_lastOpcodePosition(0) #endif - , m_stack(wtfThreadData().stack()) , m_usesExceptions(false) , m_expressionTooDeep(false) + , m_isBuiltinFunction(false) { - m_globalData->startedCompiling(m_codeBlock); - if (m_shouldEmitDebugHooks || m_baseScopeDepth) - m_codeBlock->setNeedsFullScopeChain(true); + m_symbolTable->setUsesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode()); + m_codeBlock->setNumParameters(1); emitOpcode(op_enter); - codeBlock->setGlobalData(m_globalData); - m_codeBlock->setNumParameters(1); 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); + for (size_t i = 0; i < numVariables; ++i) { + ASSERT(varStack[i].first.impl()->isAtomic()); + variables.append(varStack[i].first); + } codeBlock->adoptVariables(variables); - codeBlock->m_numCapturedVars = codeBlock->m_numVars; preserveLastVar(); } BytecodeGenerator::~BytecodeGenerator() { - m_globalData->finishedCompiling(m_codeBlock); } RegisterID* BytecodeGenerator::emitInitLazyRegister(RegisterID* reg) { emitOpcode(op_init_lazy_reg); instructions().append(reg->index()); + ASSERT(!hasWatchableVariable(reg->index())); return reg; } +RegisterID* BytecodeGenerator::resolveCallee(FunctionBodyNode* functionBodyNode) +{ + if (!functionNameIsInScope(functionBodyNode->ident(), functionBodyNode->functionMode())) + return 0; + + if (functionNameScopeIsDynamic(m_codeBlock->usesEval(), m_codeBlock->isStrictMode())) + return 0; + + m_calleeRegister.setIndex(JSStack::Callee); + if (functionBodyNode->captures(functionBodyNode->ident())) + return emitMove(addVar(), IsCaptured, &m_calleeRegister); + + return &m_calleeRegister; +} + +void BytecodeGenerator::addCallee(FunctionBodyNode* functionBodyNode, RegisterID* calleeRegister) +{ + if (!calleeRegister) + return; + + symbolTable().add(functionBodyNode->ident().impl(), SymbolTableEntry(calleeRegister->index(), ReadOnly)); +} + void BytecodeGenerator::addParameter(const Identifier& ident, int parameterIndex) { // Parameters overwrite var declarations, but not function declarations. @@ -501,42 +511,6 @@ void BytecodeGenerator::addParameter(const Identifier& ident, int parameterIndex m_codeBlock->addParameter(); } -RegisterID* BytecodeGenerator::registerFor(const Identifier& ident) -{ - if (ident == propertyNames().thisIdentifier) - return &m_thisRegister; - - if (m_codeType == GlobalCode) - return 0; - - if (!shouldOptimizeLocals()) - return 0; - - SymbolTableEntry entry = symbolTable().get(ident.impl()); - if (entry.isNull()) - return 0; - - if (ident == propertyNames().arguments) - createArgumentsIfNecessary(); - - return createLazyRegisterIfNecessary(®isterFor(entry.getIndex())); -} - -RegisterID* BytecodeGenerator::constRegisterFor(const Identifier& ident) -{ - if (m_codeType == EvalCode) - return 0; - - if (m_codeType == GlobalCode) - return 0; - - SymbolTableEntry entry = symbolTable().get(ident.impl()); - if (entry.isNull()) - return 0; - - return createLazyRegisterIfNecessary(®isterFor(entry.getIndex())); -} - bool BytecodeGenerator::willResolveToArguments(const Identifier& ident) { if (ident != propertyNames().arguments) @@ -548,7 +522,7 @@ bool BytecodeGenerator::willResolveToArguments(const Identifier& ident) SymbolTableEntry entry = symbolTable().get(ident.impl()); if (entry.isNull()) return false; - + if (m_codeBlock->usesArguments() && m_codeType == FunctionCode) return true; @@ -566,29 +540,23 @@ RegisterID* BytecodeGenerator::uncheckedRegisterForArguments() RegisterID* BytecodeGenerator::createLazyRegisterIfNecessary(RegisterID* reg) { - if (m_lastLazyFunction <= reg->index() || reg->index() < m_firstLazyFunction) + if (!reg->virtualRegister().isLocal()) return reg; - emitLazyNewFunction(reg, m_lazyFunctions.get(reg->index())); - return reg; -} -bool BytecodeGenerator::isLocal(const Identifier& ident) -{ - if (ident == propertyNames().thisIdentifier) - return true; - - return shouldOptimizeLocals() && symbolTable().contains(ident.impl()); -} + int localVariableNumber = reg->virtualRegister().toLocal(); -bool BytecodeGenerator::isLocalConstant(const Identifier& ident) -{ - return symbolTable().get(ident.impl()).isReadOnly(); + if (m_lastLazyFunction <= localVariableNumber || localVariableNumber < m_firstLazyFunction) + return reg; + emitLazyNewFunction(reg, m_lazyFunctions.get(localVariableNumber)); + return reg; } 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(); } @@ -603,15 +571,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()) @@ -620,7 +580,7 @@ PassRefPtr BytecodeGenerator::newLabelScope(LabelScope::Type type, c // Allocate new label scope. LabelScope scope(type, name, scopeDepth(), newLabel(), type == LabelScope::Loop ? newLabel() : PassRefPtr