/* * 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 * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 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 * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "BytecodeGenerator.h" #include "BatchedTransitionOptimizer.h" #include "Interpreter.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 { void Label::setLocation(unsigned location) { 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; } #ifndef NDEBUG 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 ParserError BytecodeGenerator::generate() { SamplingRegion samplingRegion("Bytecode Generation"); m_codeBlock->setThisRegister(m_thisRegister.index()); m_scopeNode->emitBytecode(*this); m_staticPropertyAnalyzer.kill(); 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->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); SymbolTable::AddResult result = symbolTable().add(ident.impl(), newEntry); if (!result.isNewEntry) { r0 = ®isterFor(result.iterator->value.getIndex()); return false; } r0 = addVar(); return true; } void BytecodeGenerator::preserveLastVar() { if ((m_firstConstantIndex = m_calleeRegisters.size()) != 0) m_lastVar = &m_calleeRegisters.last(); } 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(vm, codeBlock) , m_thisRegister(CallFrame::thisArgumentOffset()) , m_emptyValueRegister(0) , m_globalObjectRegister(0) , m_finallyDepth(0) , m_dynamicScopeDepth(0) , m_codeType(GlobalCode) , m_nextConstantOffset(0) , m_globalConstantIndex(0) , m_hasCreatedActivation(true) , m_firstLazyFunction(0) , m_lastLazyFunction(0) , m_staticPropertyAnalyzer(&m_instructions) , m_vm(&vm) , m_lastOpcodeID(op_end) #ifndef NDEBUG , m_lastOpcodePosition(0) #endif , m_stack(wtfThreadData().stack()) , m_usesExceptions(false) , m_expressionTooDeep(false) { if (m_shouldEmitDebugHooks) m_codeBlock->setNeedsFullScopeChain(true); m_codeBlock->setNumParameters(1); // Allocate space for "this" emitOpcode(op_enter); const VarStack& varStack = programNode->varStack(); const FunctionStack& functionStack = programNode->functionStack(); 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 < varStack.size(); ++i) codeBlock->addVariableDeclaration(*varStack[i].first, !!(varStack[i].second & DeclarationStacks::IsConstant)); } 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_scope(vm, scope) , m_codeBlock(vm, codeBlock) , m_activationRegister(0) , m_emptyValueRegister(0) , m_globalObjectRegister(0) , m_finallyDepth(0) , m_dynamicScopeDepth(0) , m_codeType(FunctionCode) , m_nextConstantOffset(0) , m_globalConstantIndex(0) , m_hasCreatedActivation(false) , m_firstLazyFunction(0) , m_lastLazyFunction(0) , m_staticPropertyAnalyzer(&m_instructions) , m_vm(&vm) , m_lastOpcodeID(op_end) #ifndef NDEBUG , m_lastOpcodePosition(0) #endif , m_stack(wtfThreadData().stack()) , m_usesExceptions(false) , m_expressionTooDeep(false) { if (m_shouldEmitDebugHooks) m_codeBlock->setNeedsFullScopeChain(true); m_symbolTable->setUsesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode()); m_symbolTable->setParameterCountIncludingThis(functionBody->parameters()->size() + 1); emitOpcode(op_enter); if (m_codeBlock->needsFullScopeChain()) { m_activationRegister = addVar(); emitInitLazyRegister(m_activationRegister); m_codeBlock->setActivationRegister(m_activationRegister->index()); } m_symbolTable->setCaptureStart(m_codeBlock->m_numVars); 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) { 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(); 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); } if (shouldCaptureAllTheThings) m_symbolTable->setCaptureEnd(codeBlock->m_numVars); FunctionParameters& parameters = *functionBody->parameters(); m_parameters.grow(parameters.size() + 1); // reserve space for "this" // Add "this" as a parameter int nextParameterIndex = CallFrame::thisArgumentOffset(); m_thisRegister.setIndex(nextParameterIndex--); m_codeBlock->addParameter(); 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(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_scope(vm, scope) , m_codeBlock(vm, codeBlock) , m_thisRegister(CallFrame::thisArgumentOffset()) , m_emptyValueRegister(0) , m_globalObjectRegister(0) , m_finallyDepth(0) , m_dynamicScopeDepth(0) , m_codeType(EvalCode) , m_nextConstantOffset(0) , m_globalConstantIndex(0) , m_hasCreatedActivation(true) , m_firstLazyFunction(0) , m_lastLazyFunction(0) , m_staticPropertyAnalyzer(&m_instructions) , m_vm(&vm) , m_lastOpcodeID(op_end) #ifndef NDEBUG , m_lastOpcodePosition(0) #endif , m_stack(wtfThreadData().stack()) , m_usesExceptions(false) , m_expressionTooDeep(false) { m_codeBlock->setNeedsFullScopeChain(true); m_symbolTable->setUsesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode()); m_codeBlock->setNumParameters(1); emitOpcode(op_enter); const DeclarationStacks::FunctionStack& functionStack = evalNode->functionStack(); for (size_t i = 0; i < functionStack.size(); ++i) m_codeBlock->addFunctionDecl(makeFunction(functionStack[i])); const DeclarationStacks::VarStack& varStack = evalNode->varStack(); unsigned numVariables = varStack.size(); Vector variables; variables.reserveCapacity(numVariables); for (size_t i = 0; i < numVariables; ++i) variables.append(*varStack[i].first); codeBlock->adoptVariables(variables); preserveLastVar(); } BytecodeGenerator::~BytecodeGenerator() { } RegisterID* BytecodeGenerator::emitInitLazyRegister(RegisterID* reg) { emitOpcode(op_init_lazy_reg); instructions().append(reg->index()); return reg; } RegisterID* BytecodeGenerator::resolveCallee(FunctionBodyNode* functionBodyNode) { if (functionBodyNode->ident().isNull() || !functionBodyNode->functionNameIsInScope()) return 0; 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 (!functionBodyNode->captures(functionBodyNode->ident())) return &m_calleeRegister; // 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) { if (ident != propertyNames().arguments) return false; if (!shouldOptimizeLocals()) return false; SymbolTableEntry entry = symbolTable().get(ident.impl()); if (entry.isNull()) return false; if (m_codeBlock->usesArguments() && m_codeType == FunctionCode) return true; return false; } RegisterID* BytecodeGenerator::uncheckedRegisterForArguments() { ASSERT(willResolveToArguments(propertyNames().arguments)); SymbolTableEntry entry = symbolTable().get(propertyNames().arguments.impl()); ASSERT(!entry.isNull()); return ®isterFor(entry.getIndex()); } RegisterID* BytecodeGenerator::createLazyRegisterIfNecessary(RegisterID* reg) { if (m_lastLazyFunction <= reg->index() || reg->index() < m_firstLazyFunction) return reg; emitLazyNewFunction(reg, m_lazyFunctions.get(reg->index())); return reg; } RegisterID* BytecodeGenerator::newRegister() { m_calleeRegisters.append(m_calleeRegisters.size()); m_codeBlock->m_numCalleeRegisters = max(m_codeBlock->m_numCalleeRegisters, m_calleeRegisters.size()); return &m_calleeRegisters.last(); } RegisterID* BytecodeGenerator::newTemporary() { // Reclaim free register IDs. while (m_calleeRegisters.size() && !m_calleeRegisters.last().refCount()) m_calleeRegisters.removeLast(); RegisterID* result = newRegister(); result->setTemporary(); return result; } LabelScopePtr BytecodeGenerator::newLabelScope(LabelScope::Type type, const Identifier* name) { // Reclaim free label scopes. while (m_labelScopes.size() && !m_labelScopes.last().refCount()) m_labelScopes.removeLast(); // Allocate new label scope. LabelScope scope(type, name, scopeDepth(), newLabel(), type == LabelScope::Loop ? newLabel() : PassRefPtr