/* * 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 * 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 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 "Interpreter.h" #include "JSActivation.h" #include "JSFunction.h" #include "JSNameScope.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 { 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; } ParserError BytecodeGenerator::generate() { SamplingRegion samplingRegion("Bytecode Generation"); 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_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->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 ParserError(ParserError::OutOfMemory); return ParserError(ParserError::ErrorNone); } bool BytecodeGenerator::addVar( const Identifier& ident, ConstantMode constantMode, WatchMode watchMode, RegisterID*& r0) { 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->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; } void BytecodeGenerator::preserveLastVar() { if ((m_firstConstantIndex = m_calleeRegisters.size()) != 0) m_lastVar = &m_calleeRegisters.last(); } BytecodeGenerator::BytecodeGenerator(VM& vm, ProgramNode* programNode, UnlinkedProgramCodeBlock* codeBlock, DebuggerMode debuggerMode, ProfilerMode profilerMode) : m_shouldEmitDebugHooks(Options::forceDebuggerBytecodeGeneration() || debuggerMode == DebuggerOn) , m_shouldEmitProfileHooks(Options::forceProfilerBytecodeGeneration() || profilerMode == ProfilerOn) , m_symbolTable(0) , m_scopeNode(programNode) , m_codeBlock(vm, codeBlock) , m_thisRegister(CallFrame::thisArgumentOffset()) , m_activationRegister(0) , m_emptyValueRegister(0) , m_globalObjectRegister(0) , m_finallyDepth(0) , m_localScopeDepth(0) , m_codeType(GlobalCode) , m_nextConstantOffset(0) , m_globalConstantIndex(0) , m_firstLazyFunction(0) , m_lastLazyFunction(0) , m_staticPropertyAnalyzer(&m_instructions) , m_vm(&vm) , m_lastOpcodeID(op_end) #ifndef NDEBUG , m_lastOpcodePosition(0) #endif , m_usesExceptions(false) , m_expressionTooDeep(false) , m_isBuiltinFunction(false) { 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, 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(vm, codeBlock) , m_activationRegister(0) , m_emptyValueRegister(0) , m_globalObjectRegister(0) , m_finallyDepth(0) , m_localScopeDepth(0) , m_codeType(FunctionCode) , m_nextConstantOffset(0) , m_globalConstantIndex(0) , m_firstLazyFunction(0) , m_lastLazyFunction(0) , m_staticPropertyAnalyzer(&m_instructions) , m_vm(&vm) , m_lastOpcodeID(op_end) #ifndef NDEBUG , m_lastOpcodePosition(0) #endif , m_usesExceptions(false) , m_expressionTooDeep(false) , m_isBuiltinFunction(codeBlock->isBuiltinFunction()) { 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); emitOpcode(op_enter); if (m_codeBlock->needsFullScopeChain() || m_shouldEmitDebugHooks) { m_activationRegister = addVar(); emitInitLazyRegister(m_activationRegister); m_codeBlock->setActivationRegister(m_activationRegister->virtualRegister()); } m_symbolTable->setCaptureStart(virtualRegisterForLocal(m_codeBlock->m_numVars).offset()); if (functionBody->usesArguments() || codeBlock->usesEval()) { // May reify arguments object. RegisterID* unmodifiedArgumentsRegister = addVar(); // Anonymous, so it can't be modified by user code. RegisterID* argumentsRegister = addVar(propertyNames().arguments, IsVariable, NotWatchable); // Can be changed by assigning to 'arguments'. // We can save a little space by hard-coding the knowledge that the two // 'arguments' values are stored in consecutive registers, and storing // only the index of the assignable one. codeBlock->setArgumentsRegister(argumentsRegister->virtualRegister()); ASSERT_UNUSED(unmodifiedArgumentsRegister, unmodifiedArgumentsRegister->virtualRegister() == JSC::unmodifiedArgumentsRegister(codeBlock->argumentsRegister())); emitInitLazyRegister(argumentsRegister); emitInitLazyRegister(unmodifiedArgumentsRegister); if (shouldTearOffArgumentsEagerly()) { emitOpcode(op_create_arguments); instructions().append(argumentsRegister->index()); } } 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. 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)) { m_functions.add(ident.impl()); emitNewFunction(addVar(ident, IsVariable, IsWatchable), IsCaptured, 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) ? IsConstant : IsVariable, IsWatchable); } } 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, IsVariable, NotWatchable); // Don't lazily create functions that override the name 'arguments' // as this would complicate lazy instantiation of actual arguments. if (!canLazilyCreateFunctions || ident == propertyNames().arguments) emitNewFunction(reg.get(), NotCaptured, function); else { emitInitLazyRegister(reg.get()); m_lazyFunctions.set(reg->virtualRegister().toLocal(), function); } } } 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; if (!functionBody->captures(ident)) addVar(ident, (varStack[i].second & DeclarationStacks::IsConstant) ? IsConstant : IsVariable, NotWatchable); } if (shouldCaptureAllTheThings) m_symbolTable->setCaptureEnd(virtualRegisterForLocal(codeBlock->m_numVars).offset()); if (m_symbolTable->captureCount()) emitOpcode(op_touch_entry); 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; 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(); // 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 (functionBody->usesThis() || codeBlock->usesEval()) { m_codeBlock->addPropertyAccessInstruction(instructions().size()); emitOpcode(op_to_this); instructions().append(kill(&m_thisRegister)); instructions().append(0); } } 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(vm, codeBlock) , m_thisRegister(CallFrame::thisArgumentOffset()) , m_activationRegister(0) , m_emptyValueRegister(0) , m_globalObjectRegister(0) , m_finallyDepth(0) , m_localScopeDepth(0) , m_codeType(EvalCode) , m_nextConstantOffset(0) , m_globalConstantIndex(0) , m_firstLazyFunction(0) , m_lastLazyFunction(0) , m_staticPropertyAnalyzer(&m_instructions) , m_vm(&vm) , m_lastOpcodeID(op_end) #ifndef NDEBUG , m_lastOpcodePosition(0) #endif , m_usesExceptions(false) , m_expressionTooDeep(false) , m_isBuiltinFunction(false) { 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) { ASSERT(varStack[i].first.impl()->isAtomic()); 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()); 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. 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 (!reg->virtualRegister().isLocal()) return reg; int localVariableNumber = reg->virtualRegister().toLocal(); if (m_lastLazyFunction <= localVariableNumber || localVariableNumber < m_firstLazyFunction) return reg; emitLazyNewFunction(reg, m_lazyFunctions.get(localVariableNumber)); return reg; } RegisterID* BytecodeGenerator::newRegister() { 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(); } 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