X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/4e4e5a6f2694187498445a6ac6f1634ce8141119..93a3786624b2768d89bfa27e46598dc64e2fb70a:/bytecompiler/BytecodeGenerator.cpp?ds=sidebyside diff --git a/bytecompiler/BytecodeGenerator.cpp b/bytecompiler/BytecodeGenerator.cpp index c4b3cab..e719380 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, 2013 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 @@ -31,422 +32,484 @@ #include "BytecodeGenerator.h" #include "BatchedTransitionOptimizer.h" -#include "PrototypeFunction.h" -#include "JSFunction.h" #include "Interpreter.h" -#include "UString.h" +#include "JSActivation.h" +#include "JSFunction.h" +#include "JSNameScope.h" +#include "LowLevelInterpreter.h" +#include "Operations.h" +#include "Options.h" +#include "StrongInlines.h" +#include "UnlinkedCodeBlock.h" +#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. -*/ - -#ifndef NDEBUG -static bool s_dumpsGeneratedCode = false; -#endif - -void BytecodeGenerator::setDumpsGeneratedCode(bool dumpsGeneratedCode) +void Label::setLocation(unsigned location) { -#ifndef NDEBUG - s_dumpsGeneratedCode = dumpsGeneratedCode; -#else - UNUSED_PARAM(dumpsGeneratedCode); -#endif + m_location = location; + + unsigned size = m_unresolvedJumps.size(); + for (unsigned i = 0; i < size; ++i) + m_generator->m_instructions[m_unresolvedJumps[i].second].u.operand = m_location - m_unresolvedJumps[i].first; } -bool BytecodeGenerator::dumpsGeneratedCode() -{ #ifndef NDEBUG - return s_dumpsGeneratedCode; -#else - return false; -#endif +void ResolveResult::checkValidity() +{ + switch (m_type) { + case Register: + case ReadOnlyRegister: + ASSERT(m_local); + return; + case Dynamic: + ASSERT(!m_local); + return; + case Lexical: + case ReadOnlyLexical: + ASSERT(!m_local); + return; + default: + RELEASE_ASSERT_NOT_REACHED(); + } } +#endif -void BytecodeGenerator::generate() +ParserError BytecodeGenerator::generate() { + SamplingRegion samplingRegion("Bytecode Generation"); + m_codeBlock->setThisRegister(m_thisRegister.index()); m_scopeNode->emitBytecode(*this); -#ifndef NDEBUG - m_codeBlock->setInstructionCount(m_codeBlock->instructions().size()); - - if (s_dumpsGeneratedCode) - m_codeBlock->dump(m_scopeChain->globalObject()->globalExec()); -#endif + 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(); - m_codeBlock->setIsNumericCompareFunction(instructions() == m_globalData->numericCompareFunction(m_scopeChain->globalObject()->globalExec())); - -#if !ENABLE(OPCODE_SAMPLING) - if (!m_regeneratingForExceptionInfo && (m_codeType == FunctionCode || m_codeType == EvalCode)) - m_codeBlock->clearExceptionInfo(); -#endif + // 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->instructions() = RefCountedArray(m_instructions); m_codeBlock->shrinkToFit(); + + if (m_expressionTooDeep) + return ParserError(ParserError::OutOfMemory); + return ParserError(ParserError::ErrorNone); } bool BytecodeGenerator::addVar(const Identifier& ident, bool isConstant, RegisterID*& r0) { int index = m_calleeRegisters.size(); SymbolTableEntry newEntry(index, isConstant ? ReadOnly : 0); - pair result = symbolTable().add(ident.ustring().rep(), newEntry); + SymbolTable::AddResult result = symbolTable().add(ident.impl(), newEntry); - if (!result.second) { - r0 = ®isterFor(result.first->second.getIndex()); + if (!result.isNewEntry) { + r0 = ®isterFor(result.iterator->value.getIndex()); return false; } - ++m_codeBlock->m_numVars; - r0 = newRegister(); + r0 = addVar(); return true; } -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); - - if (!result.second) - index = result.first->second.getIndex(); - else { - --m_nextGlobalIndex; - m_globals.append(index + m_globalVarStorageOffset); - } - - 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) +BytecodeGenerator::BytecodeGenerator(VM& vm, JSScope*, ProgramNode* programNode, UnlinkedProgramCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode) + : m_shouldEmitDebugHooks(debuggerMode == DebuggerOn) + , m_shouldEmitProfileHooks(profilerMode == ProfilerOn) + , m_symbolTable(0) , m_scopeNode(programNode) - , m_codeBlock(codeBlock) - , m_thisRegister(RegisterFile::ProgramCodeThisRegister) + , m_codeBlock(vm, codeBlock) + , m_thisRegister(CallFrame::thisArgumentOffset()) + , m_emptyValueRegister(0) + , m_globalObjectRegister(0) , 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_hasCreatedActivation(true) + , m_firstLazyFunction(0) + , m_lastLazyFunction(0) + , m_staticPropertyAnalyzer(&m_instructions) + , m_vm(&vm) , m_lastOpcodeID(op_end) - , m_emitNodeDepth(0) - , m_regeneratingForExceptionInfo(false) - , m_codeBlockBeingRegeneratedFrom(0) +#ifndef NDEBUG + , m_lastOpcodePosition(0) +#endif + , m_stack(wtfThreadData().stack()) + , m_usesExceptions(false) + , m_expressionTooDeep(false) { 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->m_numParameters = 1; // Allocate space for "this" + m_codeBlock->setNumParameters(1); // Allocate space for "this" - JSGlobalObject* globalObject = scopeChain.globalObject(); - ExecState* exec = globalObject->globalExec(); - RegisterFile* registerFile = &exec->globalData().interpreter->registerFile(); - - // 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); - - BatchedTransitionOptimizer optimizer(globalObject); + emitOpcode(op_enter); 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); - } - - 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 < functionStack.size(); ++i) { + FunctionBodyNode* function = functionStack[i]; + UnlinkedFunctionExecutable* unlinkedFunction = makeFunction(function); + codeBlock->addFunctionDeclaration(*m_vm, function->ident(), unlinkedFunction); + } - 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)) - continue; - int attributes = DontDelete; - if (varStack[i].second & DeclarationStacks::IsConstant) - attributes |= ReadOnly; - globalObject->putWithAttributes(exec, *varStack[i].first, jsUndefined(), attributes); - } + for (size_t i = 0; i < varStack.size(); ++i) + codeBlock->addVariableDeclaration(*varStack[i].first, !!(varStack[i].second & DeclarationStacks::IsConstant)); - 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) +BytecodeGenerator::BytecodeGenerator(VM& vm, JSScope* scope, 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(codeBlock) + , m_scope(vm, scope) + , m_codeBlock(vm, codeBlock) + , m_activationRegister(0) + , m_emptyValueRegister(0) + , m_globalObjectRegister(0) , 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_hasCreatedActivation(false) + , m_firstLazyFunction(0) + , m_lastLazyFunction(0) + , m_staticPropertyAnalyzer(&m_instructions) + , m_vm(&vm) , m_lastOpcodeID(op_end) - , m_emitNodeDepth(0) - , m_regeneratingForExceptionInfo(false) - , m_codeBlockBeingRegeneratedFrom(0) +#ifndef NDEBUG + , m_lastOpcodePosition(0) +#endif + , m_stack(wtfThreadData().stack()) + , m_usesExceptions(false) + , m_expressionTooDeep(false) { if (m_shouldEmitDebugHooks) m_codeBlock->setNeedsFullScopeChain(true); - codeBlock->setGlobalData(m_globalData); + m_symbolTable->setUsesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode()); + m_symbolTable->setParameterCountIncludingThis(functionBody->parameters()->size() + 1); - bool usesArguments = functionBody->usesArguments(); - codeBlock->setUsesArguments(usesArguments); - if (usesArguments) { - m_argumentsRegister.setIndex(RegisterFile::OptionalCalleeArguments); - addVar(propertyNames().arguments, false); + emitOpcode(op_enter); + if (m_codeBlock->needsFullScopeChain()) { + m_activationRegister = addVar(); + emitInitLazyRegister(m_activationRegister); + m_codeBlock->setActivationRegister(m_activationRegister->index()); } - 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); + m_symbolTable->setCaptureStart(m_codeBlock->m_numVars); - if (usesArguments) { - emitOpcode(op_init_arguments); + if (functionBody->usesArguments() || codeBlock->usesEval() || m_shouldEmitDebugHooks) { // 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'. + + // 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())); + + emitInitLazyRegister(argumentsRegister); + emitInitLazyRegister(unmodifiedArgumentsRegister); + + if (m_codeBlock->isStrictMode()) { + 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) + 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; + if (!functionBody->captures(parameters.at(i)) && !shouldCaptureAllTheThings) + continue; + capturesAnyArgumentByName = true; + capturedArguments[i] = addVar(); + } + } + + if (capturesAnyArgumentByName && !codeBlock->isStrictMode()) { + size_t parameterCount = m_symbolTable->parameterCount(); + OwnArrayPtr slowArguments = adoptArrayPtr(new 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; + } + slowArguments[i].status = SlowArgument::Captured; + slowArguments[i].index = capturedArguments[i]->index(); + } + m_symbolTable->setSlowArguments(slowArguments.release()); + } + + 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, false), function); + } + } + 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); + } + } + bool canLazilyCreateFunctions = !functionBody->needsActivationForMoreThanVariables() && !m_shouldEmitDebugHooks; + if (!canLazilyCreateFunctions && !m_hasCreatedActivation) { + m_hasCreatedActivation = true; + emitOpcode(op_create_activation); + instructions().append(m_activationRegister->index()); + } + + m_symbolTable->setCaptureEnd(codeBlock->m_numVars); + + m_firstLazyFunction = codeBlock->m_numVars; for (size_t i = 0; i < functionStack.size(); ++i) { FunctionBodyNode* function = functionStack[i]; const Identifier& ident = function->ident(); - m_functions.add(ident.ustring().rep()); - emitNewFunction(addVar(ident, false), function); + if (!functionBody->captures(ident)) { + m_functions.add(ident.impl()); + RefPtr reg = addVar(ident, false); + // 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); + else { + emitInitLazyRegister(reg.get()); + m_lazyFunctions.set(reg->index(), function); + } + } + } + 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); } - const DeclarationStacks::VarStack& varStack = functionBody->varStack(); - for (size_t i = 0; i < varStack.size(); ++i) - addVar(*varStack[i].first, varStack[i].second & DeclarationStacks::IsConstant); + if (shouldCaptureAllTheThings) + m_symbolTable->setCaptureEnd(codeBlock->m_numVars); FunctionParameters& parameters = *functionBody->parameters(); - size_t parameterCount = parameters.size(); - m_nextParameterIndex = -RegisterFile::CallFrameHeaderSize - parameterCount - 1; - m_parameters.grow(1 + parameterCount); // reserve space for "this" + m_parameters.grow(parameters.size() + 1); // 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()); - } + int nextParameterIndex = CallFrame::thisArgumentOffset(); + m_thisRegister.setIndex(nextParameterIndex--); + m_codeBlock->addParameter(); - for (size_t i = 0; i < parameterCount; ++i) - addParameter(parameters[i]); - + for (size_t i = 0; i < parameters.size(); ++i, --nextParameterIndex) { + int index = nextParameterIndex; + if (capturedArguments.size() && capturedArguments[i]) { + ASSERT((functionBody->hasCapturedVariables() && functionBody->captures(parameters.at(i))) || shouldCaptureAllTheThings); + index = capturedArguments[i]->index(); + RegisterID original(nextParameterIndex); + emitMove(capturedArguments[i], &original); + } + addParameter(parameters.at(i), index); + } preserveLastVar(); + + // We declare the callee's name last because it should lose to a var, function, and/or parameter declaration. + addCallee(functionBody, calleeRegister); + + if (isConstructor()) { + emitCreateThis(&m_thisRegister); + } else if (!codeBlock->isStrictMode() && (functionBody->usesThis() || codeBlock->usesEval() || m_shouldEmitDebugHooks)) { + UnlinkedValueProfile profile = emitProfiledOpcode(op_convert_this); + instructions().append(kill(&m_thisRegister)); + instructions().append(profile); + } } -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, JSScope* scope, 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(codeBlock) - , m_thisRegister(RegisterFile::ProgramCodeThisRegister) + , m_scope(vm, scope) + , m_codeBlock(vm, codeBlock) + , m_thisRegister(CallFrame::thisArgumentOffset()) + , m_emptyValueRegister(0) + , m_globalObjectRegister(0) , m_finallyDepth(0) , m_dynamicScopeDepth(0) - , m_baseScopeDepth(codeBlock->baseScopeDepth()) , m_codeType(EvalCode) , m_nextConstantOffset(0) , m_globalConstantIndex(0) - , m_globalData(&scopeChain.globalObject()->globalExec()->globalData()) + , m_hasCreatedActivation(true) + , m_firstLazyFunction(0) + , m_lastLazyFunction(0) + , m_staticPropertyAnalyzer(&m_instructions) + , m_vm(&vm) , m_lastOpcodeID(op_end) - , m_emitNodeDepth(0) - , m_regeneratingForExceptionInfo(false) - , m_codeBlockBeingRegeneratedFrom(0) +#ifndef NDEBUG + , m_lastOpcodePosition(0) +#endif + , m_stack(wtfThreadData().stack()) + , m_usesExceptions(false) + , m_expressionTooDeep(false) { - if (m_shouldEmitDebugHooks || m_baseScopeDepth) - m_codeBlock->setNeedsFullScopeChain(true); + m_codeBlock->setNeedsFullScopeChain(true); + + 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" 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) +BytecodeGenerator::~BytecodeGenerator() { - // 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; - } - - // 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) +RegisterID* BytecodeGenerator::emitInitLazyRegister(RegisterID* reg) { - if (ident == propertyNames().thisIdentifier) - return &m_thisRegister; + emitOpcode(op_init_lazy_reg); + instructions().append(reg->index()); + return reg; +} - if (!shouldOptimizeLocals()) +RegisterID* BytecodeGenerator::resolveCallee(FunctionBodyNode* functionBodyNode) +{ + if (functionBodyNode->ident().isNull() || !functionBodyNode->functionNameIsInScope()) return 0; - SymbolTableEntry entry = symbolTable().get(ident.ustring().rep()); - if (entry.isNull()) + m_calleeRegister.setIndex(JSStack::Callee); + + // 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) { + emitOpcode(op_push_name_scope); + instructions().append(addConstant(functionBodyNode->ident())); + instructions().append(m_calleeRegister.index()); + instructions().append(ReadOnly | DontDelete); return 0; + } - if (ident == propertyNames().arguments) - createArgumentsIfNecessary(); + if (!functionBodyNode->captures(functionBodyNode->ident())) + return &m_calleeRegister; - return ®isterFor(entry.getIndex()); + // Move the callee into the captured section of the stack. + return emitMove(addVar(), &m_calleeRegister); +} + +void BytecodeGenerator::addCallee(FunctionBodyNode* functionBodyNode, RegisterID* calleeRegister) +{ + if (functionBodyNode->ident().isNull() || !functionBodyNode->functionNameIsInScope()) + return; + + // 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)); +} + +void BytecodeGenerator::addParameter(const Identifier& ident, int parameterIndex) +{ + // 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(); } bool BytecodeGenerator::willResolveToArguments(const Identifier& ident) @@ -457,10 +520,10 @@ bool BytecodeGenerator::willResolveToArguments(const Identifier& ident) if (!shouldOptimizeLocals()) return false; - SymbolTableEntry entry = symbolTable().get(ident.ustring().rep()); + SymbolTableEntry entry = symbolTable().get(ident.impl()); if (entry.isNull()) return false; - + if (m_codeBlock->usesArguments() && m_codeType == FunctionCode) return true; @@ -471,34 +534,17 @@ RegisterID* BytecodeGenerator::uncheckedRegisterForArguments() { ASSERT(willResolveToArguments(propertyNames().arguments)); - SymbolTableEntry entry = symbolTable().get(propertyNames().arguments.ustring().rep()); + SymbolTableEntry entry = symbolTable().get(propertyNames().arguments.impl()); ASSERT(!entry.isNull()); return ®isterFor(entry.getIndex()); } -RegisterID* BytecodeGenerator::constRegisterFor(const Identifier& ident) -{ - 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) -{ - if (ident == propertyNames().thisIdentifier) - return true; - - return shouldOptimizeLocals() && symbolTable().contains(ident.ustring().rep()); -} - -bool BytecodeGenerator::isLocalConstant(const Identifier& ident) +RegisterID* BytecodeGenerator::createLazyRegisterIfNecessary(RegisterID* reg) { - return symbolTable().get(ident.ustring().rep()).isReadOnly(); + if (m_lastLazyFunction <= reg->index() || reg->index() < m_firstLazyFunction) + return reg; + emitLazyNewFunction(reg, m_lazyFunctions.get(reg->index())); + return reg; } RegisterID* BytecodeGenerator::newRegister() @@ -519,15 +565,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 +574,7 @@ PassRefPtr BytecodeGenerator::newLabelScope(LabelScope::Type type, c // Allocate new label scope. LabelScope scope(type, name, scopeDepth(), newLabel(), type == LabelScope::Loop ? newLabel() : PassRefPtr