diff options
Diffstat (limited to 'Source/JavaScriptCore/bytecompiler')
-rw-r--r-- | Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp | 2371 | ||||
-rw-r--r-- | Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h | 588 | ||||
-rw-r--r-- | Source/JavaScriptCore/bytecompiler/Label.h | 90 | ||||
-rw-r--r-- | Source/JavaScriptCore/bytecompiler/LabelScope.h | 79 | ||||
-rw-r--r-- | Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp | 2076 | ||||
-rw-r--r-- | Source/JavaScriptCore/bytecompiler/RegisterID.h | 121 |
6 files changed, 5325 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp new file mode 100644 index 000000000..dec5c826e --- /dev/null +++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp @@ -0,0 +1,2371 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca> + * + * 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 "JSFunction.h" +#include "Interpreter.h" +#include "ScopeChain.h" +#include "StrongInlines.h" +#include "UString.h" + +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) +{ +#ifndef NDEBUG + s_dumpsGeneratedCode = dumpsGeneratedCode; +#else + UNUSED_PARAM(dumpsGeneratedCode); +#endif +} + +bool BytecodeGenerator::dumpsGeneratedCode() +{ +#ifndef NDEBUG + return s_dumpsGeneratedCode; +#else + return false; +#endif +} + +JSObject* BytecodeGenerator::generate() +{ + SamplingRegion samplingRegion("Bytecode Generation"); + + m_codeBlock->setThisRegister(m_thisRegister.index()); + + m_scopeNode->emitBytecode(*this); + + m_codeBlock->setInstructionCount(m_codeBlock->instructions().size()); + +#ifndef NDEBUG + if (s_dumpsGeneratedCode) + m_codeBlock->dump(m_scopeChain->globalObject->globalExec()); +#endif + + if ((m_codeType == FunctionCode && !m_codeBlock->needsFullScopeChain() && !m_codeBlock->usesArguments()) || m_codeType == EvalCode) + symbolTable().clear(); + + m_codeBlock->shrinkToFit(); + + if (m_expressionTooDeep) + return createOutOfMemoryError(m_scopeChain->globalObject.get()); + return 0; +} + +bool BytecodeGenerator::addVar(const Identifier& ident, bool isConstant, RegisterID*& r0) +{ + int index = m_calleeRegisters.size(); + SymbolTableEntry newEntry(index, isConstant ? ReadOnly : 0); + pair<SymbolTable::iterator, bool> result = symbolTable().add(ident.impl(), newEntry); + + if (!result.second) { + r0 = ®isterFor(result.first->second.getIndex()); + return false; + } + + r0 = addVar(); + return true; +} + +int BytecodeGenerator::addGlobalVar(const Identifier& ident, bool isConstant) +{ + int index = symbolTable().size(); + SymbolTableEntry newEntry(index, isConstant ? ReadOnly : 0); + pair<SymbolTable::iterator, bool> result = symbolTable().add(ident.impl(), newEntry); + if (!result.second) + index = result.first->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) + , m_scopeNode(programNode) + , m_codeBlock(codeBlock) + , m_thisRegister(CallFrame::thisArgumentOffset()) + , m_finallyDepth(0) + , m_dynamicScopeDepth(0) + , m_baseScopeDepth(0) + , m_codeType(GlobalCode) + , m_nextConstantOffset(0) + , m_globalConstantIndex(0) + , m_hasCreatedActivation(true) + , m_firstLazyFunction(0) + , m_lastLazyFunction(0) + , m_globalData(scopeChain->globalData) + , m_lastOpcodeID(op_end) +#ifndef NDEBUG + , m_lastOpcodePosition(0) +#endif + , m_stack(m_globalData->stack()) + , m_usesExceptions(false) + , m_expressionTooDeep(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->m_numParameters = 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); + + 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); + } + + 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); + } +} + +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) + , m_scopeNode(functionBody) + , m_codeBlock(codeBlock) + , m_activationRegister(0) + , m_finallyDepth(0) + , m_dynamicScopeDepth(0) + , m_baseScopeDepth(0) + , m_codeType(FunctionCode) + , m_nextConstantOffset(0) + , m_globalConstantIndex(0) + , m_hasCreatedActivation(false) + , m_firstLazyFunction(0) + , m_lastLazyFunction(0) + , m_globalData(scopeChain->globalData) + , m_lastOpcodeID(op_end) +#ifndef NDEBUG + , m_lastOpcodePosition(0) +#endif + , m_stack(m_globalData->stack()) + , m_usesExceptions(false) + , m_expressionTooDeep(false) +{ + m_globalData->startedCompiling(m_codeBlock); + if (m_shouldEmitDebugHooks) + m_codeBlock->setNeedsFullScopeChain(true); + + codeBlock->setGlobalData(m_globalData); + + emitOpcode(op_enter); + if (m_codeBlock->needsFullScopeChain()) { + m_activationRegister = addVar(); + emitInitLazyRegister(m_activationRegister); + m_codeBlock->setActivationRegister(m_activationRegister->index()); + } + + // Both op_tear_off_activation and op_tear_off_arguments tear off the 'arguments' + // object, if created. + if (m_codeBlock->needsFullScopeChain() || functionBody->usesArguments()) { + 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 (<rdar://problem/6911886>), + // but for now we force eager creation of the arguments object when debugging. + if (m_shouldEmitDebugHooks) { + emitOpcode(op_create_arguments); + instructions().append(argumentsRegister->index()); + } + } + + 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()); + } + + codeBlock->m_numCapturedVars = 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<RegisterID> 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 (m_shouldEmitDebugHooks) + codeBlock->m_numCapturedVars = 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->m_numParameters; + + for (size_t i = 0; i < parameters.size(); ++i) + addParameter(parameters[i], nextParameterIndex--); + + preserveLastVar(); + + if (isConstructor()) { + RefPtr<RegisterID> func = newTemporary(); + RefPtr<RegisterID> funcProto = newTemporary(); + + emitOpcode(op_get_callee); + instructions().append(func->index()); + // Load prototype. + emitGetById(funcProto.get(), func.get(), globalData()->propertyNames->prototype); + + 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()); + } +} + +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) + , m_scopeNode(evalNode) + , m_codeBlock(codeBlock) + , m_thisRegister(CallFrame::thisArgumentOffset()) + , m_finallyDepth(0) + , m_dynamicScopeDepth(0) + , m_baseScopeDepth(codeBlock->baseScopeDepth()) + , m_codeType(EvalCode) + , m_nextConstantOffset(0) + , m_globalConstantIndex(0) + , m_hasCreatedActivation(true) + , m_firstLazyFunction(0) + , m_lastLazyFunction(0) + , m_globalData(scopeChain->globalData) + , m_lastOpcodeID(op_end) +#ifndef NDEBUG + , m_lastOpcodePosition(0) +#endif + , m_stack(m_globalData->stack()) + , m_usesExceptions(false) + , m_expressionTooDeep(false) +{ + m_globalData->startedCompiling(m_codeBlock); + if (m_shouldEmitDebugHooks || m_baseScopeDepth) + m_codeBlock->setNeedsFullScopeChain(true); + + 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])); + + const DeclarationStacks::VarStack& varStack = evalNode->varStack(); + unsigned numVariables = varStack.size(); + Vector<Identifier> variables; + variables.reserveCapacity(numVariables); + for (size_t i = 0; i < numVariables; ++i) + 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()); + return reg; +} + +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->m_numParameters; +} + +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) + 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; +} + +bool BytecodeGenerator::isLocal(const Identifier& ident) +{ + if (ident == propertyNames().thisIdentifier) + return true; + + return shouldOptimizeLocals() && symbolTable().contains(ident.impl()); +} + +bool BytecodeGenerator::isLocalConstant(const Identifier& ident) +{ + return symbolTable().get(ident.impl()).isReadOnly(); +} + +RegisterID* BytecodeGenerator::newRegister() +{ + m_calleeRegisters.append(m_calleeRegisters.size()); + m_codeBlock->m_numCalleeRegisters = max<int>(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; +} + +RegisterID* BytecodeGenerator::highestUsedRegister() +{ + size_t count = m_codeBlock->m_numCalleeRegisters; + while (m_calleeRegisters.size() < count) + newRegister(); + return &m_calleeRegisters.last(); +} + +PassRefPtr<LabelScope> 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<Label>()); // Only loops have continue targets. + m_labelScopes.append(scope); + return &m_labelScopes.last(); +} + +PassRefPtr<Label> BytecodeGenerator::newLabel() +{ + // Reclaim free label IDs. + while (m_labels.size() && !m_labels.last().refCount()) + m_labels.removeLast(); + + // Allocate new label ID. + m_labels.append(m_codeBlock); + return &m_labels.last(); +} + +PassRefPtr<Label> BytecodeGenerator::emitLabel(Label* l0) +{ + unsigned newLabelIndex = instructions().size(); + l0->setLocation(newLabelIndex); + + if (m_codeBlock->numberOfJumpTargets()) { + unsigned lastLabelIndex = m_codeBlock->lastJumpTarget(); + ASSERT(lastLabelIndex <= newLabelIndex); + if (newLabelIndex == lastLabelIndex) { + // Peephole optimizations have already been disabled by emitting the last label + return l0; + } + } + + m_codeBlock->addJumpTarget(newLabelIndex); + + // This disables peephole optimizations when an instruction is a jump target + m_lastOpcodeID = op_end; + return l0; +} + +void BytecodeGenerator::emitOpcode(OpcodeID opcodeID) +{ +#ifndef NDEBUG + size_t opcodePosition = instructions().size(); + ASSERT(opcodePosition - m_lastOpcodePosition == opcodeLength(m_lastOpcodeID) || m_lastOpcodeID == op_end); + m_lastOpcodePosition = opcodePosition; +#endif + instructions().append(globalData()->interpreter->getOpcode(opcodeID)); + m_lastOpcodeID = opcodeID; +} + +void BytecodeGenerator::emitLoopHint() +{ +#if ENABLE(DFG_JIT) + emitOpcode(op_loop_hint); +#endif +} + +void BytecodeGenerator::retrieveLastBinaryOp(int& dstIndex, int& src1Index, int& src2Index) +{ + ASSERT(instructions().size() >= 4); + size_t size = instructions().size(); + dstIndex = instructions().at(size - 3).u.operand; + src1Index = instructions().at(size - 2).u.operand; + src2Index = instructions().at(size - 1).u.operand; +} + +void BytecodeGenerator::retrieveLastUnaryOp(int& dstIndex, int& srcIndex) +{ + ASSERT(instructions().size() >= 3); + size_t size = instructions().size(); + dstIndex = instructions().at(size - 2).u.operand; + srcIndex = instructions().at(size - 1).u.operand; +} + +void ALWAYS_INLINE BytecodeGenerator::rewindBinaryOp() +{ + ASSERT(instructions().size() >= 4); + instructions().shrink(instructions().size() - 4); + m_lastOpcodeID = op_end; +} + +void ALWAYS_INLINE BytecodeGenerator::rewindUnaryOp() +{ + ASSERT(instructions().size() >= 3); + instructions().shrink(instructions().size() - 3); + m_lastOpcodeID = op_end; +} + +PassRefPtr<Label> BytecodeGenerator::emitJump(Label* target) +{ + size_t begin = instructions().size(); + emitOpcode(target->isForward() ? op_jmp : op_loop); + instructions().append(target->bind(begin, instructions().size())); + return target; +} + +PassRefPtr<Label> BytecodeGenerator::emitJumpIfTrue(RegisterID* cond, Label* target) +{ + if (m_lastOpcodeID == op_less) { + int dstIndex; + int src1Index; + int src2Index; + + retrieveLastBinaryOp(dstIndex, src1Index, src2Index); + + if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) { + rewindBinaryOp(); + + size_t begin = instructions().size(); + emitOpcode(target->isForward() ? op_jless : op_loop_if_less); + instructions().append(src1Index); + instructions().append(src2Index); + instructions().append(target->bind(begin, instructions().size())); + return target; + } + } else if (m_lastOpcodeID == op_lesseq) { + int dstIndex; + int src1Index; + int src2Index; + + retrieveLastBinaryOp(dstIndex, src1Index, src2Index); + + if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) { + rewindBinaryOp(); + + size_t begin = instructions().size(); + emitOpcode(target->isForward() ? op_jlesseq : op_loop_if_lesseq); + instructions().append(src1Index); + instructions().append(src2Index); + instructions().append(target->bind(begin, instructions().size())); + return target; + } + } else if (m_lastOpcodeID == op_greater) { + int dstIndex; + int src1Index; + int src2Index; + + retrieveLastBinaryOp(dstIndex, src1Index, src2Index); + + if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) { + rewindBinaryOp(); + + size_t begin = instructions().size(); + emitOpcode(target->isForward() ? op_jgreater : op_loop_if_greater); + instructions().append(src1Index); + instructions().append(src2Index); + instructions().append(target->bind(begin, instructions().size())); + return target; + } + } else if (m_lastOpcodeID == op_greatereq) { + int dstIndex; + int src1Index; + int src2Index; + + retrieveLastBinaryOp(dstIndex, src1Index, src2Index); + + if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) { + rewindBinaryOp(); + + size_t begin = instructions().size(); + emitOpcode(target->isForward() ? op_jgreatereq : op_loop_if_greatereq); + instructions().append(src1Index); + instructions().append(src2Index); + instructions().append(target->bind(begin, instructions().size())); + return target; + } + } else if (m_lastOpcodeID == op_eq_null && target->isForward()) { + int dstIndex; + int srcIndex; + + retrieveLastUnaryOp(dstIndex, srcIndex); + + if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) { + rewindUnaryOp(); + + size_t begin = instructions().size(); + emitOpcode(op_jeq_null); + instructions().append(srcIndex); + instructions().append(target->bind(begin, instructions().size())); + return target; + } + } else if (m_lastOpcodeID == op_neq_null && target->isForward()) { + int dstIndex; + int srcIndex; + + retrieveLastUnaryOp(dstIndex, srcIndex); + + if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) { + rewindUnaryOp(); + + size_t begin = instructions().size(); + emitOpcode(op_jneq_null); + instructions().append(srcIndex); + instructions().append(target->bind(begin, instructions().size())); + return target; + } + } + + size_t begin = instructions().size(); + + emitOpcode(target->isForward() ? op_jtrue : op_loop_if_true); + instructions().append(cond->index()); + instructions().append(target->bind(begin, instructions().size())); + return target; +} + +PassRefPtr<Label> BytecodeGenerator::emitJumpIfFalse(RegisterID* cond, Label* target) +{ + if (m_lastOpcodeID == op_less && target->isForward()) { + int dstIndex; + int src1Index; + int src2Index; + + retrieveLastBinaryOp(dstIndex, src1Index, src2Index); + + if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) { + rewindBinaryOp(); + + size_t begin = instructions().size(); + emitOpcode(op_jnless); + instructions().append(src1Index); + instructions().append(src2Index); + instructions().append(target->bind(begin, instructions().size())); + return target; + } + } else if (m_lastOpcodeID == op_lesseq && target->isForward()) { + int dstIndex; + int src1Index; + int src2Index; + + retrieveLastBinaryOp(dstIndex, src1Index, src2Index); + + if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) { + rewindBinaryOp(); + + size_t begin = instructions().size(); + emitOpcode(op_jnlesseq); + instructions().append(src1Index); + instructions().append(src2Index); + instructions().append(target->bind(begin, instructions().size())); + return target; + } + } else if (m_lastOpcodeID == op_greater && target->isForward()) { + int dstIndex; + int src1Index; + int src2Index; + + retrieveLastBinaryOp(dstIndex, src1Index, src2Index); + + if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) { + rewindBinaryOp(); + + size_t begin = instructions().size(); + emitOpcode(op_jngreater); + instructions().append(src1Index); + instructions().append(src2Index); + instructions().append(target->bind(begin, instructions().size())); + return target; + } + } else if (m_lastOpcodeID == op_greatereq && target->isForward()) { + int dstIndex; + int src1Index; + int src2Index; + + retrieveLastBinaryOp(dstIndex, src1Index, src2Index); + + if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) { + rewindBinaryOp(); + + size_t begin = instructions().size(); + emitOpcode(op_jngreatereq); + instructions().append(src1Index); + instructions().append(src2Index); + instructions().append(target->bind(begin, instructions().size())); + return target; + } + } else if (m_lastOpcodeID == op_not) { + int dstIndex; + int srcIndex; + + retrieveLastUnaryOp(dstIndex, srcIndex); + + if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) { + rewindUnaryOp(); + + size_t begin = instructions().size(); + emitOpcode(target->isForward() ? op_jtrue : op_loop_if_true); + instructions().append(srcIndex); + instructions().append(target->bind(begin, instructions().size())); + return target; + } + } else if (m_lastOpcodeID == op_eq_null && target->isForward()) { + int dstIndex; + int srcIndex; + + retrieveLastUnaryOp(dstIndex, srcIndex); + + if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) { + rewindUnaryOp(); + + size_t begin = instructions().size(); + emitOpcode(op_jneq_null); + instructions().append(srcIndex); + instructions().append(target->bind(begin, instructions().size())); + return target; + } + } else if (m_lastOpcodeID == op_neq_null && target->isForward()) { + int dstIndex; + int srcIndex; + + retrieveLastUnaryOp(dstIndex, srcIndex); + + if (cond->index() == dstIndex && cond->isTemporary() && !cond->refCount()) { + rewindUnaryOp(); + + size_t begin = instructions().size(); + emitOpcode(op_jeq_null); + instructions().append(srcIndex); + instructions().append(target->bind(begin, instructions().size())); + return target; + } + } + + size_t begin = instructions().size(); + emitOpcode(target->isForward() ? op_jfalse : op_loop_if_false); + instructions().append(cond->index()); + instructions().append(target->bind(begin, instructions().size())); + return target; +} + +PassRefPtr<Label> BytecodeGenerator::emitJumpIfNotFunctionCall(RegisterID* cond, Label* target) +{ + size_t begin = instructions().size(); + + emitOpcode(op_jneq_ptr); + instructions().append(cond->index()); + instructions().append(Instruction(*m_globalData, m_codeBlock->ownerExecutable(), m_scopeChain->globalObject->callFunction())); + instructions().append(target->bind(begin, instructions().size())); + return target; +} + +PassRefPtr<Label> BytecodeGenerator::emitJumpIfNotFunctionApply(RegisterID* cond, Label* target) +{ + size_t begin = instructions().size(); + + emitOpcode(op_jneq_ptr); + instructions().append(cond->index()); + instructions().append(Instruction(*m_globalData, m_codeBlock->ownerExecutable(), m_scopeChain->globalObject->applyFunction())); + instructions().append(target->bind(begin, instructions().size())); + return target; +} + +unsigned BytecodeGenerator::addConstant(const Identifier& ident) +{ + StringImpl* rep = ident.impl(); + pair<IdentifierMap::iterator, bool> result = m_identifierMap.add(rep, m_codeBlock->numberOfIdentifiers()); + if (result.second) // new entry + m_codeBlock->addIdentifier(Identifier(m_globalData, rep)); + + return result.first->second; +} + +RegisterID* BytecodeGenerator::addConstantValue(JSValue v) +{ + int index = m_nextConstantOffset; + + pair<JSValueMap::iterator, bool> result = m_jsValueMap.add(JSValue::encode(v), m_nextConstantOffset); + if (result.second) { + m_constantPoolRegisters.append(FirstConstantRegisterIndex + m_nextConstantOffset); + ++m_nextConstantOffset; + m_codeBlock->addConstant(JSValue(v)); + } else + index = result.first->second; + + return &m_constantPoolRegisters[index]; +} + +unsigned BytecodeGenerator::addRegExp(RegExp* r) +{ + return m_codeBlock->addRegExp(r); +} + +RegisterID* BytecodeGenerator::emitMove(RegisterID* dst, RegisterID* src) +{ + emitOpcode(op_mov); + instructions().append(dst->index()); + instructions().append(src->index()); + return dst; +} + +RegisterID* BytecodeGenerator::emitUnaryOp(OpcodeID opcodeID, RegisterID* dst, RegisterID* src) +{ + emitOpcode(opcodeID); + instructions().append(dst->index()); + instructions().append(src->index()); + return dst; +} + +RegisterID* BytecodeGenerator::emitPreInc(RegisterID* srcDst) +{ + emitOpcode(op_pre_inc); + instructions().append(srcDst->index()); + return srcDst; +} + +RegisterID* BytecodeGenerator::emitPreDec(RegisterID* srcDst) +{ + emitOpcode(op_pre_dec); + instructions().append(srcDst->index()); + return srcDst; +} + +RegisterID* BytecodeGenerator::emitPostInc(RegisterID* dst, RegisterID* srcDst) +{ + emitOpcode(op_post_inc); + instructions().append(dst->index()); + instructions().append(srcDst->index()); + return dst; +} + +RegisterID* BytecodeGenerator::emitPostDec(RegisterID* dst, RegisterID* srcDst) +{ + emitOpcode(op_post_dec); + instructions().append(dst->index()); + instructions().append(srcDst->index()); + return dst; +} + +RegisterID* BytecodeGenerator::emitBinaryOp(OpcodeID opcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2, OperandTypes types) +{ + emitOpcode(opcodeID); + instructions().append(dst->index()); + instructions().append(src1->index()); + instructions().append(src2->index()); + + if (opcodeID == op_bitor || opcodeID == op_bitand || opcodeID == op_bitxor || + opcodeID == op_add || opcodeID == op_mul || opcodeID == op_sub || opcodeID == op_div) + instructions().append(types.toInt()); + + return dst; +} + +RegisterID* BytecodeGenerator::emitEqualityOp(OpcodeID opcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2) +{ + if (m_lastOpcodeID == op_typeof) { + int dstIndex; + int srcIndex; + + retrieveLastUnaryOp(dstIndex, srcIndex); + + if (src1->index() == dstIndex + && src1->isTemporary() + && m_codeBlock->isConstantRegisterIndex(src2->index()) + && m_codeBlock->constantRegister(src2->index()).get().isString()) { + const UString& value = asString(m_codeBlock->constantRegister(src2->index()).get())->tryGetValue(); + if (value == "undefined") { + rewindUnaryOp(); + emitOpcode(op_is_undefined); + instructions().append(dst->index()); + instructions().append(srcIndex); + return dst; + } + if (value == "boolean") { + rewindUnaryOp(); + emitOpcode(op_is_boolean); + instructions().append(dst->index()); + instructions().append(srcIndex); + return dst; + } + if (value == "number") { + rewindUnaryOp(); + emitOpcode(op_is_number); + instructions().append(dst->index()); + instructions().append(srcIndex); + return dst; + } + if (value == "string") { + rewindUnaryOp(); + emitOpcode(op_is_string); + instructions().append(dst->index()); + instructions().append(srcIndex); + return dst; + } + if (value == "object") { + rewindUnaryOp(); + emitOpcode(op_is_object); + instructions().append(dst->index()); + instructions().append(srcIndex); + return dst; + } + if (value == "function") { + rewindUnaryOp(); + emitOpcode(op_is_function); + instructions().append(dst->index()); + instructions().append(srcIndex); + return dst; + } + } + } + + emitOpcode(opcodeID); + instructions().append(dst->index()); + instructions().append(src1->index()); + instructions().append(src2->index()); + return dst; +} + +RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, bool b) +{ + return emitLoad(dst, jsBoolean(b)); +} + +RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, double number) +{ + // FIXME: Our hash tables won't hold infinity, so we make a new JSValue each time. + // Later we can do the extra work to handle that like the other cases. They also don't + // work correctly with NaN as a key. + if (isnan(number) || number == HashTraits<double>::emptyValue() || HashTraits<double>::isDeletedValue(number)) + return emitLoad(dst, jsNumber(number)); + JSValue& valueInMap = m_numberMap.add(number, JSValue()).first->second; + if (!valueInMap) + valueInMap = jsNumber(number); + return emitLoad(dst, valueInMap); +} + +RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, const Identifier& identifier) +{ + JSString*& stringInMap = m_stringMap.add(identifier.impl(), 0).first->second; + if (!stringInMap) + stringInMap = jsOwnedString(globalData(), identifier.ustring()); + return emitLoad(dst, JSValue(stringInMap)); +} + +RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, JSValue v) +{ + RegisterID* constantID = addConstantValue(v); + if (dst) + return emitMove(dst, constantID); + return constantID; +} + +bool BytecodeGenerator::findScopedProperty(const Identifier& property, int& index, size_t& stackDepth, bool forWriting, bool& requiresDynamicChecks, JSObject*& globalObject) +{ + // Cases where we cannot statically optimize the lookup. + if (property == propertyNames().arguments || !canOptimizeNonLocals()) { + stackDepth = 0; + index = missingSymbolMarker(); + + if (shouldOptimizeLocals() && m_codeType == GlobalCode) { + ScopeChainIterator iter = m_scopeChain->begin(); + globalObject = iter->get(); + ASSERT((++iter) == m_scopeChain->end()); + } + return false; + } + + size_t depth = 0; + requiresDynamicChecks = false; + ScopeChainIterator iter = m_scopeChain->begin(); + ScopeChainIterator end = m_scopeChain->end(); + for (; iter != end; ++iter, ++depth) { + JSObject* currentScope = iter->get(); + if (!currentScope->isVariableObject()) + break; + JSVariableObject* currentVariableObject = static_cast<JSVariableObject*>(currentScope); + SymbolTableEntry entry = currentVariableObject->symbolTable().get(property.impl()); + + // Found the property + if (!entry.isNull()) { + if (entry.isReadOnly() && forWriting) { + stackDepth = 0; + index = missingSymbolMarker(); + if (++iter == end) + globalObject = currentVariableObject; + return false; + } + stackDepth = depth + m_codeBlock->needsFullScopeChain(); + index = entry.getIndex(); + if (++iter == end) + globalObject = currentVariableObject; + return true; + } + bool scopeRequiresDynamicChecks = false; + if (currentVariableObject->isDynamicScope(scopeRequiresDynamicChecks)) + break; + requiresDynamicChecks |= scopeRequiresDynamicChecks; + } + // Can't locate the property but we're able to avoid a few lookups. + stackDepth = depth + m_codeBlock->needsFullScopeChain(); + index = missingSymbolMarker(); + JSObject* scope = iter->get(); + if (++iter == end) + globalObject = scope; + return true; +} + +void BytecodeGenerator::emitCheckHasInstance(RegisterID* base) +{ + emitOpcode(op_check_has_instance); + instructions().append(base->index()); +} + +RegisterID* BytecodeGenerator::emitInstanceOf(RegisterID* dst, RegisterID* value, RegisterID* base, RegisterID* basePrototype) +{ + emitOpcode(op_instanceof); + instructions().append(dst->index()); + instructions().append(value->index()); + instructions().append(base->index()); + instructions().append(basePrototype->index()); + return dst; +} + +static const unsigned maxGlobalResolves = 128; + +bool BytecodeGenerator::shouldAvoidResolveGlobal() +{ + return m_codeBlock->globalResolveInfoCount() > maxGlobalResolves && !m_labelScopes.size(); +} + +RegisterID* BytecodeGenerator::emitResolve(RegisterID* dst, const Identifier& property) +{ + size_t depth = 0; + int index = 0; + JSObject* globalObject = 0; + bool requiresDynamicChecks = false; + if (!findScopedProperty(property, index, depth, false, requiresDynamicChecks, globalObject) && !globalObject) { + // We can't optimise at all :-( + emitOpcode(op_resolve); + instructions().append(dst->index()); + instructions().append(addConstant(property)); + return dst; + } + if (shouldAvoidResolveGlobal()) { + globalObject = 0; + requiresDynamicChecks = true; + } + + if (globalObject) { + if (index != missingSymbolMarker() && !requiresDynamicChecks) { + // Directly index the property lookup across multiple scopes. + return emitGetScopedVar(dst, depth, index, globalObject); + } + +#if ENABLE(JIT) + m_codeBlock->addGlobalResolveInfo(instructions().size()); +#endif +#if ENABLE(INTERPRETER) + m_codeBlock->addGlobalResolveInstruction(instructions().size()); +#endif + emitOpcode(requiresDynamicChecks ? op_resolve_global_dynamic : op_resolve_global); + instructions().append(dst->index()); + instructions().append(addConstant(property)); + instructions().append(0); + instructions().append(0); + if (requiresDynamicChecks) + instructions().append(depth); + return dst; + } + + if (requiresDynamicChecks) { + // If we get here we have eval nested inside a |with| just give up + emitOpcode(op_resolve); + instructions().append(dst->index()); + instructions().append(addConstant(property)); + return dst; + } + + if (index != missingSymbolMarker()) { + // Directly index the property lookup across multiple scopes. + return emitGetScopedVar(dst, depth, index, globalObject); + } + + // In this case we are at least able to drop a few scope chains from the + // lookup chain, although we still need to hash from then on. + emitOpcode(op_resolve_skip); + instructions().append(dst->index()); + instructions().append(addConstant(property)); + instructions().append(depth); + return dst; +} + +RegisterID* BytecodeGenerator::emitGetScopedVar(RegisterID* dst, size_t depth, int index, JSValue globalObject) +{ + if (globalObject) { + if (m_lastOpcodeID == op_put_global_var) { + int dstIndex; + int srcIndex; + retrieveLastUnaryOp(dstIndex, srcIndex); + + if (dstIndex == index && srcIndex == dst->index()) + return dst; + } + + emitOpcode(op_get_global_var); + instructions().append(dst->index()); + instructions().append(index); + return dst; + } + + emitOpcode(op_get_scoped_var); + instructions().append(dst->index()); + instructions().append(index); + instructions().append(depth); + return dst; +} + +RegisterID* BytecodeGenerator::emitPutScopedVar(size_t depth, int index, RegisterID* value, JSValue globalObject) +{ + if (globalObject) { + emitOpcode(op_put_global_var); + instructions().append(index); + instructions().append(value->index()); + return value; + } + emitOpcode(op_put_scoped_var); + instructions().append(index); + instructions().append(depth); + instructions().append(value->index()); + return value; +} + +RegisterID* BytecodeGenerator::emitResolveBase(RegisterID* dst, const Identifier& property) +{ + size_t depth = 0; + int index = 0; + JSObject* globalObject = 0; + bool requiresDynamicChecks = false; + findScopedProperty(property, index, depth, false, requiresDynamicChecks, globalObject); + if (!globalObject || requiresDynamicChecks) { + // We can't optimise at all :-( + emitOpcode(op_resolve_base); + instructions().append(dst->index()); + instructions().append(addConstant(property)); + instructions().append(false); + return dst; + } + + // Global object is the base + return emitLoad(dst, JSValue(globalObject)); +} + +RegisterID* BytecodeGenerator::emitResolveBaseForPut(RegisterID* dst, const Identifier& property) +{ + if (!m_codeBlock->isStrictMode()) + return emitResolveBase(dst, property); + size_t depth = 0; + int index = 0; + JSObject* globalObject = 0; + bool requiresDynamicChecks = false; + findScopedProperty(property, index, depth, false, requiresDynamicChecks, globalObject); + if (!globalObject || requiresDynamicChecks) { + // We can't optimise at all :-( + emitOpcode(op_resolve_base); + instructions().append(dst->index()); + instructions().append(addConstant(property)); + instructions().append(true); + return dst; + } + + // Global object is the base + RefPtr<RegisterID> result = emitLoad(dst, JSValue(globalObject)); + emitOpcode(op_ensure_property_exists); + instructions().append(dst->index()); + instructions().append(addConstant(property)); + return result.get(); +} + +RegisterID* BytecodeGenerator::emitResolveWithBase(RegisterID* baseDst, RegisterID* propDst, const Identifier& property) +{ + size_t depth = 0; + int index = 0; + JSObject* globalObject = 0; + bool requiresDynamicChecks = false; + if (!findScopedProperty(property, index, depth, false, requiresDynamicChecks, globalObject) || !globalObject || requiresDynamicChecks) { + // We can't optimise at all :-( + emitOpcode(op_resolve_with_base); + instructions().append(baseDst->index()); + instructions().append(propDst->index()); + instructions().append(addConstant(property)); + return baseDst; + } + + bool forceGlobalResolve = false; + + // Global object is the base + emitLoad(baseDst, JSValue(globalObject)); + + if (index != missingSymbolMarker() && !forceGlobalResolve) { + // Directly index the property lookup across multiple scopes. + emitGetScopedVar(propDst, depth, index, globalObject); + return baseDst; + } + if (shouldAvoidResolveGlobal()) { + emitOpcode(op_resolve); + instructions().append(propDst->index()); + instructions().append(addConstant(property)); + return baseDst; + } +#if ENABLE(JIT) + m_codeBlock->addGlobalResolveInfo(instructions().size()); +#endif +#if ENABLE(INTERPRETER) + m_codeBlock->addGlobalResolveInstruction(instructions().size()); +#endif + emitOpcode(requiresDynamicChecks ? op_resolve_global_dynamic : op_resolve_global); + instructions().append(propDst->index()); + instructions().append(addConstant(property)); + instructions().append(0); + instructions().append(0); + if (requiresDynamicChecks) + instructions().append(depth); + return baseDst; +} + +RegisterID* BytecodeGenerator::emitResolveWithThis(RegisterID* baseDst, RegisterID* propDst, const Identifier& property) +{ + size_t depth = 0; + int index = 0; + JSObject* globalObject = 0; + bool requiresDynamicChecks = false; + if (!findScopedProperty(property, index, depth, false, requiresDynamicChecks, globalObject) || !globalObject || requiresDynamicChecks) { + // We can't optimise at all :-( + emitOpcode(op_resolve_with_this); + instructions().append(baseDst->index()); + instructions().append(propDst->index()); + instructions().append(addConstant(property)); + return baseDst; + } + + bool forceGlobalResolve = false; + + // Global object is the base + emitLoad(baseDst, jsUndefined()); + + if (index != missingSymbolMarker() && !forceGlobalResolve) { + // Directly index the property lookup across multiple scopes. + emitGetScopedVar(propDst, depth, index, globalObject); + return baseDst; + } + if (shouldAvoidResolveGlobal()) { + emitOpcode(op_resolve); + instructions().append(propDst->index()); + instructions().append(addConstant(property)); + return baseDst; + } +#if ENABLE(JIT) + m_codeBlock->addGlobalResolveInfo(instructions().size()); +#endif +#if ENABLE(INTERPRETER) + m_codeBlock->addGlobalResolveInstruction(instructions().size()); +#endif + emitOpcode(requiresDynamicChecks ? op_resolve_global_dynamic : op_resolve_global); + instructions().append(propDst->index()); + instructions().append(addConstant(property)); + instructions().append(0); + instructions().append(0); + if (requiresDynamicChecks) + instructions().append(depth); + return baseDst; +} + +void BytecodeGenerator::emitMethodCheck() +{ + emitOpcode(op_method_check); +} + +RegisterID* BytecodeGenerator::emitGetById(RegisterID* dst, RegisterID* base, const Identifier& property) +{ +#if ENABLE(INTERPRETER) + m_codeBlock->addPropertyAccessInstruction(instructions().size()); +#endif + + emitOpcode(op_get_by_id); + instructions().append(dst->index()); + instructions().append(base->index()); + instructions().append(addConstant(property)); + instructions().append(0); + instructions().append(0); + instructions().append(0); + instructions().append(0); + return dst; +} + +RegisterID* BytecodeGenerator::emitGetArgumentsLength(RegisterID* dst, RegisterID* base) +{ + emitOpcode(op_get_arguments_length); + instructions().append(dst->index()); + ASSERT(base->index() == m_codeBlock->argumentsRegister()); + instructions().append(base->index()); + instructions().append(addConstant(propertyNames().length)); + return dst; +} + +RegisterID* BytecodeGenerator::emitPutById(RegisterID* base, const Identifier& property, RegisterID* value) +{ +#if ENABLE(INTERPRETER) + m_codeBlock->addPropertyAccessInstruction(instructions().size()); +#endif + + emitOpcode(op_put_by_id); + instructions().append(base->index()); + instructions().append(addConstant(property)); + instructions().append(value->index()); + instructions().append(0); + instructions().append(0); + instructions().append(0); + instructions().append(0); + instructions().append(0); + return value; +} + +RegisterID* BytecodeGenerator::emitDirectPutById(RegisterID* base, const Identifier& property, RegisterID* value) +{ +#if ENABLE(INTERPRETER) + m_codeBlock->addPropertyAccessInstruction(instructions().size()); +#endif + + emitOpcode(op_put_by_id); + instructions().append(base->index()); + instructions().append(addConstant(property)); + instructions().append(value->index()); + instructions().append(0); + instructions().append(0); + instructions().append(0); + instructions().append(0); + instructions().append(property != m_globalData->propertyNames->underscoreProto); + return value; +} + +RegisterID* BytecodeGenerator::emitPutGetter(RegisterID* base, const Identifier& property, RegisterID* value) +{ + emitOpcode(op_put_getter); + instructions().append(base->index()); + instructions().append(addConstant(property)); + instructions().append(value->index()); + return value; +} + +RegisterID* BytecodeGenerator::emitPutSetter(RegisterID* base, const Identifier& property, RegisterID* value) +{ + emitOpcode(op_put_setter); + instructions().append(base->index()); + instructions().append(addConstant(property)); + instructions().append(value->index()); + return value; +} + +RegisterID* BytecodeGenerator::emitDeleteById(RegisterID* dst, RegisterID* base, const Identifier& property) +{ + emitOpcode(op_del_by_id); + instructions().append(dst->index()); + instructions().append(base->index()); + instructions().append(addConstant(property)); + return dst; +} + +RegisterID* BytecodeGenerator::emitGetArgumentByVal(RegisterID* dst, RegisterID* base, RegisterID* property) +{ + emitOpcode(op_get_argument_by_val); + instructions().append(dst->index()); + ASSERT(base->index() == m_codeBlock->argumentsRegister()); + instructions().append(base->index()); + instructions().append(property->index()); + return dst; +} + +RegisterID* BytecodeGenerator::emitGetByVal(RegisterID* dst, RegisterID* base, RegisterID* property) +{ + for (size_t i = m_forInContextStack.size(); i > 0; i--) { + ForInContext& context = m_forInContextStack[i - 1]; + if (context.propertyRegister == property) { + emitOpcode(op_get_by_pname); + instructions().append(dst->index()); + instructions().append(base->index()); + instructions().append(property->index()); + instructions().append(context.expectedSubscriptRegister->index()); + instructions().append(context.iterRegister->index()); + instructions().append(context.indexRegister->index()); + return dst; + } + } + emitOpcode(op_get_by_val); + instructions().append(dst->index()); + instructions().append(base->index()); + instructions().append(property->index()); + return dst; +} + +RegisterID* BytecodeGenerator::emitPutByVal(RegisterID* base, RegisterID* property, RegisterID* value) +{ + emitOpcode(op_put_by_val); + instructions().append(base->index()); + instructions().append(property->index()); + instructions().append(value->index()); + return value; +} + +RegisterID* BytecodeGenerator::emitDeleteByVal(RegisterID* dst, RegisterID* base, RegisterID* property) +{ + emitOpcode(op_del_by_val); + instructions().append(dst->index()); + instructions().append(base->index()); + instructions().append(property->index()); + return dst; +} + +RegisterID* BytecodeGenerator::emitPutByIndex(RegisterID* base, unsigned index, RegisterID* value) +{ + emitOpcode(op_put_by_index); + instructions().append(base->index()); + instructions().append(index); + instructions().append(value->index()); + return value; +} + +RegisterID* BytecodeGenerator::emitNewObject(RegisterID* dst) +{ + emitOpcode(op_new_object); + instructions().append(dst->index()); + return dst; +} + +unsigned BytecodeGenerator::addConstantBuffer(unsigned length) +{ + return m_codeBlock->addConstantBuffer(length); +} + +JSString* BytecodeGenerator::addStringConstant(const Identifier& identifier) +{ + JSString*& stringInMap = m_stringMap.add(identifier.impl(), 0).first->second; + if (!stringInMap) { + stringInMap = jsString(globalData(), identifier.ustring()); + addConstantValue(stringInMap); + } + return stringInMap; +} + +RegisterID* BytecodeGenerator::emitNewArray(RegisterID* dst, ElementNode* elements, unsigned length) +{ +#if !ASSERT_DISABLED + unsigned checkLength = 0; +#endif + bool hadVariableExpression = false; + if (length) { + for (ElementNode* n = elements; n; n = n->next()) { + if (!n->value()->isNumber() && !n->value()->isString()) { + hadVariableExpression = true; + break; + } + if (n->elision()) + break; +#if !ASSERT_DISABLED + checkLength++; +#endif + } + if (!hadVariableExpression) { + ASSERT(length == checkLength); + unsigned constantBufferIndex = addConstantBuffer(length); + JSValue* constantBuffer = m_codeBlock->constantBuffer(constantBufferIndex); + unsigned index = 0; + for (ElementNode* n = elements; index < length; n = n->next()) { + if (n->value()->isNumber()) + constantBuffer[index++] = jsNumber(static_cast<NumberNode*>(n->value())->value()); + else { + ASSERT(n->value()->isString()); + constantBuffer[index++] = addStringConstant(static_cast<StringNode*>(n->value())->value()); + } + } + emitOpcode(op_new_array_buffer); + instructions().append(dst->index()); + instructions().append(constantBufferIndex); + instructions().append(length); + return dst; + } + } + + Vector<RefPtr<RegisterID>, 16> argv; + for (ElementNode* n = elements; n; n = n->next()) { + if (n->elision()) + break; + argv.append(newTemporary()); + // op_new_array requires the initial values to be a sequential range of registers + ASSERT(argv.size() == 1 || argv[argv.size() - 1]->index() == argv[argv.size() - 2]->index() + 1); + emitNode(argv.last().get(), n->value()); + } + emitOpcode(op_new_array); + instructions().append(dst->index()); + instructions().append(argv.size() ? argv[0]->index() : 0); // argv + instructions().append(argv.size()); // argc + return dst; +} + +RegisterID* BytecodeGenerator::emitNewFunction(RegisterID* dst, FunctionBodyNode* function) +{ + return emitNewFunctionInternal(dst, m_codeBlock->addFunctionDecl(makeFunction(m_globalData, function)), false); +} + +RegisterID* BytecodeGenerator::emitLazyNewFunction(RegisterID* dst, FunctionBodyNode* function) +{ + std::pair<FunctionOffsetMap::iterator, bool> ptr = m_functionOffsets.add(function, 0); + if (ptr.second) + ptr.first->second = m_codeBlock->addFunctionDecl(makeFunction(m_globalData, function)); + return emitNewFunctionInternal(dst, ptr.first->second, true); +} + +RegisterID* BytecodeGenerator::emitNewFunctionInternal(RegisterID* dst, unsigned index, bool doNullCheck) +{ + createActivationIfNecessary(); + emitOpcode(op_new_func); + instructions().append(dst->index()); + instructions().append(index); + instructions().append(doNullCheck); + return dst; +} + +RegisterID* BytecodeGenerator::emitNewRegExp(RegisterID* dst, RegExp* regExp) +{ + emitOpcode(op_new_regexp); + instructions().append(dst->index()); + instructions().append(addRegExp(regExp)); + return dst; +} + +RegisterID* BytecodeGenerator::emitNewFunctionExpression(RegisterID* r0, FuncExprNode* n) +{ + FunctionBodyNode* function = n->body(); + unsigned index = m_codeBlock->addFunctionExpr(makeFunction(m_globalData, function)); + + createActivationIfNecessary(); + emitOpcode(op_new_func_exp); + instructions().append(r0->index()); + instructions().append(index); + return r0; +} + +RegisterID* BytecodeGenerator::emitCall(RegisterID* dst, RegisterID* func, CallArguments& callArguments, unsigned divot, unsigned startOffset, unsigned endOffset) +{ + return emitCall(op_call, dst, func, callArguments, divot, startOffset, endOffset); +} + +void BytecodeGenerator::createArgumentsIfNecessary() +{ + if (m_codeType != FunctionCode) + return; + + if (!m_codeBlock->usesArguments()) + return; + + // If we're in strict mode we tear off the arguments on function + // entry, so there's no need to check if we need to create them + // now + if (m_codeBlock->isStrictMode()) + return; + + emitOpcode(op_create_arguments); + instructions().append(m_codeBlock->argumentsRegister()); +} + +void BytecodeGenerator::createActivationIfNecessary() +{ + if (m_hasCreatedActivation) + return; + if (!m_codeBlock->needsFullScopeChain()) + return; + emitOpcode(op_create_activation); + instructions().append(m_activationRegister->index()); +} + +RegisterID* BytecodeGenerator::emitCallEval(RegisterID* dst, RegisterID* func, CallArguments& callArguments, unsigned divot, unsigned startOffset, unsigned endOffset) +{ + return emitCall(op_call_eval, dst, func, callArguments, divot, startOffset, endOffset); +} + +RegisterID* BytecodeGenerator::emitCall(OpcodeID opcodeID, RegisterID* dst, RegisterID* func, CallArguments& callArguments, unsigned divot, unsigned startOffset, unsigned endOffset) +{ + ASSERT(opcodeID == op_call || opcodeID == op_call_eval); + ASSERT(func->refCount()); + + if (m_shouldEmitProfileHooks) + emitMove(callArguments.profileHookRegister(), func); + + // Generate code for arguments. + unsigned argument = 0; + for (ArgumentListNode* n = callArguments.argumentsNode()->m_listNode; n; n = n->m_next) + emitNode(callArguments.argumentRegister(argument++), n); + + // Reserve space for call frame. + Vector<RefPtr<RegisterID>, RegisterFile::CallFrameHeaderSize> callFrame; + for (int i = 0; i < RegisterFile::CallFrameHeaderSize; ++i) + callFrame.append(newTemporary()); + + if (m_shouldEmitProfileHooks) { + emitOpcode(op_profile_will_call); + instructions().append(callArguments.profileHookRegister()->index()); + } + + emitExpressionInfo(divot, startOffset, endOffset); + + // Emit call. + emitOpcode(opcodeID); + instructions().append(func->index()); // func + instructions().append(callArguments.argumentCountIncludingThis()); // argCount + instructions().append(callArguments.registerOffset()); // registerOffset + instructions().append(0); + instructions().append(0); + if (dst != ignoredResult()) { + emitOpcode(op_call_put_result); + instructions().append(dst->index()); // dst + } + + if (m_shouldEmitProfileHooks) { + emitOpcode(op_profile_did_call); + instructions().append(callArguments.profileHookRegister()->index()); + } + + return dst; +} + +RegisterID* BytecodeGenerator::emitCallVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, RegisterID* profileHookRegister, unsigned divot, unsigned startOffset, unsigned endOffset) +{ + if (m_shouldEmitProfileHooks) { + emitMove(profileHookRegister, func); + emitOpcode(op_profile_will_call); + instructions().append(profileHookRegister->index()); + } + + emitExpressionInfo(divot, startOffset, endOffset); + + // Emit call. + emitOpcode(op_call_varargs); + instructions().append(func->index()); + instructions().append(thisRegister->index()); + instructions().append(arguments->index()); + instructions().append(firstFreeRegister->index()); + if (dst != ignoredResult()) { + emitOpcode(op_call_put_result); + instructions().append(dst->index()); + } + if (m_shouldEmitProfileHooks) { + emitOpcode(op_profile_did_call); + instructions().append(profileHookRegister->index()); + } + return dst; +} + +RegisterID* BytecodeGenerator::emitReturn(RegisterID* src) +{ + if (m_codeBlock->needsFullScopeChain()) { + emitOpcode(op_tear_off_activation); + instructions().append(m_activationRegister->index()); + instructions().append(m_codeBlock->argumentsRegister()); + } else if (m_codeBlock->usesArguments() && m_codeBlock->m_numParameters != 1 && !m_codeBlock->isStrictMode()) { + emitOpcode(op_tear_off_arguments); + instructions().append(m_codeBlock->argumentsRegister()); + } + + // Constructors use op_ret_object_or_this to check the result is an + // object, unless we can trivially determine the check is not + // necessary (currently, if the return value is 'this'). + if (isConstructor() && (src->index() != m_thisRegister.index())) { + emitOpcode(op_ret_object_or_this); + instructions().append(src->index()); + instructions().append(m_thisRegister.index()); + return src; + } + return emitUnaryNoDstOp(op_ret, src); +} + +RegisterID* BytecodeGenerator::emitUnaryNoDstOp(OpcodeID opcodeID, RegisterID* src) +{ + emitOpcode(opcodeID); + instructions().append(src->index()); + return src; +} + +RegisterID* BytecodeGenerator::emitConstruct(RegisterID* dst, RegisterID* func, CallArguments& callArguments, unsigned divot, unsigned startOffset, unsigned endOffset) +{ + ASSERT(func->refCount()); + + if (m_shouldEmitProfileHooks) + emitMove(callArguments.profileHookRegister(), func); + + // Generate code for arguments. + unsigned argument = 0; + if (ArgumentsNode* argumentsNode = callArguments.argumentsNode()) { + for (ArgumentListNode* n = argumentsNode->m_listNode; n; n = n->m_next) + emitNode(callArguments.argumentRegister(argument++), n); + } + + if (m_shouldEmitProfileHooks) { + emitOpcode(op_profile_will_call); + instructions().append(callArguments.profileHookRegister()->index()); + } + + // Reserve space for call frame. + Vector<RefPtr<RegisterID>, RegisterFile::CallFrameHeaderSize> callFrame; + for (int i = 0; i < RegisterFile::CallFrameHeaderSize; ++i) + callFrame.append(newTemporary()); + + emitExpressionInfo(divot, startOffset, endOffset); + + emitOpcode(op_construct); + instructions().append(func->index()); // func + instructions().append(callArguments.argumentCountIncludingThis()); // argCount + instructions().append(callArguments.registerOffset()); // registerOffset + instructions().append(0); + instructions().append(0); + if (dst != ignoredResult()) { + emitOpcode(op_call_put_result); + instructions().append(dst->index()); // dst + } + + if (m_shouldEmitProfileHooks) { + emitOpcode(op_profile_did_call); + instructions().append(callArguments.profileHookRegister()->index()); + } + + return dst; +} + +RegisterID* BytecodeGenerator::emitStrcat(RegisterID* dst, RegisterID* src, int count) +{ + emitOpcode(op_strcat); + instructions().append(dst->index()); + instructions().append(src->index()); + instructions().append(count); + + return dst; +} + +void BytecodeGenerator::emitToPrimitive(RegisterID* dst, RegisterID* src) +{ + emitOpcode(op_to_primitive); + instructions().append(dst->index()); + instructions().append(src->index()); +} + +RegisterID* BytecodeGenerator::emitPushScope(RegisterID* scope) +{ + ASSERT(scope->isTemporary()); + ControlFlowContext context; + context.isFinallyBlock = false; + m_scopeContextStack.append(context); + m_dynamicScopeDepth++; + + return emitUnaryNoDstOp(op_push_scope, scope); +} + +void BytecodeGenerator::emitPopScope() +{ + ASSERT(m_scopeContextStack.size()); + ASSERT(!m_scopeContextStack.last().isFinallyBlock); + + emitOpcode(op_pop_scope); + + m_scopeContextStack.removeLast(); + m_dynamicScopeDepth--; +} + +void BytecodeGenerator::emitDebugHook(DebugHookID debugHookID, int firstLine, int lastLine) +{ +#if ENABLE(DEBUG_WITH_BREAKPOINT) + if (debugHookID != DidReachBreakpoint) + return; +#else + if (!m_shouldEmitDebugHooks) + return; +#endif + emitOpcode(op_debug); + instructions().append(debugHookID); + instructions().append(firstLine); + instructions().append(lastLine); +} + +void BytecodeGenerator::pushFinallyContext(Label* target, RegisterID* retAddrDst) +{ + ControlFlowContext scope; + scope.isFinallyBlock = true; + FinallyContext context = { target, retAddrDst }; + scope.finallyContext = context; + m_scopeContextStack.append(scope); + m_finallyDepth++; +} + +void BytecodeGenerator::popFinallyContext() +{ + ASSERT(m_scopeContextStack.size()); + ASSERT(m_scopeContextStack.last().isFinallyBlock); + ASSERT(m_finallyDepth > 0); + m_scopeContextStack.removeLast(); + m_finallyDepth--; +} + +LabelScope* BytecodeGenerator::breakTarget(const Identifier& name) +{ + // Reclaim free label scopes. + // + // The condition was previously coded as 'm_labelScopes.size() && !m_labelScopes.last().refCount()', + // however sometimes this appears to lead to GCC going a little haywire and entering the loop with + // size 0, leading to segfaulty badness. We are yet to identify a valid cause within our code to + // cause the GCC codegen to misbehave in this fashion, and as such the following refactoring of the + // loop condition is a workaround. + while (m_labelScopes.size()) { + if (m_labelScopes.last().refCount()) + break; + m_labelScopes.removeLast(); + } + + if (!m_labelScopes.size()) + return 0; + + // We special-case the following, which is a syntax error in Firefox: + // label: + // break; + if (name.isEmpty()) { + for (int i = m_labelScopes.size() - 1; i >= 0; --i) { + LabelScope* scope = &m_labelScopes[i]; + if (scope->type() != LabelScope::NamedLabel) { + ASSERT(scope->breakTarget()); + return scope; + } + } + return 0; + } + + for (int i = m_labelScopes.size() - 1; i >= 0; --i) { + LabelScope* scope = &m_labelScopes[i]; + if (scope->name() && *scope->name() == name) { + ASSERT(scope->breakTarget()); + return scope; + } + } + return 0; +} + +LabelScope* BytecodeGenerator::continueTarget(const Identifier& name) +{ + // Reclaim free label scopes. + while (m_labelScopes.size() && !m_labelScopes.last().refCount()) + m_labelScopes.removeLast(); + + if (!m_labelScopes.size()) + return 0; + + if (name.isEmpty()) { + for (int i = m_labelScopes.size() - 1; i >= 0; --i) { + LabelScope* scope = &m_labelScopes[i]; + if (scope->type() == LabelScope::Loop) { + ASSERT(scope->continueTarget()); + return scope; + } + } + return 0; + } + + // Continue to the loop nested nearest to the label scope that matches + // 'name'. + LabelScope* result = 0; + for (int i = m_labelScopes.size() - 1; i >= 0; --i) { + LabelScope* scope = &m_labelScopes[i]; + if (scope->type() == LabelScope::Loop) { + ASSERT(scope->continueTarget()); + result = scope; + } + if (scope->name() && *scope->name() == name) + return result; // may be 0 + } + return 0; +} + +PassRefPtr<Label> BytecodeGenerator::emitComplexJumpScopes(Label* target, ControlFlowContext* topScope, ControlFlowContext* bottomScope) +{ + while (topScope > bottomScope) { + // First we count the number of dynamic scopes we need to remove to get + // to a finally block. + int nNormalScopes = 0; + while (topScope > bottomScope) { + if (topScope->isFinallyBlock) + break; + ++nNormalScopes; + --topScope; + } + + if (nNormalScopes) { + size_t begin = instructions().size(); + + // We need to remove a number of dynamic scopes to get to the next + // finally block + emitOpcode(op_jmp_scopes); + instructions().append(nNormalScopes); + + // If topScope == bottomScope then there isn't actually a finally block + // left to emit, so make the jmp_scopes jump directly to the target label + if (topScope == bottomScope) { + instructions().append(target->bind(begin, instructions().size())); + return target; + } + + // Otherwise we just use jmp_scopes to pop a group of scopes and go + // to the next instruction + RefPtr<Label> nextInsn = newLabel(); + instructions().append(nextInsn->bind(begin, instructions().size())); + emitLabel(nextInsn.get()); + } + + while (topScope > bottomScope && topScope->isFinallyBlock) { + emitJumpSubroutine(topScope->finallyContext.retAddrDst, topScope->finallyContext.finallyAddr); + --topScope; + } + } + return emitJump(target); +} + +PassRefPtr<Label> BytecodeGenerator::emitJumpScopes(Label* target, int targetScopeDepth) +{ + ASSERT(scopeDepth() - targetScopeDepth >= 0); + ASSERT(target->isForward()); + + size_t scopeDelta = scopeDepth() - targetScopeDepth; + ASSERT(scopeDelta <= m_scopeContextStack.size()); + if (!scopeDelta) + return emitJump(target); + + if (m_finallyDepth) + return emitComplexJumpScopes(target, &m_scopeContextStack.last(), &m_scopeContextStack.last() - scopeDelta); + + size_t begin = instructions().size(); + + emitOpcode(op_jmp_scopes); + instructions().append(scopeDelta); + instructions().append(target->bind(begin, instructions().size())); + return target; +} + +RegisterID* BytecodeGenerator::emitGetPropertyNames(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, Label* breakTarget) +{ + size_t begin = instructions().size(); + + emitOpcode(op_get_pnames); + instructions().append(dst->index()); + instructions().append(base->index()); + instructions().append(i->index()); + instructions().append(size->index()); + instructions().append(breakTarget->bind(begin, instructions().size())); + return dst; +} + +RegisterID* BytecodeGenerator::emitNextPropertyName(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, RegisterID* iter, Label* target) +{ + size_t begin = instructions().size(); + + emitOpcode(op_next_pname); + instructions().append(dst->index()); + instructions().append(base->index()); + instructions().append(i->index()); + instructions().append(size->index()); + instructions().append(iter->index()); + instructions().append(target->bind(begin, instructions().size())); + return dst; +} + +RegisterID* BytecodeGenerator::emitCatch(RegisterID* targetRegister, Label* start, Label* end) +{ + m_usesExceptions = true; +#if ENABLE(JIT) + HandlerInfo info = { start->bind(0, 0), end->bind(0, 0), instructions().size(), m_dynamicScopeDepth + m_baseScopeDepth, CodeLocationLabel() }; +#else + HandlerInfo info = { start->bind(0, 0), end->bind(0, 0), instructions().size(), m_dynamicScopeDepth + m_baseScopeDepth }; +#endif + + m_codeBlock->addExceptionHandler(info); + emitOpcode(op_catch); + instructions().append(targetRegister->index()); + return targetRegister; +} + +void BytecodeGenerator::emitThrowReferenceError(const UString& message) +{ + emitOpcode(op_throw_reference_error); + instructions().append(addConstantValue(jsString(globalData(), message))->index()); +} + +PassRefPtr<Label> BytecodeGenerator::emitJumpSubroutine(RegisterID* retAddrDst, Label* finally) +{ + size_t begin = instructions().size(); + + emitOpcode(op_jsr); + instructions().append(retAddrDst->index()); + instructions().append(finally->bind(begin, instructions().size())); + emitLabel(newLabel().get()); // Record the fact that the next instruction is implicitly labeled, because op_sret will return to it. + return finally; +} + +void BytecodeGenerator::emitSubroutineReturn(RegisterID* retAddrSrc) +{ + emitOpcode(op_sret); + instructions().append(retAddrSrc->index()); +} + +void BytecodeGenerator::emitPushNewScope(RegisterID* dst, const Identifier& property, RegisterID* value) +{ + ControlFlowContext context; + context.isFinallyBlock = false; + m_scopeContextStack.append(context); + m_dynamicScopeDepth++; + + emitOpcode(op_push_new_scope); + instructions().append(dst->index()); + instructions().append(addConstant(property)); + instructions().append(value->index()); +} + +void BytecodeGenerator::beginSwitch(RegisterID* scrutineeRegister, SwitchInfo::SwitchType type) +{ + SwitchInfo info = { instructions().size(), type }; + switch (type) { + case SwitchInfo::SwitchImmediate: + emitOpcode(op_switch_imm); + break; + case SwitchInfo::SwitchCharacter: + emitOpcode(op_switch_char); + break; + case SwitchInfo::SwitchString: + emitOpcode(op_switch_string); + break; + default: + ASSERT_NOT_REACHED(); + } + + instructions().append(0); // place holder for table index + instructions().append(0); // place holder for default target + instructions().append(scrutineeRegister->index()); + m_switchContextStack.append(info); +} + +static int32_t keyForImmediateSwitch(ExpressionNode* node, int32_t min, int32_t max) +{ + UNUSED_PARAM(max); + ASSERT(node->isNumber()); + double value = static_cast<NumberNode*>(node)->value(); + int32_t key = static_cast<int32_t>(value); + ASSERT(key == value); + ASSERT(key >= min); + ASSERT(key <= max); + return key - min; +} + +static void prepareJumpTableForImmediateSwitch(SimpleJumpTable& jumpTable, int32_t switchAddress, uint32_t clauseCount, RefPtr<Label>* labels, ExpressionNode** nodes, int32_t min, int32_t max) +{ + jumpTable.min = min; + jumpTable.branchOffsets.resize(max - min + 1); + jumpTable.branchOffsets.fill(0); + for (uint32_t i = 0; i < clauseCount; ++i) { + // We're emitting this after the clause labels should have been fixed, so + // the labels should not be "forward" references + ASSERT(!labels[i]->isForward()); + jumpTable.add(keyForImmediateSwitch(nodes[i], min, max), labels[i]->bind(switchAddress, switchAddress + 3)); + } +} + +static int32_t keyForCharacterSwitch(ExpressionNode* node, int32_t min, int32_t max) +{ + UNUSED_PARAM(max); + ASSERT(node->isString()); + StringImpl* clause = static_cast<StringNode*>(node)->value().impl(); + ASSERT(clause->length() == 1); + + int32_t key = (*clause)[0]; + ASSERT(key >= min); + ASSERT(key <= max); + return key - min; +} + +static void prepareJumpTableForCharacterSwitch(SimpleJumpTable& jumpTable, int32_t switchAddress, uint32_t clauseCount, RefPtr<Label>* labels, ExpressionNode** nodes, int32_t min, int32_t max) +{ + jumpTable.min = min; + jumpTable.branchOffsets.resize(max - min + 1); + jumpTable.branchOffsets.fill(0); + for (uint32_t i = 0; i < clauseCount; ++i) { + // We're emitting this after the clause labels should have been fixed, so + // the labels should not be "forward" references + ASSERT(!labels[i]->isForward()); + jumpTable.add(keyForCharacterSwitch(nodes[i], min, max), labels[i]->bind(switchAddress, switchAddress + 3)); + } +} + +static void prepareJumpTableForStringSwitch(StringJumpTable& jumpTable, int32_t switchAddress, uint32_t clauseCount, RefPtr<Label>* labels, ExpressionNode** nodes) +{ + for (uint32_t i = 0; i < clauseCount; ++i) { + // We're emitting this after the clause labels should have been fixed, so + // the labels should not be "forward" references + ASSERT(!labels[i]->isForward()); + + ASSERT(nodes[i]->isString()); + StringImpl* clause = static_cast<StringNode*>(nodes[i])->value().impl(); + OffsetLocation location; + location.branchOffset = labels[i]->bind(switchAddress, switchAddress + 3); + jumpTable.offsetTable.add(clause, location); + } +} + +void BytecodeGenerator::endSwitch(uint32_t clauseCount, RefPtr<Label>* labels, ExpressionNode** nodes, Label* defaultLabel, int32_t min, int32_t max) +{ + SwitchInfo switchInfo = m_switchContextStack.last(); + m_switchContextStack.removeLast(); + if (switchInfo.switchType == SwitchInfo::SwitchImmediate) { + instructions()[switchInfo.bytecodeOffset + 1] = m_codeBlock->numberOfImmediateSwitchJumpTables(); + instructions()[switchInfo.bytecodeOffset + 2] = defaultLabel->bind(switchInfo.bytecodeOffset, switchInfo.bytecodeOffset + 3); + + SimpleJumpTable& jumpTable = m_codeBlock->addImmediateSwitchJumpTable(); + prepareJumpTableForImmediateSwitch(jumpTable, switchInfo.bytecodeOffset, clauseCount, labels, nodes, min, max); + } else if (switchInfo.switchType == SwitchInfo::SwitchCharacter) { + instructions()[switchInfo.bytecodeOffset + 1] = m_codeBlock->numberOfCharacterSwitchJumpTables(); + instructions()[switchInfo.bytecodeOffset + 2] = defaultLabel->bind(switchInfo.bytecodeOffset, switchInfo.bytecodeOffset + 3); + + SimpleJumpTable& jumpTable = m_codeBlock->addCharacterSwitchJumpTable(); + prepareJumpTableForCharacterSwitch(jumpTable, switchInfo.bytecodeOffset, clauseCount, labels, nodes, min, max); + } else { + ASSERT(switchInfo.switchType == SwitchInfo::SwitchString); + instructions()[switchInfo.bytecodeOffset + 1] = m_codeBlock->numberOfStringSwitchJumpTables(); + instructions()[switchInfo.bytecodeOffset + 2] = defaultLabel->bind(switchInfo.bytecodeOffset, switchInfo.bytecodeOffset + 3); + + StringJumpTable& jumpTable = m_codeBlock->addStringSwitchJumpTable(); + prepareJumpTableForStringSwitch(jumpTable, switchInfo.bytecodeOffset, clauseCount, labels, nodes); + } +} + +RegisterID* BytecodeGenerator::emitThrowExpressionTooDeepException() +{ + // It would be nice to do an even better job of identifying exactly where the expression is. + // And we could make the caller pass the node pointer in, if there was some way of getting + // that from an arbitrary node. However, calling emitExpressionInfo without any useful data + // is still good enough to get us an accurate line number. + m_expressionTooDeep = true; + return newTemporary(); +} + +void BytecodeGenerator::setIsNumericCompareFunction(bool isNumericCompareFunction) +{ + m_codeBlock->setIsNumericCompareFunction(isNumericCompareFunction); +} + +bool BytecodeGenerator::isArgumentNumber(const Identifier& ident, int argumentNumber) +{ + RegisterID* registerID = registerFor(ident); + if (!registerID || registerID->index() >= 0) + return 0; + return registerID->index() == CallFrame::argumentOffset(argumentNumber); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h new file mode 100644 index 000000000..3ff5f2343 --- /dev/null +++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h @@ -0,0 +1,588 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca> + * + * 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. + */ + +#ifndef BytecodeGenerator_h +#define BytecodeGenerator_h + +#include "CodeBlock.h" +#include "HashTraits.h" +#include "Instruction.h" +#include "Label.h" +#include "LabelScope.h" +#include "Interpreter.h" +#include "RegisterID.h" +#include "SymbolTable.h" +#include "Debugger.h" +#include "Nodes.h" +#include <wtf/PassRefPtr.h> +#include <wtf/SegmentedVector.h> +#include <wtf/Vector.h> + +namespace JSC { + + class Identifier; + class ScopeChainNode; + + class CallArguments { + public: + CallArguments(BytecodeGenerator& generator, ArgumentsNode* argumentsNode); + + RegisterID* thisRegister() { return m_argv[0].get(); } + RegisterID* argumentRegister(unsigned i) { return m_argv[i + 1].get(); } + unsigned registerOffset() { return m_argv.last()->index() + CallFrame::offsetFor(argumentCountIncludingThis()); } + unsigned argumentCountIncludingThis() { return m_argv.size(); } + RegisterID* profileHookRegister() { return m_profileHookRegister.get(); } + ArgumentsNode* argumentsNode() { return m_argumentsNode; } + + private: + void newArgument(BytecodeGenerator&); + + RefPtr<RegisterID> m_profileHookRegister; + ArgumentsNode* m_argumentsNode; + Vector<RefPtr<RegisterID>, 8> m_argv; + }; + + struct FinallyContext { + Label* finallyAddr; + RegisterID* retAddrDst; + }; + + struct ControlFlowContext { + bool isFinallyBlock; + FinallyContext finallyContext; + }; + + struct ForInContext { + RefPtr<RegisterID> expectedSubscriptRegister; + RefPtr<RegisterID> iterRegister; + RefPtr<RegisterID> indexRegister; + RefPtr<RegisterID> propertyRegister; + }; + + class BytecodeGenerator { + WTF_MAKE_FAST_ALLOCATED; + public: + typedef DeclarationStacks::VarStack VarStack; + typedef DeclarationStacks::FunctionStack FunctionStack; + + JS_EXPORT_PRIVATE static void setDumpsGeneratedCode(bool dumpsGeneratedCode); + static bool dumpsGeneratedCode(); + + BytecodeGenerator(ProgramNode*, ScopeChainNode*, SymbolTable*, ProgramCodeBlock*, CompilationKind); + BytecodeGenerator(FunctionBodyNode*, ScopeChainNode*, SymbolTable*, CodeBlock*, CompilationKind); + BytecodeGenerator(EvalNode*, ScopeChainNode*, SymbolTable*, EvalCodeBlock*, CompilationKind); + + ~BytecodeGenerator(); + + JSGlobalData* globalData() const { return m_globalData; } + const CommonIdentifiers& propertyNames() const { return *m_globalData->propertyNames; } + + bool isConstructor() { return m_codeBlock->m_isConstructor; } + + JSObject* generate(); + + // Returns the register corresponding to a local variable, or 0 if no + // such register exists. Registers returned by registerFor do not + // require explicit reference counting. + RegisterID* registerFor(const Identifier&); + + bool isArgumentNumber(const Identifier&, int); + + void setIsNumericCompareFunction(bool isNumericCompareFunction); + + bool willResolveToArguments(const Identifier&); + RegisterID* uncheckedRegisterForArguments(); + + // Behaves as registerFor does, but ignores dynamic scope as + // dynamic scope should not interfere with const initialisation + RegisterID* constRegisterFor(const Identifier&); + + // Searches the scope chain in an attempt to statically locate the requested + // property. Returns false if for any reason the property cannot be safely + // optimised at all. Otherwise it will return the index and depth of the + // VariableObject that defines the property. If the property cannot be found + // statically, depth will contain the depth of the scope chain where dynamic + // lookup must begin. + bool findScopedProperty(const Identifier&, int& index, size_t& depth, bool forWriting, bool& includesDynamicScopes, JSObject*& globalObject); + + // Returns the register storing "this" + RegisterID* thisRegister() { return &m_thisRegister; } + + bool isLocal(const Identifier&); + bool isLocalConstant(const Identifier&); + + // Returns the next available temporary register. Registers returned by + // newTemporary require a modified form of reference counting: any + // register with a refcount of 0 is considered "available", meaning that + // the next instruction may overwrite it. + RegisterID* newTemporary(); + + RegisterID* highestUsedRegister(); + + // The same as newTemporary(), but this function returns "suggestion" if + // "suggestion" is a temporary. This function is helpful in situations + // where you've put "suggestion" in a RefPtr, but you'd like to allow + // the next instruction to overwrite it anyway. + RegisterID* newTemporaryOr(RegisterID* suggestion) { return suggestion->isTemporary() ? suggestion : newTemporary(); } + + // Functions for handling of dst register + + RegisterID* ignoredResult() { return &m_ignoredResultRegister; } + + // Returns a place to write intermediate values of an operation + // which reuses dst if it is safe to do so. + RegisterID* tempDestination(RegisterID* dst) + { + return (dst && dst != ignoredResult() && dst->isTemporary()) ? dst : newTemporary(); + } + + // Returns the place to write the final output of an operation. + RegisterID* finalDestination(RegisterID* originalDst, RegisterID* tempDst = 0) + { + if (originalDst && originalDst != ignoredResult()) + return originalDst; + ASSERT(tempDst != ignoredResult()); + if (tempDst && tempDst->isTemporary()) + return tempDst; + return newTemporary(); + } + + // Returns the place to write the final output of an operation. + RegisterID* finalDestinationOrIgnored(RegisterID* originalDst, RegisterID* tempDst = 0) + { + if (originalDst) + return originalDst; + ASSERT(tempDst != ignoredResult()); + if (tempDst && tempDst->isTemporary()) + return tempDst; + return newTemporary(); + } + + RegisterID* destinationForAssignResult(RegisterID* dst) + { + if (dst && dst != ignoredResult() && m_codeBlock->needsFullScopeChain()) + return dst->isTemporary() ? dst : newTemporary(); + return 0; + } + + // Moves src to dst if dst is not null and is different from src, otherwise just returns src. + RegisterID* moveToDestinationIfNeeded(RegisterID* dst, RegisterID* src) + { + return dst == ignoredResult() ? 0 : (dst && dst != src) ? emitMove(dst, src) : src; + } + + PassRefPtr<LabelScope> newLabelScope(LabelScope::Type, const Identifier* = 0); + PassRefPtr<Label> newLabel(); + + // The emitNode functions are just syntactic sugar for calling + // Node::emitCode. These functions accept a 0 for the register, + // meaning that the node should allocate a register, or ignoredResult(), + // meaning that the node need not put the result in a register. + // Other emit functions do not accept 0 or ignoredResult(). + RegisterID* emitNode(RegisterID* dst, Node* n) + { + // Node::emitCode assumes that dst, if provided, is either a local or a referenced temporary. + ASSERT(!dst || dst == ignoredResult() || !dst->isTemporary() || dst->refCount()); + addLineInfo(n->lineNo()); + return m_stack.recursionCheck() + ? n->emitBytecode(*this, dst) + : emitThrowExpressionTooDeepException(); + } + + RegisterID* emitNode(Node* n) + { + return emitNode(0, n); + } + + void emitNodeInConditionContext(ExpressionNode* n, Label* trueTarget, Label* falseTarget, bool fallThroughMeansTrue) + { + addLineInfo(n->lineNo()); + if (m_stack.recursionCheck()) + n->emitBytecodeInConditionContext(*this, trueTarget, falseTarget, fallThroughMeansTrue); + else + emitThrowExpressionTooDeepException(); + } + + void emitExpressionInfo(unsigned divot, unsigned startOffset, unsigned endOffset) + { + if (!m_shouldEmitRichSourceInfo) + return; + + divot -= m_codeBlock->sourceOffset(); + if (divot > ExpressionRangeInfo::MaxDivot) { + // Overflow has occurred, we can only give line number info for errors for this region + divot = 0; + startOffset = 0; + endOffset = 0; + } else if (startOffset > ExpressionRangeInfo::MaxOffset) { + // If the start offset is out of bounds we clear both offsets + // so we only get the divot marker. Error message will have to be reduced + // to line and column number. + startOffset = 0; + endOffset = 0; + } else if (endOffset > ExpressionRangeInfo::MaxOffset) { + // The end offset is only used for additional context, and is much more likely + // to overflow (eg. function call arguments) so we are willing to drop it without + // dropping the rest of the range. + endOffset = 0; + } + + ExpressionRangeInfo info; + info.instructionOffset = instructions().size(); + info.divotPoint = divot; + info.startOffset = startOffset; + info.endOffset = endOffset; + m_codeBlock->addExpressionInfo(info); + } + + ALWAYS_INLINE bool leftHandSideNeedsCopy(bool rightHasAssignments, bool rightIsPure) + { + return (m_codeType != FunctionCode || m_codeBlock->needsFullScopeChain() || rightHasAssignments) && !rightIsPure; + } + + ALWAYS_INLINE PassRefPtr<RegisterID> emitNodeForLeftHandSide(ExpressionNode* n, bool rightHasAssignments, bool rightIsPure) + { + if (leftHandSideNeedsCopy(rightHasAssignments, rightIsPure)) { + PassRefPtr<RegisterID> dst = newTemporary(); + emitNode(dst.get(), n); + return dst; + } + + return PassRefPtr<RegisterID>(emitNode(n)); + } + + RegisterID* emitLoad(RegisterID* dst, bool); + RegisterID* emitLoad(RegisterID* dst, double); + RegisterID* emitLoad(RegisterID* dst, const Identifier&); + RegisterID* emitLoad(RegisterID* dst, JSValue); + + RegisterID* emitUnaryOp(OpcodeID, RegisterID* dst, RegisterID* src); + RegisterID* emitBinaryOp(OpcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2, OperandTypes); + RegisterID* emitEqualityOp(OpcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2); + RegisterID* emitUnaryNoDstOp(OpcodeID, RegisterID* src); + + RegisterID* emitNewObject(RegisterID* dst); + RegisterID* emitNewArray(RegisterID* dst, ElementNode*, unsigned length); // stops at first elision + + RegisterID* emitNewFunction(RegisterID* dst, FunctionBodyNode* body); + RegisterID* emitLazyNewFunction(RegisterID* dst, FunctionBodyNode* body); + RegisterID* emitNewFunctionInternal(RegisterID* dst, unsigned index, bool shouldNullCheck); + RegisterID* emitNewFunctionExpression(RegisterID* dst, FuncExprNode* func); + RegisterID* emitNewRegExp(RegisterID* dst, RegExp*); + + RegisterID* emitMove(RegisterID* dst, RegisterID* src); + + RegisterID* emitToJSNumber(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_to_jsnumber, dst, src); } + RegisterID* emitPreInc(RegisterID* srcDst); + RegisterID* emitPreDec(RegisterID* srcDst); + RegisterID* emitPostInc(RegisterID* dst, RegisterID* srcDst); + RegisterID* emitPostDec(RegisterID* dst, RegisterID* srcDst); + + void emitCheckHasInstance(RegisterID* base); + RegisterID* emitInstanceOf(RegisterID* dst, RegisterID* value, RegisterID* base, RegisterID* basePrototype); + RegisterID* emitTypeOf(RegisterID* dst, RegisterID* src) { return emitUnaryOp(op_typeof, dst, src); } + RegisterID* emitIn(RegisterID* dst, RegisterID* property, RegisterID* base) { return emitBinaryOp(op_in, dst, property, base, OperandTypes()); } + + RegisterID* emitResolve(RegisterID* dst, const Identifier& property); + RegisterID* emitGetScopedVar(RegisterID* dst, size_t skip, int index, JSValue globalObject); + RegisterID* emitPutScopedVar(size_t skip, int index, RegisterID* value, JSValue globalObject); + + RegisterID* emitResolveBase(RegisterID* dst, const Identifier& property); + RegisterID* emitResolveBaseForPut(RegisterID* dst, const Identifier& property); + RegisterID* emitResolveWithBase(RegisterID* baseDst, RegisterID* propDst, const Identifier& property); + RegisterID* emitResolveWithThis(RegisterID* baseDst, RegisterID* propDst, const Identifier& property); + + void emitMethodCheck(); + + RegisterID* emitGetById(RegisterID* dst, RegisterID* base, const Identifier& property); + RegisterID* emitGetArgumentsLength(RegisterID* dst, RegisterID* base); + RegisterID* emitPutById(RegisterID* base, const Identifier& property, RegisterID* value); + RegisterID* emitDirectPutById(RegisterID* base, const Identifier& property, RegisterID* value); + RegisterID* emitDeleteById(RegisterID* dst, RegisterID* base, const Identifier&); + RegisterID* emitGetByVal(RegisterID* dst, RegisterID* base, RegisterID* property); + RegisterID* emitGetArgumentByVal(RegisterID* dst, RegisterID* base, RegisterID* property); + RegisterID* emitPutByVal(RegisterID* base, RegisterID* property, RegisterID* value); + RegisterID* emitDeleteByVal(RegisterID* dst, RegisterID* base, RegisterID* property); + RegisterID* emitPutByIndex(RegisterID* base, unsigned index, RegisterID* value); + RegisterID* emitPutGetter(RegisterID* base, const Identifier& property, RegisterID* value); + RegisterID* emitPutSetter(RegisterID* base, const Identifier& property, RegisterID* value); + + RegisterID* emitCall(RegisterID* dst, RegisterID* func, CallArguments&, unsigned divot, unsigned startOffset, unsigned endOffset); + RegisterID* emitCallEval(RegisterID* dst, RegisterID* func, CallArguments&, unsigned divot, unsigned startOffset, unsigned endOffset); + RegisterID* emitCallVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, RegisterID* profileHookRegister, unsigned divot, unsigned startOffset, unsigned endOffset); + RegisterID* emitLoadVarargs(RegisterID* argCountDst, RegisterID* thisRegister, RegisterID* args); + + RegisterID* emitReturn(RegisterID* src); + RegisterID* emitEnd(RegisterID* src) { return emitUnaryNoDstOp(op_end, src); } + + RegisterID* emitConstruct(RegisterID* dst, RegisterID* func, CallArguments&, unsigned divot, unsigned startOffset, unsigned endOffset); + RegisterID* emitStrcat(RegisterID* dst, RegisterID* src, int count); + void emitToPrimitive(RegisterID* dst, RegisterID* src); + + PassRefPtr<Label> emitLabel(Label*); + void emitLoopHint(); + PassRefPtr<Label> emitJump(Label* target); + PassRefPtr<Label> emitJumpIfTrue(RegisterID* cond, Label* target); + PassRefPtr<Label> emitJumpIfFalse(RegisterID* cond, Label* target); + PassRefPtr<Label> emitJumpIfNotFunctionCall(RegisterID* cond, Label* target); + PassRefPtr<Label> emitJumpIfNotFunctionApply(RegisterID* cond, Label* target); + PassRefPtr<Label> emitJumpScopes(Label* target, int targetScopeDepth); + + PassRefPtr<Label> emitJumpSubroutine(RegisterID* retAddrDst, Label*); + void emitSubroutineReturn(RegisterID* retAddrSrc); + + RegisterID* emitGetPropertyNames(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, Label* breakTarget); + RegisterID* emitNextPropertyName(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, RegisterID* iter, Label* target); + + RegisterID* emitCatch(RegisterID*, Label* start, Label* end); + void emitThrow(RegisterID* exc) + { + m_usesExceptions = true; + emitUnaryNoDstOp(op_throw, exc); + } + + void emitThrowReferenceError(const UString& message); + + void emitPushNewScope(RegisterID* dst, const Identifier& property, RegisterID* value); + + RegisterID* emitPushScope(RegisterID* scope); + void emitPopScope(); + + void emitDebugHook(DebugHookID, int firstLine, int lastLine); + + int scopeDepth() { return m_dynamicScopeDepth + m_finallyDepth; } + bool hasFinaliser() { return m_finallyDepth != 0; } + + void pushFinallyContext(Label* target, RegisterID* returnAddrDst); + void popFinallyContext(); + + void pushOptimisedForIn(RegisterID* expectedBase, RegisterID* iter, RegisterID* index, RegisterID* propertyRegister) + { + ForInContext context = { expectedBase, iter, index, propertyRegister }; + m_forInContextStack.append(context); + } + + void popOptimisedForIn() + { + m_forInContextStack.removeLast(); + } + + LabelScope* breakTarget(const Identifier&); + LabelScope* continueTarget(const Identifier&); + + void beginSwitch(RegisterID*, SwitchInfo::SwitchType); + void endSwitch(uint32_t clauseCount, RefPtr<Label>*, ExpressionNode**, Label* defaultLabel, int32_t min, int32_t range); + + CodeType codeType() const { return m_codeType; } + + bool shouldEmitProfileHooks() { return m_shouldEmitProfileHooks; } + + bool isStrictMode() const { return m_codeBlock->isStrictMode(); } + + ScopeChainNode* scopeChain() const { return m_scopeChain.get(); } + + private: + void emitOpcode(OpcodeID); + void retrieveLastBinaryOp(int& dstIndex, int& src1Index, int& src2Index); + void retrieveLastUnaryOp(int& dstIndex, int& srcIndex); + ALWAYS_INLINE void rewindBinaryOp(); + ALWAYS_INLINE void rewindUnaryOp(); + + PassRefPtr<Label> emitComplexJumpScopes(Label* target, ControlFlowContext* topScope, ControlFlowContext* bottomScope); + + typedef HashMap<double, JSValue> NumberMap; + typedef HashMap<StringImpl*, JSString*, IdentifierRepHash> IdentifierStringMap; + + RegisterID* emitCall(OpcodeID, RegisterID* dst, RegisterID* func, CallArguments&, unsigned divot, unsigned startOffset, unsigned endOffset); + + RegisterID* newRegister(); + + // Adds a var slot and maps it to the name ident in symbolTable(). + RegisterID* addVar(const Identifier& ident, bool isConstant) + { + RegisterID* local; + addVar(ident, isConstant, local); + return local; + } + + // Ditto. Returns true if a new RegisterID was added, false if a pre-existing RegisterID was re-used. + bool addVar(const Identifier&, bool isConstant, RegisterID*&); + + // Adds an anonymous var slot. To give this slot a name, add it to symbolTable(). + RegisterID* addVar() + { + ++m_codeBlock->m_numVars; + return newRegister(); + } + + // Returns the index of the added var. + int addGlobalVar(const Identifier&, bool isConstant); + + void addParameter(const Identifier&, int parameterIndex); + + void preserveLastVar(); + bool shouldAvoidResolveGlobal(); + + RegisterID& registerFor(int index) + { + if (index >= 0) + return m_calleeRegisters[index]; + + ASSERT(m_parameters.size()); + return m_parameters[index + m_parameters.size() + RegisterFile::CallFrameHeaderSize]; + } + + unsigned addConstant(const Identifier&); + RegisterID* addConstantValue(JSValue); + unsigned addRegExp(RegExp*); + + unsigned addConstantBuffer(unsigned length); + + FunctionExecutable* makeFunction(ExecState* exec, FunctionBodyNode* body) + { + return FunctionExecutable::create(exec, body->ident(), body->source(), body->usesArguments(), body->parameters(), body->isStrictMode(), body->lineNo(), body->lastLine()); + } + + FunctionExecutable* makeFunction(JSGlobalData* globalData, FunctionBodyNode* body) + { + return FunctionExecutable::create(*globalData, body->ident(), body->source(), body->usesArguments(), body->parameters(), body->isStrictMode(), body->lineNo(), body->lastLine()); + } + + JSString* addStringConstant(const Identifier&); + + void addLineInfo(unsigned lineNo) + { +#if !ENABLE(OPCODE_SAMPLING) + if (m_shouldEmitRichSourceInfo) +#endif + m_codeBlock->addLineInfo(instructions().size(), lineNo); + } + + RegisterID* emitInitLazyRegister(RegisterID*); + + Vector<Instruction>& instructions() { return m_codeBlock->instructions(); } + SymbolTable& symbolTable() { return *m_symbolTable; } + + bool shouldOptimizeLocals() + { + if (m_dynamicScopeDepth) + return false; + + if (m_codeType != FunctionCode) + return false; + + return true; + } + + bool canOptimizeNonLocals() + { + if (m_dynamicScopeDepth) + return false; + + if (m_codeType == EvalCode) + return false; + + if (m_codeType == FunctionCode && m_codeBlock->usesEval()) + return false; + + return true; + } + + RegisterID* emitThrowExpressionTooDeepException(); + + void createArgumentsIfNecessary(); + void createActivationIfNecessary(); + RegisterID* createLazyRegisterIfNecessary(RegisterID*); + + bool m_shouldEmitDebugHooks; + bool m_shouldEmitProfileHooks; + bool m_shouldEmitRichSourceInfo; + + Strong<ScopeChainNode> m_scopeChain; + SymbolTable* m_symbolTable; + + ScopeNode* m_scopeNode; + CodeBlock* m_codeBlock; + + // Some of these objects keep pointers to one another. They are arranged + // to ensure a sane destruction order that avoids references to freed memory. + HashSet<RefPtr<StringImpl>, IdentifierRepHash> m_functions; + RegisterID m_ignoredResultRegister; + RegisterID m_thisRegister; + RegisterID* m_activationRegister; + SegmentedVector<RegisterID, 32> m_constantPoolRegisters; + SegmentedVector<RegisterID, 32> m_calleeRegisters; + SegmentedVector<RegisterID, 32> m_parameters; + SegmentedVector<Label, 32> m_labels; + SegmentedVector<LabelScope, 8> m_labelScopes; + RefPtr<RegisterID> m_lastVar; + int m_finallyDepth; + int m_dynamicScopeDepth; + int m_baseScopeDepth; + CodeType m_codeType; + + Vector<ControlFlowContext> m_scopeContextStack; + Vector<SwitchInfo> m_switchContextStack; + Vector<ForInContext> m_forInContextStack; + + int m_firstConstantIndex; + int m_nextConstantOffset; + unsigned m_globalConstantIndex; + + int m_globalVarStorageOffset; + + bool m_hasCreatedActivation; + int m_firstLazyFunction; + int m_lastLazyFunction; + HashMap<unsigned int, FunctionBodyNode*, WTF::IntHash<unsigned int>, WTF::UnsignedWithZeroKeyHashTraits<unsigned int> > m_lazyFunctions; + typedef HashMap<FunctionBodyNode*, unsigned> FunctionOffsetMap; + FunctionOffsetMap m_functionOffsets; + + // Constant pool + IdentifierMap m_identifierMap; + JSValueMap m_jsValueMap; + NumberMap m_numberMap; + IdentifierStringMap m_stringMap; + + JSGlobalData* m_globalData; + + OpcodeID m_lastOpcodeID; +#ifndef NDEBUG + size_t m_lastOpcodePosition; +#endif + + StackBounds m_stack; + + bool m_usesExceptions; + bool m_expressionTooDeep; + }; + +} + +#endif // BytecodeGenerator_h diff --git a/Source/JavaScriptCore/bytecompiler/Label.h b/Source/JavaScriptCore/bytecompiler/Label.h new file mode 100644 index 000000000..8cab1dbc0 --- /dev/null +++ b/Source/JavaScriptCore/bytecompiler/Label.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * 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. + */ + +#ifndef Label_h +#define Label_h + +#include "CodeBlock.h" +#include "Instruction.h" +#include <wtf/Assertions.h> +#include <wtf/Vector.h> +#include <limits.h> + +namespace JSC { + + class Label { + public: + explicit Label(CodeBlock* codeBlock) + : m_refCount(0) + , m_location(invalidLocation) + , m_codeBlock(codeBlock) + { + } + + void setLocation(unsigned location) + { + m_location = location; + + unsigned size = m_unresolvedJumps.size(); + for (unsigned i = 0; i < size; ++i) + m_codeBlock->instructions()[m_unresolvedJumps[i].second].u.operand = m_location - m_unresolvedJumps[i].first; + } + + int bind(int opcode, int offset) const + { + if (m_location == invalidLocation) { + m_unresolvedJumps.append(std::make_pair(opcode, offset)); + return 0; + } + return m_location - opcode; + } + + void ref() { ++m_refCount; } + void deref() + { + --m_refCount; + ASSERT(m_refCount >= 0); + } + int refCount() const { return m_refCount; } + + bool isForward() const { return m_location == invalidLocation; } + + private: + typedef Vector<std::pair<int, int>, 8> JumpVector; + + static const unsigned invalidLocation = UINT_MAX; + + int m_refCount; + unsigned m_location; + CodeBlock* m_codeBlock; + mutable JumpVector m_unresolvedJumps; + }; + +} // namespace JSC + +#endif // Label_h diff --git a/Source/JavaScriptCore/bytecompiler/LabelScope.h b/Source/JavaScriptCore/bytecompiler/LabelScope.h new file mode 100644 index 000000000..cc21fffd1 --- /dev/null +++ b/Source/JavaScriptCore/bytecompiler/LabelScope.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * 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. + */ + +#ifndef LabelScope_h +#define LabelScope_h + +#include <wtf/PassRefPtr.h> +#include "Label.h" + +namespace JSC { + + class Identifier; + + class LabelScope { + public: + enum Type { Loop, Switch, NamedLabel }; + + LabelScope(Type type, const Identifier* name, int scopeDepth, PassRefPtr<Label> breakTarget, PassRefPtr<Label> continueTarget) + : m_refCount(0) + , m_type(type) + , m_name(name) + , m_scopeDepth(scopeDepth) + , m_breakTarget(breakTarget) + , m_continueTarget(continueTarget) + { + } + + void ref() { ++m_refCount; } + void deref() + { + --m_refCount; + ASSERT(m_refCount >= 0); + } + int refCount() const { return m_refCount; } + + Label* breakTarget() const { return m_breakTarget.get(); } + Label* continueTarget() const { return m_continueTarget.get(); } + + Type type() const { return m_type; } + const Identifier* name() const { return m_name; } + int scopeDepth() const { return m_scopeDepth; } + + private: + int m_refCount; + Type m_type; + const Identifier* m_name; + int m_scopeDepth; + RefPtr<Label> m_breakTarget; + RefPtr<Label> m_continueTarget; + }; + +} // namespace JSC + +#endif // LabelScope_h diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp new file mode 100644 index 000000000..a0127d8d7 --- /dev/null +++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp @@ -0,0 +1,2076 @@ +/* +* Copyright (C) 1999-2002 Harri Porten (porten@kde.org) +* Copyright (C) 2001 Peter Kelly (pmk@post.com) +* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. +* Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) +* Copyright (C) 2007 Maks Orlovich +* Copyright (C) 2007 Eric Seidel <eric@webkit.org> +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public License +* along with this library; see the file COPYING.LIB. If not, write to +* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301, USA. +* +*/ + +#include "config.h" +#include "Nodes.h" +#include "NodeConstructors.h" + +#include "BytecodeGenerator.h" +#include "CallFrame.h" +#include "Debugger.h" +#include "JIT.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "JSStaticScopeObject.h" +#include "LabelScope.h" +#include "Lexer.h" +#include "Operations.h" +#include "Parser.h" +#include "PropertyNameArray.h" +#include "RegExpCache.h" +#include "RegExpObject.h" +#include "SamplingTool.h" +#include "UStringConcatenate.h" +#include <wtf/Assertions.h> +#include <wtf/RefCountedLeakCounter.h> +#include <wtf/Threading.h> + +using namespace WTF; + +namespace JSC { + +/* + Details of the emitBytecode function. + + Return value: The register holding the production's value. + dst: An optional parameter specifying the most efficient destination at + which to store the production's value. The callee must honor dst. + + The dst argument provides for a crude form of copy propagation. For example, + + x = 1 + + becomes + + load r[x], 1 + + instead of + + load r0, 1 + mov r[x], r0 + + because the assignment node, "x =", passes r[x] as dst to the number node, "1". +*/ + +// ------------------------------ ThrowableExpressionData -------------------------------- + +RegisterID* ThrowableExpressionData::emitThrowReferenceError(BytecodeGenerator& generator, const UString& message) +{ + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitThrowReferenceError(message); + return generator.newTemporary(); +} + +// ------------------------------ NullNode ------------------------------------- + +RegisterID* NullNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (dst == generator.ignoredResult()) + return 0; + return generator.emitLoad(dst, jsNull()); +} + +// ------------------------------ BooleanNode ---------------------------------- + +RegisterID* BooleanNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (dst == generator.ignoredResult()) + return 0; + return generator.emitLoad(dst, m_value); +} + +// ------------------------------ NumberNode ----------------------------------- + +RegisterID* NumberNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (dst == generator.ignoredResult()) + return 0; + return generator.emitLoad(dst, m_value); +} + +// ------------------------------ StringNode ----------------------------------- + +RegisterID* StringNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (dst == generator.ignoredResult()) + return 0; + return generator.emitLoad(dst, m_value); +} + +// ------------------------------ RegExpNode ----------------------------------- + +RegisterID* RegExpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (dst == generator.ignoredResult()) + return 0; + return generator.emitNewRegExp(generator.finalDestination(dst), RegExp::create(*generator.globalData(), m_pattern.ustring(), regExpFlags(m_flags.ustring()))); +} + +// ------------------------------ ThisNode ------------------------------------- + +RegisterID* ThisNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (dst == generator.ignoredResult()) + return 0; + return generator.moveToDestinationIfNeeded(dst, generator.thisRegister()); +} + +// ------------------------------ ResolveNode ---------------------------------- + +bool ResolveNode::isPure(BytecodeGenerator& generator) const +{ + return generator.isLocal(m_ident); +} + +RegisterID* ResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (RegisterID* local = generator.registerFor(m_ident)) { + if (dst == generator.ignoredResult()) + return 0; + return generator.moveToDestinationIfNeeded(dst, local); + } + + generator.emitExpressionInfo(m_startOffset + m_ident.length(), m_ident.length(), 0); + return generator.emitResolve(generator.finalDestination(dst), m_ident); +} + +// ------------------------------ ArrayNode ------------------------------------ + +RegisterID* ArrayNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + // FIXME: Should we put all of this code into emitNewArray? + + unsigned length = 0; + ElementNode* firstPutElement; + for (firstPutElement = m_element; firstPutElement; firstPutElement = firstPutElement->next()) { + if (firstPutElement->elision()) + break; + ++length; + } + + if (!firstPutElement && !m_elision) + return generator.emitNewArray(generator.finalDestination(dst), m_element, length); + + RefPtr<RegisterID> array = generator.emitNewArray(generator.tempDestination(dst), m_element, length); + + for (ElementNode* n = firstPutElement; n; n = n->next()) { + RegisterID* value = generator.emitNode(n->value()); + length += n->elision(); + generator.emitPutByIndex(array.get(), length++, value); + } + + if (m_elision) { + RegisterID* value = generator.emitLoad(0, jsNumber(m_elision + length)); + generator.emitPutById(array.get(), generator.propertyNames().length, value); + } + + return generator.moveToDestinationIfNeeded(dst, array.get()); +} + +bool ArrayNode::isSimpleArray() const +{ + if (m_elision || m_optional) + return false; + for (ElementNode* ptr = m_element; ptr; ptr = ptr->next()) { + if (ptr->elision()) + return false; + } + return true; +} + +ArgumentListNode* ArrayNode::toArgumentList(JSGlobalData* globalData, int lineNumber) const +{ + ASSERT(!m_elision && !m_optional); + ElementNode* ptr = m_element; + if (!ptr) + return 0; + ArgumentListNode* head = new (globalData) ArgumentListNode(lineNumber, ptr->value()); + ArgumentListNode* tail = head; + ptr = ptr->next(); + for (; ptr; ptr = ptr->next()) { + ASSERT(!ptr->elision()); + tail = new (globalData) ArgumentListNode(lineNumber, tail, ptr->value()); + } + return head; +} + +// ------------------------------ ObjectLiteralNode ---------------------------- + +RegisterID* ObjectLiteralNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (!m_list) { + if (dst == generator.ignoredResult()) + return 0; + return generator.emitNewObject(generator.finalDestination(dst)); + } + return generator.emitNode(dst, m_list); +} + +// ------------------------------ PropertyListNode ----------------------------- + +RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> newObj = generator.tempDestination(dst); + + generator.emitNewObject(newObj.get()); + + for (PropertyListNode* p = this; p; p = p->m_next) { + RegisterID* value = generator.emitNode(p->m_node->m_assign); + + switch (p->m_node->m_type) { + case PropertyNode::Constant: { + generator.emitDirectPutById(newObj.get(), p->m_node->name(), value); + break; + } + case PropertyNode::Getter: { + generator.emitPutGetter(newObj.get(), p->m_node->name(), value); + break; + } + case PropertyNode::Setter: { + generator.emitPutSetter(newObj.get(), p->m_node->name(), value); + break; + } + default: + ASSERT_NOT_REACHED(); + } + } + + return generator.moveToDestinationIfNeeded(dst, newObj.get()); +} + +// ------------------------------ BracketAccessorNode -------------------------------- + +RegisterID* BracketAccessorNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (m_base->isResolveNode() && generator.willResolveToArguments(static_cast<ResolveNode*>(m_base)->identifier())) { + RegisterID* property = generator.emitNode(m_subscript); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitGetArgumentByVal(generator.finalDestination(dst), generator.uncheckedRegisterForArguments(), property); + } + + RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments, m_subscript->isPure(generator)); + RegisterID* property = generator.emitNode(m_subscript); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitGetByVal(generator.finalDestination(dst), base.get(), property); +} + +// ------------------------------ DotAccessorNode -------------------------------- + +RegisterID* DotAccessorNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (m_ident == generator.propertyNames().length) { + if (!m_base->isResolveNode()) + goto nonArgumentsPath; + ResolveNode* resolveNode = static_cast<ResolveNode*>(m_base); + if (!generator.willResolveToArguments(resolveNode->identifier())) + goto nonArgumentsPath; + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitGetArgumentsLength(generator.finalDestination(dst), generator.uncheckedRegisterForArguments()); + } + +nonArgumentsPath: + RegisterID* base = generator.emitNode(m_base); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitGetById(generator.finalDestination(dst), base, m_ident); +} + +// ------------------------------ ArgumentListNode ----------------------------- + +RegisterID* ArgumentListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + ASSERT(m_expr); + return generator.emitNode(dst, m_expr); +} + +// ------------------------------ NewExprNode ---------------------------------- + +RegisterID* NewExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> func = generator.emitNode(m_expr); + CallArguments callArguments(generator, m_args); + return generator.emitConstruct(generator.finalDestinationOrIgnored(dst), func.get(), callArguments, divot(), startOffset(), endOffset()); +} + +inline CallArguments::CallArguments(BytecodeGenerator& generator, ArgumentsNode* argumentsNode) + : m_argumentsNode(argumentsNode) +{ + if (generator.shouldEmitProfileHooks()) + m_profileHookRegister = generator.newTemporary(); + + size_t argumentCountIncludingThis = 1; // 'this' register. + if (argumentsNode) { + for (ArgumentListNode* node = argumentsNode->m_listNode; node; node = node->m_next) + ++argumentCountIncludingThis; + } + + m_argv.grow(argumentCountIncludingThis); + for (int i = argumentCountIncludingThis - 1; i >= 0; --i) { + m_argv[i] = generator.newTemporary(); + ASSERT(static_cast<size_t>(i) == m_argv.size() - 1 || m_argv[i]->index() == m_argv[i + 1]->index() + 1); + } +} + +inline void CallArguments::newArgument(BytecodeGenerator& generator) +{ + RefPtr<RegisterID> tmp = generator.newTemporary(); + ASSERT(m_argv.isEmpty() || tmp->index() == m_argv.last()->index() + 1); // Calling convention assumes that all arguments are contiguous. + m_argv.append(tmp.release()); +} + +// ------------------------------ EvalFunctionCallNode ---------------------------------- + +RegisterID* EvalFunctionCallNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> func = generator.tempDestination(dst); + CallArguments callArguments(generator, m_args); + generator.emitExpressionInfo(divot() - startOffset() + 4, 4, 0); + generator.emitResolveWithThis(callArguments.thisRegister(), func.get(), generator.propertyNames().eval); + return generator.emitCallEval(generator.finalDestination(dst, func.get()), func.get(), callArguments, divot(), startOffset(), endOffset()); +} + +// ------------------------------ FunctionCallValueNode ---------------------------------- + +RegisterID* FunctionCallValueNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> func = generator.emitNode(m_expr); + CallArguments callArguments(generator, m_args); + generator.emitLoad(callArguments.thisRegister(), jsUndefined()); + return generator.emitCall(generator.finalDestinationOrIgnored(dst, func.get()), func.get(), callArguments, divot(), startOffset(), endOffset()); +} + +// ------------------------------ FunctionCallResolveNode ---------------------------------- + +RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (RefPtr<RegisterID> local = generator.registerFor(m_ident)) { + CallArguments callArguments(generator, m_args); + generator.emitLoad(callArguments.thisRegister(), jsUndefined()); + return generator.emitCall(generator.finalDestinationOrIgnored(dst, callArguments.thisRegister()), local.get(), callArguments, divot(), startOffset(), endOffset()); + } + + int index = 0; + size_t depth = 0; + JSObject* globalObject = 0; + bool requiresDynamicChecks = false; + if (generator.findScopedProperty(m_ident, index, depth, false, requiresDynamicChecks, globalObject) && index != missingSymbolMarker() && !requiresDynamicChecks) { + RefPtr<RegisterID> func = generator.emitGetScopedVar(generator.newTemporary(), depth, index, globalObject); + CallArguments callArguments(generator, m_args); + generator.emitLoad(callArguments.thisRegister(), jsUndefined()); + return generator.emitCall(generator.finalDestinationOrIgnored(dst, func.get()), func.get(), callArguments, divot(), startOffset(), endOffset()); + } + + RefPtr<RegisterID> func = generator.newTemporary(); + CallArguments callArguments(generator, m_args); + int identifierStart = divot() - startOffset(); + generator.emitExpressionInfo(identifierStart + m_ident.length(), m_ident.length(), 0); + generator.emitResolveWithThis(callArguments.thisRegister(), func.get(), m_ident); + return generator.emitCall(generator.finalDestinationOrIgnored(dst, func.get()), func.get(), callArguments, divot(), startOffset(), endOffset()); +} + +// ------------------------------ FunctionCallBracketNode ---------------------------------- + +RegisterID* FunctionCallBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNode(m_base); + RegisterID* property = generator.emitNode(m_subscript); + generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> function = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property); + CallArguments callArguments(generator, m_args); + generator.emitMove(callArguments.thisRegister(), base.get()); + return generator.emitCall(generator.finalDestinationOrIgnored(dst, function.get()), function.get(), callArguments, divot(), startOffset(), endOffset()); +} + +// ------------------------------ FunctionCallDotNode ---------------------------------- + +RegisterID* FunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> function = generator.tempDestination(dst); + CallArguments callArguments(generator, m_args); + generator.emitNode(callArguments.thisRegister(), m_base); + generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); + generator.emitMethodCheck(); + generator.emitGetById(function.get(), callArguments.thisRegister(), m_ident); + return generator.emitCall(generator.finalDestinationOrIgnored(dst, function.get()), function.get(), callArguments, divot(), startOffset(), endOffset()); +} + +RegisterID* CallFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<Label> realCall = generator.newLabel(); + RefPtr<Label> end = generator.newLabel(); + RefPtr<RegisterID> base = generator.emitNode(m_base); + generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> function = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident); + RefPtr<RegisterID> finalDestinationOrIgnored = generator.finalDestinationOrIgnored(dst, function.get()); + generator.emitJumpIfNotFunctionCall(function.get(), realCall.get()); + { + if (m_args->m_listNode && m_args->m_listNode->m_expr) { + ArgumentListNode* oldList = m_args->m_listNode; + m_args->m_listNode = m_args->m_listNode->m_next; + + RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get()); + CallArguments callArguments(generator, m_args); + generator.emitNode(callArguments.thisRegister(), oldList->m_expr); + generator.emitCall(finalDestinationOrIgnored.get(), realFunction.get(), callArguments, divot(), startOffset(), endOffset()); + generator.emitJump(end.get()); + + m_args->m_listNode = oldList; + } else { + RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get()); + CallArguments callArguments(generator, m_args); + generator.emitLoad(callArguments.thisRegister(), jsUndefined()); + generator.emitCall(finalDestinationOrIgnored.get(), realFunction.get(), callArguments, divot(), startOffset(), endOffset()); + generator.emitJump(end.get()); + } + } + generator.emitLabel(realCall.get()); + { + CallArguments callArguments(generator, m_args); + generator.emitMove(callArguments.thisRegister(), base.get()); + generator.emitCall(finalDestinationOrIgnored.get(), function.get(), callArguments, divot(), startOffset(), endOffset()); + } + generator.emitLabel(end.get()); + return finalDestinationOrIgnored.get(); +} + +static bool areTrivialApplyArguments(ArgumentsNode* args) +{ + return !args->m_listNode || !args->m_listNode->m_expr || !args->m_listNode->m_next + || (!args->m_listNode->m_next->m_next && args->m_listNode->m_next->m_expr->isSimpleArray()); +} + +RegisterID* ApplyFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + // A few simple cases can be trivially handled as ordinary function calls. + // function.apply(), function.apply(arg) -> identical to function.call + // function.apply(thisArg, [arg0, arg1, ...]) -> can be trivially coerced into function.call(thisArg, arg0, arg1, ...) and saves object allocation + bool mayBeCall = areTrivialApplyArguments(m_args); + + RefPtr<Label> realCall = generator.newLabel(); + RefPtr<Label> end = generator.newLabel(); + RefPtr<RegisterID> base = generator.emitNode(m_base); + generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> function = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident); + RefPtr<RegisterID> finalDestinationOrIgnored = generator.finalDestinationOrIgnored(dst, function.get()); + generator.emitJumpIfNotFunctionApply(function.get(), realCall.get()); + { + if (mayBeCall) { + if (m_args->m_listNode && m_args->m_listNode->m_expr) { + ArgumentListNode* oldList = m_args->m_listNode; + if (m_args->m_listNode->m_next) { + ASSERT(m_args->m_listNode->m_next->m_expr->isSimpleArray()); + ASSERT(!m_args->m_listNode->m_next->m_next); + m_args->m_listNode = static_cast<ArrayNode*>(m_args->m_listNode->m_next->m_expr)->toArgumentList(generator.globalData(), 0); + RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get()); + CallArguments callArguments(generator, m_args); + generator.emitNode(callArguments.thisRegister(), oldList->m_expr); + generator.emitCall(finalDestinationOrIgnored.get(), realFunction.get(), callArguments, divot(), startOffset(), endOffset()); + } else { + m_args->m_listNode = m_args->m_listNode->m_next; + RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get()); + CallArguments callArguments(generator, m_args); + generator.emitNode(callArguments.thisRegister(), oldList->m_expr); + generator.emitCall(finalDestinationOrIgnored.get(), realFunction.get(), callArguments, divot(), startOffset(), endOffset()); + } + m_args->m_listNode = oldList; + } else { + RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get()); + CallArguments callArguments(generator, m_args); + generator.emitLoad(callArguments.thisRegister(), jsUndefined()); + generator.emitCall(finalDestinationOrIgnored.get(), realFunction.get(), callArguments, divot(), startOffset(), endOffset()); + } + } else { + ASSERT(m_args->m_listNode && m_args->m_listNode->m_next); + RefPtr<RegisterID> profileHookRegister; + if (generator.shouldEmitProfileHooks()) + profileHookRegister = generator.newTemporary(); + RefPtr<RegisterID> thisRegister = generator.emitNode(m_args->m_listNode->m_expr); + RefPtr<RegisterID> argsRegister; + ArgumentListNode* args = m_args->m_listNode->m_next; + if (args->m_expr->isResolveNode() && generator.willResolveToArguments(static_cast<ResolveNode*>(args->m_expr)->identifier())) + argsRegister = generator.uncheckedRegisterForArguments(); + else + argsRegister = generator.emitNode(args->m_expr); + + // Function.prototype.apply ignores extra arguments, but we still + // need to evaluate them for side effects. + while ((args = args->m_next)) + generator.emitNode(args->m_expr); + + generator.emitCallVarargs(finalDestinationOrIgnored.get(), base.get(), thisRegister.get(), argsRegister.get(), generator.newTemporary(), profileHookRegister.get(), divot(), startOffset(), endOffset()); + } + generator.emitJump(end.get()); + } + generator.emitLabel(realCall.get()); + { + CallArguments callArguments(generator, m_args); + generator.emitMove(callArguments.thisRegister(), base.get()); + generator.emitCall(finalDestinationOrIgnored.get(), function.get(), callArguments, divot(), startOffset(), endOffset()); + } + generator.emitLabel(end.get()); + return finalDestinationOrIgnored.get(); +} + +// ------------------------------ PostfixResolveNode ---------------------------------- + +static RegisterID* emitPreIncOrDec(BytecodeGenerator& generator, RegisterID* srcDst, Operator oper) +{ + return (oper == OpPlusPlus) ? generator.emitPreInc(srcDst) : generator.emitPreDec(srcDst); +} + +static RegisterID* emitPostIncOrDec(BytecodeGenerator& generator, RegisterID* dst, RegisterID* srcDst, Operator oper) +{ + if (srcDst == dst) + return generator.emitToJSNumber(dst, srcDst); + return (oper == OpPlusPlus) ? generator.emitPostInc(dst, srcDst) : generator.emitPostDec(dst, srcDst); +} + +RegisterID* PostfixResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (RegisterID* local = generator.registerFor(m_ident)) { + if (generator.isLocalConstant(m_ident)) { + if (dst == generator.ignoredResult()) + return 0; + return generator.emitToJSNumber(generator.finalDestination(dst), local); + } + + if (dst == generator.ignoredResult()) + return emitPreIncOrDec(generator, local, m_operator); + return emitPostIncOrDec(generator, generator.finalDestination(dst), local, m_operator); + } + + int index = 0; + size_t depth = 0; + JSObject* globalObject = 0; + bool requiresDynamicChecks = false; + if (generator.findScopedProperty(m_ident, index, depth, true, requiresDynamicChecks, globalObject) && index != missingSymbolMarker() && !requiresDynamicChecks) { + RefPtr<RegisterID> value = generator.emitGetScopedVar(generator.newTemporary(), depth, index, globalObject); + RegisterID* oldValue; + if (dst == generator.ignoredResult()) { + oldValue = 0; + emitPreIncOrDec(generator, value.get(), m_operator); + } else { + oldValue = emitPostIncOrDec(generator, generator.finalDestination(dst), value.get(), m_operator); + } + generator.emitPutScopedVar(depth, index, value.get(), globalObject); + return oldValue; + } + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + RefPtr<RegisterID> value = generator.newTemporary(); + RefPtr<RegisterID> base = generator.emitResolveWithBase(generator.newTemporary(), value.get(), m_ident); + RegisterID* oldValue; + if (dst == generator.ignoredResult()) { + oldValue = 0; + emitPreIncOrDec(generator, value.get(), m_operator); + } else { + oldValue = emitPostIncOrDec(generator, generator.finalDestination(dst), value.get(), m_operator); + } + generator.emitPutById(base.get(), m_ident, value.get()); + return oldValue; +} + +// ------------------------------ PostfixBracketNode ---------------------------------- + +RegisterID* PostfixBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNode(m_base); + RefPtr<RegisterID> property = generator.emitNode(m_subscript); + + generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> value = generator.emitGetByVal(generator.newTemporary(), base.get(), property.get()); + RegisterID* oldValue; + if (dst == generator.ignoredResult()) { + oldValue = 0; + if (m_operator == OpPlusPlus) + generator.emitPreInc(value.get()); + else + generator.emitPreDec(value.get()); + } else { + oldValue = (m_operator == OpPlusPlus) ? generator.emitPostInc(generator.finalDestination(dst), value.get()) : generator.emitPostDec(generator.finalDestination(dst), value.get()); + } + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitPutByVal(base.get(), property.get(), value.get()); + return oldValue; +} + +// ------------------------------ PostfixDotNode ---------------------------------- + +RegisterID* PostfixDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNode(m_base); + + generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> value = generator.emitGetById(generator.newTemporary(), base.get(), m_ident); + RegisterID* oldValue; + if (dst == generator.ignoredResult()) { + oldValue = 0; + if (m_operator == OpPlusPlus) + generator.emitPreInc(value.get()); + else + generator.emitPreDec(value.get()); + } else { + oldValue = (m_operator == OpPlusPlus) ? generator.emitPostInc(generator.finalDestination(dst), value.get()) : generator.emitPostDec(generator.finalDestination(dst), value.get()); + } + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitPutById(base.get(), m_ident, value.get()); + return oldValue; +} + +// ------------------------------ PostfixErrorNode ----------------------------------- + +RegisterID* PostfixErrorNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + return emitThrowReferenceError(generator, m_operator == OpPlusPlus + ? "Postfix ++ operator applied to value that is not a reference." + : "Postfix -- operator applied to value that is not a reference."); +} + +// ------------------------------ DeleteResolveNode ----------------------------------- + +RegisterID* DeleteResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (generator.registerFor(m_ident)) + return generator.emitLoad(generator.finalDestination(dst), false); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + RegisterID* base = generator.emitResolveBase(generator.tempDestination(dst), m_ident); + return generator.emitDeleteById(generator.finalDestination(dst, base), base, m_ident); +} + +// ------------------------------ DeleteBracketNode ----------------------------------- + +RegisterID* DeleteBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> r0 = generator.emitNode(m_base); + RegisterID* r1 = generator.emitNode(m_subscript); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitDeleteByVal(generator.finalDestination(dst), r0.get(), r1); +} + +// ------------------------------ DeleteDotNode ----------------------------------- + +RegisterID* DeleteDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RegisterID* r0 = generator.emitNode(m_base); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitDeleteById(generator.finalDestination(dst), r0, m_ident); +} + +// ------------------------------ DeleteValueNode ----------------------------------- + +RegisterID* DeleteValueNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitNode(generator.ignoredResult(), m_expr); + + // delete on a non-location expression ignores the value and returns true + return generator.emitLoad(generator.finalDestination(dst), true); +} + +// ------------------------------ VoidNode ------------------------------------- + +RegisterID* VoidNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (dst == generator.ignoredResult()) { + generator.emitNode(generator.ignoredResult(), m_expr); + return 0; + } + RefPtr<RegisterID> r0 = generator.emitNode(m_expr); + return generator.emitLoad(dst, jsUndefined()); +} + +// ------------------------------ TypeOfValueNode ----------------------------------- + +RegisterID* TypeOfResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (RegisterID* local = generator.registerFor(m_ident)) { + if (dst == generator.ignoredResult()) + return 0; + return generator.emitTypeOf(generator.finalDestination(dst), local); + } + + RefPtr<RegisterID> scratch = generator.emitResolveBase(generator.tempDestination(dst), m_ident); + generator.emitGetById(scratch.get(), scratch.get(), m_ident); + if (dst == generator.ignoredResult()) + return 0; + return generator.emitTypeOf(generator.finalDestination(dst, scratch.get()), scratch.get()); +} + +// ------------------------------ TypeOfValueNode ----------------------------------- + +RegisterID* TypeOfValueNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (dst == generator.ignoredResult()) { + generator.emitNode(generator.ignoredResult(), m_expr); + return 0; + } + RefPtr<RegisterID> src = generator.emitNode(m_expr); + return generator.emitTypeOf(generator.finalDestination(dst), src.get()); +} + +// ------------------------------ PrefixResolveNode ---------------------------------- + +RegisterID* PrefixResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (RegisterID* local = generator.registerFor(m_ident)) { + if (generator.isLocalConstant(m_ident)) { + if (dst == generator.ignoredResult()) + return 0; + RefPtr<RegisterID> r0 = generator.emitLoad(generator.finalDestination(dst), (m_operator == OpPlusPlus) ? 1.0 : -1.0); + return generator.emitBinaryOp(op_add, r0.get(), local, r0.get(), OperandTypes()); + } + + emitPreIncOrDec(generator, local, m_operator); + return generator.moveToDestinationIfNeeded(dst, local); + } + + int index = 0; + size_t depth = 0; + JSObject* globalObject = 0; + bool requiresDynamicChecks = false; + if (generator.findScopedProperty(m_ident, index, depth, true, requiresDynamicChecks, globalObject) && index != missingSymbolMarker() && !requiresDynamicChecks) { + RefPtr<RegisterID> propDst = generator.emitGetScopedVar(generator.tempDestination(dst), depth, index, globalObject); + emitPreIncOrDec(generator, propDst.get(), m_operator); + generator.emitPutScopedVar(depth, index, propDst.get(), globalObject); + return generator.moveToDestinationIfNeeded(dst, propDst.get()); + } + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + RefPtr<RegisterID> propDst = generator.tempDestination(dst); + RefPtr<RegisterID> base = generator.emitResolveWithBase(generator.newTemporary(), propDst.get(), m_ident); + emitPreIncOrDec(generator, propDst.get(), m_operator); + generator.emitPutById(base.get(), m_ident, propDst.get()); + return generator.moveToDestinationIfNeeded(dst, propDst.get()); +} + +// ------------------------------ PrefixBracketNode ---------------------------------- + +RegisterID* PrefixBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNode(m_base); + RefPtr<RegisterID> property = generator.emitNode(m_subscript); + RefPtr<RegisterID> propDst = generator.tempDestination(dst); + + generator.emitExpressionInfo(divot() + m_subexpressionDivotOffset, m_subexpressionStartOffset, endOffset() - m_subexpressionDivotOffset); + RegisterID* value = generator.emitGetByVal(propDst.get(), base.get(), property.get()); + if (m_operator == OpPlusPlus) + generator.emitPreInc(value); + else + generator.emitPreDec(value); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitPutByVal(base.get(), property.get(), value); + return generator.moveToDestinationIfNeeded(dst, propDst.get()); +} + +// ------------------------------ PrefixDotNode ---------------------------------- + +RegisterID* PrefixDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNode(m_base); + RefPtr<RegisterID> propDst = generator.tempDestination(dst); + + generator.emitExpressionInfo(divot() + m_subexpressionDivotOffset, m_subexpressionStartOffset, endOffset() - m_subexpressionDivotOffset); + RegisterID* value = generator.emitGetById(propDst.get(), base.get(), m_ident); + if (m_operator == OpPlusPlus) + generator.emitPreInc(value); + else + generator.emitPreDec(value); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitPutById(base.get(), m_ident, value); + return generator.moveToDestinationIfNeeded(dst, propDst.get()); +} + +// ------------------------------ PrefixErrorNode ----------------------------------- + +RegisterID* PrefixErrorNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + return emitThrowReferenceError(generator, m_operator == OpPlusPlus + ? "Prefix ++ operator applied to value that is not a reference." + : "Prefix -- operator applied to value that is not a reference."); +} + +// ------------------------------ Unary Operation Nodes ----------------------------------- + +RegisterID* UnaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RegisterID* src = generator.emitNode(m_expr); + return generator.emitUnaryOp(opcodeID(), generator.finalDestination(dst), src); +} + + +// ------------------------------ LogicalNotNode ----------------------------------- + +void LogicalNotNode::emitBytecodeInConditionContext(BytecodeGenerator& generator, Label* trueTarget, Label* falseTarget, bool fallThroughMeansTrue) +{ + ASSERT(expr()->hasConditionContextCodegen()); + + // reverse the true and false targets + generator.emitNodeInConditionContext(expr(), falseTarget, trueTarget, !fallThroughMeansTrue); +} + + +// ------------------------------ Binary Operation Nodes ----------------------------------- + +// BinaryOpNode::emitStrcat: +// +// This node generates an op_strcat operation. This opcode can handle concatenation of three or +// more values, where we can determine a set of separate op_add operations would be operating on +// string values. +// +// This function expects to be operating on a graph of AST nodes looking something like this: +// +// (a)... (b) +// \ / +// (+) (c) +// \ / +// [d] ((+)) +// \ / +// [+=] +// +// The assignment operation is optional, if it exists the register holding the value on the +// lefthand side of the assignment should be passing as the optional 'lhs' argument. +// +// The method should be called on the node at the root of the tree of regular binary add +// operations (marked in the diagram with a double set of parentheses). This node must +// be performing a string concatenation (determined by statically detecting that at least +// one child must be a string). +// +// Since the minimum number of values being concatenated together is expected to be 3, if +// a lhs to a concatenating assignment is not provided then the root add should have at +// least one left child that is also an add that can be determined to be operating on strings. +// +RegisterID* BinaryOpNode::emitStrcat(BytecodeGenerator& generator, RegisterID* dst, RegisterID* lhs, ReadModifyResolveNode* emitExpressionInfoForMe) +{ + ASSERT(isAdd()); + ASSERT(resultDescriptor().definitelyIsString()); + + // Create a list of expressions for all the adds in the tree of nodes we can convert into + // a string concatenation. The rightmost node (c) is added first. The rightmost node is + // added first, and the leftmost child is never added, so the vector produced for the + // example above will be [ c, b ]. + Vector<ExpressionNode*, 16> reverseExpressionList; + reverseExpressionList.append(m_expr2); + + // Examine the left child of the add. So long as this is a string add, add its right-child + // to the list, and keep processing along the left fork. + ExpressionNode* leftMostAddChild = m_expr1; + while (leftMostAddChild->isAdd() && leftMostAddChild->resultDescriptor().definitelyIsString()) { + reverseExpressionList.append(static_cast<AddNode*>(leftMostAddChild)->m_expr2); + leftMostAddChild = static_cast<AddNode*>(leftMostAddChild)->m_expr1; + } + + Vector<RefPtr<RegisterID>, 16> temporaryRegisters; + + // If there is an assignment, allocate a temporary to hold the lhs after conversion. + // We could possibly avoid this (the lhs is converted last anyway, we could let the + // op_strcat node handle its conversion if required). + if (lhs) + temporaryRegisters.append(generator.newTemporary()); + + // Emit code for the leftmost node ((a) in the example). + temporaryRegisters.append(generator.newTemporary()); + RegisterID* leftMostAddChildTempRegister = temporaryRegisters.last().get(); + generator.emitNode(leftMostAddChildTempRegister, leftMostAddChild); + + // Note on ordering of conversions: + // + // We maintain the same ordering of conversions as we would see if the concatenations + // was performed as a sequence of adds (otherwise this optimization could change + // behaviour should an object have been provided a valueOf or toString method). + // + // Considering the above example, the sequnce of execution is: + // * evaluate operand (a) + // * evaluate operand (b) + // * convert (a) to primitive <- (this would be triggered by the first add) + // * convert (b) to primitive <- (ditto) + // * evaluate operand (c) + // * convert (c) to primitive <- (this would be triggered by the second add) + // And optionally, if there is an assignment: + // * convert (d) to primitive <- (this would be triggered by the assigning addition) + // + // As such we do not plant an op to convert the leftmost child now. Instead, use + // 'leftMostAddChildTempRegister' as a flag to trigger generation of the conversion + // once the second node has been generated. However, if the leftmost child is an + // immediate we can trivially determine that no conversion will be required. + // If this is the case + if (leftMostAddChild->isString()) + leftMostAddChildTempRegister = 0; + + while (reverseExpressionList.size()) { + ExpressionNode* node = reverseExpressionList.last(); + reverseExpressionList.removeLast(); + + // Emit the code for the current node. + temporaryRegisters.append(generator.newTemporary()); + generator.emitNode(temporaryRegisters.last().get(), node); + + // On the first iteration of this loop, when we first reach this point we have just + // generated the second node, which means it is time to convert the leftmost operand. + if (leftMostAddChildTempRegister) { + generator.emitToPrimitive(leftMostAddChildTempRegister, leftMostAddChildTempRegister); + leftMostAddChildTempRegister = 0; // Only do this once. + } + // Plant a conversion for this node, if necessary. + if (!node->isString()) + generator.emitToPrimitive(temporaryRegisters.last().get(), temporaryRegisters.last().get()); + } + ASSERT(temporaryRegisters.size() >= 3); + + // Certain read-modify nodes require expression info to be emitted *after* m_right has been generated. + // If this is required the node is passed as 'emitExpressionInfoForMe'; do so now. + if (emitExpressionInfoForMe) + generator.emitExpressionInfo(emitExpressionInfoForMe->divot(), emitExpressionInfoForMe->startOffset(), emitExpressionInfoForMe->endOffset()); + + // If there is an assignment convert the lhs now. This will also copy lhs to + // the temporary register we allocated for it. + if (lhs) + generator.emitToPrimitive(temporaryRegisters[0].get(), lhs); + + return generator.emitStrcat(generator.finalDestination(dst, temporaryRegisters[0].get()), temporaryRegisters[0].get(), temporaryRegisters.size()); +} + +RegisterID* BinaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + OpcodeID opcodeID = this->opcodeID(); + + if (opcodeID == op_add && m_expr1->isAdd() && m_expr1->resultDescriptor().definitelyIsString()) + return emitStrcat(generator, dst); + + if (opcodeID == op_neq) { + if (m_expr1->isNull() || m_expr2->isNull()) { + RefPtr<RegisterID> src = generator.tempDestination(dst); + generator.emitNode(src.get(), m_expr1->isNull() ? m_expr2 : m_expr1); + return generator.emitUnaryOp(op_neq_null, generator.finalDestination(dst, src.get()), src.get()); + } + } + + RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); + RegisterID* src2 = generator.emitNode(m_expr2); + return generator.emitBinaryOp(opcodeID, generator.finalDestination(dst, src1.get()), src1.get(), src2, OperandTypes(m_expr1->resultDescriptor(), m_expr2->resultDescriptor())); +} + +RegisterID* EqualNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (m_expr1->isNull() || m_expr2->isNull()) { + RefPtr<RegisterID> src = generator.tempDestination(dst); + generator.emitNode(src.get(), m_expr1->isNull() ? m_expr2 : m_expr1); + return generator.emitUnaryOp(op_eq_null, generator.finalDestination(dst, src.get()), src.get()); + } + + RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); + RegisterID* src2 = generator.emitNode(m_expr2); + return generator.emitEqualityOp(op_eq, generator.finalDestination(dst, src1.get()), src1.get(), src2); +} + +RegisterID* StrictEqualNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); + RegisterID* src2 = generator.emitNode(m_expr2); + return generator.emitEqualityOp(op_stricteq, generator.finalDestination(dst, src1.get()), src1.get(), src2); +} + +RegisterID* ThrowableBinaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); + RegisterID* src2 = generator.emitNode(m_expr2); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitBinaryOp(opcodeID(), generator.finalDestination(dst, src1.get()), src1.get(), src2, OperandTypes(m_expr1->resultDescriptor(), m_expr2->resultDescriptor())); +} + +RegisterID* InstanceOfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); + RefPtr<RegisterID> src2 = generator.emitNode(m_expr2); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitCheckHasInstance(src2.get()); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + RegisterID* src2Prototype = generator.emitGetById(generator.newTemporary(), src2.get(), generator.globalData()->propertyNames->prototype); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitInstanceOf(generator.finalDestination(dst, src1.get()), src1.get(), src2.get(), src2Prototype); +} + +// ------------------------------ LogicalOpNode ---------------------------- + +RegisterID* LogicalOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> temp = generator.tempDestination(dst); + RefPtr<Label> target = generator.newLabel(); + + generator.emitNode(temp.get(), m_expr1); + if (m_operator == OpLogicalAnd) + generator.emitJumpIfFalse(temp.get(), target.get()); + else + generator.emitJumpIfTrue(temp.get(), target.get()); + generator.emitNode(temp.get(), m_expr2); + generator.emitLabel(target.get()); + + return generator.moveToDestinationIfNeeded(dst, temp.get()); +} + +void LogicalOpNode::emitBytecodeInConditionContext(BytecodeGenerator& generator, Label* trueTarget, Label* falseTarget, bool fallThroughMeansTrue) +{ + if (m_expr1->hasConditionContextCodegen()) { + RefPtr<Label> afterExpr1 = generator.newLabel(); + if (m_operator == OpLogicalAnd) + generator.emitNodeInConditionContext(m_expr1, afterExpr1.get(), falseTarget, true); + else + generator.emitNodeInConditionContext(m_expr1, trueTarget, afterExpr1.get(), false); + generator.emitLabel(afterExpr1.get()); + } else { + RegisterID* temp = generator.emitNode(m_expr1); + if (m_operator == OpLogicalAnd) + generator.emitJumpIfFalse(temp, falseTarget); + else + generator.emitJumpIfTrue(temp, trueTarget); + } + + if (m_expr2->hasConditionContextCodegen()) + generator.emitNodeInConditionContext(m_expr2, trueTarget, falseTarget, fallThroughMeansTrue); + else { + RegisterID* temp = generator.emitNode(m_expr2); + if (fallThroughMeansTrue) + generator.emitJumpIfFalse(temp, falseTarget); + else + generator.emitJumpIfTrue(temp, trueTarget); + } +} + +// ------------------------------ ConditionalNode ------------------------------ + +RegisterID* ConditionalNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> newDst = generator.finalDestination(dst); + RefPtr<Label> beforeElse = generator.newLabel(); + RefPtr<Label> afterElse = generator.newLabel(); + + if (m_logical->hasConditionContextCodegen()) { + RefPtr<Label> beforeThen = generator.newLabel(); + generator.emitNodeInConditionContext(m_logical, beforeThen.get(), beforeElse.get(), true); + generator.emitLabel(beforeThen.get()); + } else { + RegisterID* cond = generator.emitNode(m_logical); + generator.emitJumpIfFalse(cond, beforeElse.get()); + } + + generator.emitNode(newDst.get(), m_expr1); + generator.emitJump(afterElse.get()); + + generator.emitLabel(beforeElse.get()); + generator.emitNode(newDst.get(), m_expr2); + + generator.emitLabel(afterElse.get()); + + return newDst.get(); +} + +// ------------------------------ ReadModifyResolveNode ----------------------------------- + +// FIXME: should this be moved to be a method on BytecodeGenerator? +static ALWAYS_INLINE RegisterID* emitReadModifyAssignment(BytecodeGenerator& generator, RegisterID* dst, RegisterID* src1, ExpressionNode* m_right, Operator oper, OperandTypes types, ReadModifyResolveNode* emitExpressionInfoForMe = 0) +{ + OpcodeID opcodeID; + switch (oper) { + case OpMultEq: + opcodeID = op_mul; + break; + case OpDivEq: + opcodeID = op_div; + break; + case OpPlusEq: + if (m_right->isAdd() && m_right->resultDescriptor().definitelyIsString()) + return static_cast<AddNode*>(m_right)->emitStrcat(generator, dst, src1, emitExpressionInfoForMe); + opcodeID = op_add; + break; + case OpMinusEq: + opcodeID = op_sub; + break; + case OpLShift: + opcodeID = op_lshift; + break; + case OpRShift: + opcodeID = op_rshift; + break; + case OpURShift: + opcodeID = op_urshift; + break; + case OpAndEq: + opcodeID = op_bitand; + break; + case OpXOrEq: + opcodeID = op_bitxor; + break; + case OpOrEq: + opcodeID = op_bitor; + break; + case OpModEq: + opcodeID = op_mod; + break; + default: + ASSERT_NOT_REACHED(); + return dst; + } + + RegisterID* src2 = generator.emitNode(m_right); + + // Certain read-modify nodes require expression info to be emitted *after* m_right has been generated. + // If this is required the node is passed as 'emitExpressionInfoForMe'; do so now. + if (emitExpressionInfoForMe) + generator.emitExpressionInfo(emitExpressionInfoForMe->divot(), emitExpressionInfoForMe->startOffset(), emitExpressionInfoForMe->endOffset()); + + return generator.emitBinaryOp(opcodeID, dst, src1, src2, types); +} + +RegisterID* ReadModifyResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (RegisterID* local = generator.registerFor(m_ident)) { + if (generator.isLocalConstant(m_ident)) { + return emitReadModifyAssignment(generator, generator.finalDestination(dst), local, m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); + } + + if (generator.leftHandSideNeedsCopy(m_rightHasAssignments, m_right->isPure(generator))) { + RefPtr<RegisterID> result = generator.newTemporary(); + generator.emitMove(result.get(), local); + emitReadModifyAssignment(generator, result.get(), result.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); + generator.emitMove(local, result.get()); + return generator.moveToDestinationIfNeeded(dst, result.get()); + } + + RegisterID* result = emitReadModifyAssignment(generator, local, local, m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); + return generator.moveToDestinationIfNeeded(dst, result); + } + + int index = 0; + size_t depth = 0; + JSObject* globalObject = 0; + bool requiresDynamicChecks = false; + if (generator.findScopedProperty(m_ident, index, depth, true, requiresDynamicChecks, globalObject) && index != missingSymbolMarker() && !requiresDynamicChecks) { + RefPtr<RegisterID> src1 = generator.emitGetScopedVar(generator.tempDestination(dst), depth, index, globalObject); + RegisterID* result = emitReadModifyAssignment(generator, generator.finalDestination(dst, src1.get()), src1.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); + generator.emitPutScopedVar(depth, index, result, globalObject); + return result; + } + + RefPtr<RegisterID> src1 = generator.tempDestination(dst); + generator.emitExpressionInfo(divot() - startOffset() + m_ident.length(), m_ident.length(), 0); + RefPtr<RegisterID> base = generator.emitResolveWithBase(generator.newTemporary(), src1.get(), m_ident); + RegisterID* result = emitReadModifyAssignment(generator, generator.finalDestination(dst, src1.get()), src1.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor()), this); + return generator.emitPutById(base.get(), m_ident, result); +} + +// ------------------------------ AssignResolveNode ----------------------------------- + +RegisterID* AssignResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (RegisterID* local = generator.registerFor(m_ident)) { + if (generator.isLocalConstant(m_ident)) + return generator.emitNode(dst, m_right); + + RegisterID* result = generator.emitNode(local, m_right); + return generator.moveToDestinationIfNeeded(dst, result); + } + + int index = 0; + size_t depth = 0; + JSObject* globalObject = 0; + bool requiresDynamicChecks = false; + if (generator.findScopedProperty(m_ident, index, depth, true, requiresDynamicChecks, globalObject) && index != missingSymbolMarker() && !requiresDynamicChecks) { + if (dst == generator.ignoredResult()) + dst = 0; + RegisterID* value = generator.emitNode(dst, m_right); + generator.emitPutScopedVar(depth, index, value, globalObject); + return value; + } + + RefPtr<RegisterID> base = generator.emitResolveBaseForPut(generator.newTemporary(), m_ident); + if (dst == generator.ignoredResult()) + dst = 0; + RegisterID* value = generator.emitNode(dst, m_right); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitPutById(base.get(), m_ident, value); +} + +// ------------------------------ AssignDotNode ----------------------------------- + +RegisterID* AssignDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_rightHasAssignments, m_right->isPure(generator)); + RefPtr<RegisterID> value = generator.destinationForAssignResult(dst); + RegisterID* result = generator.emitNode(value.get(), m_right); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + RegisterID* forwardResult = (dst == generator.ignoredResult()) ? result : generator.moveToDestinationIfNeeded(generator.tempDestination(result), result); + generator.emitPutById(base.get(), m_ident, forwardResult); + return generator.moveToDestinationIfNeeded(dst, forwardResult); +} + +// ------------------------------ ReadModifyDotNode ----------------------------------- + +RegisterID* ReadModifyDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_rightHasAssignments, m_right->isPure(generator)); + + generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> value = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident); + RegisterID* updatedValue = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitPutById(base.get(), m_ident, updatedValue); +} + +// ------------------------------ AssignErrorNode ----------------------------------- + +RegisterID* AssignErrorNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + return emitThrowReferenceError(generator, "Left side of assignment is not a reference."); +} + +// ------------------------------ AssignBracketNode ----------------------------------- + +RegisterID* AssignBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments || m_rightHasAssignments, m_subscript->isPure(generator) && m_right->isPure(generator)); + RefPtr<RegisterID> property = generator.emitNodeForLeftHandSide(m_subscript, m_rightHasAssignments, m_right->isPure(generator)); + RefPtr<RegisterID> value = generator.destinationForAssignResult(dst); + RegisterID* result = generator.emitNode(value.get(), m_right); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + RegisterID* forwardResult = (dst == generator.ignoredResult()) ? result : generator.moveToDestinationIfNeeded(generator.tempDestination(result), result); + generator.emitPutByVal(base.get(), property.get(), forwardResult); + return generator.moveToDestinationIfNeeded(dst, forwardResult); +} + +// ------------------------------ ReadModifyBracketNode ----------------------------------- + +RegisterID* ReadModifyBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments || m_rightHasAssignments, m_subscript->isPure(generator) && m_right->isPure(generator)); + RefPtr<RegisterID> property = generator.emitNodeForLeftHandSide(m_subscript, m_rightHasAssignments, m_right->isPure(generator)); + + generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> value = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property.get()); + RegisterID* updatedValue = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitPutByVal(base.get(), property.get(), updatedValue); + + return updatedValue; +} + +// ------------------------------ CommaNode ------------------------------------ + +RegisterID* CommaNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + ASSERT(m_expressions.size() > 1); + for (size_t i = 0; i < m_expressions.size() - 1; i++) + generator.emitNode(generator.ignoredResult(), m_expressions[i]); + return generator.emitNode(dst, m_expressions.last()); +} + +// ------------------------------ ConstDeclNode ------------------------------------ + +RegisterID* ConstDeclNode::emitCodeSingle(BytecodeGenerator& generator) +{ + // FIXME: This code does not match the behavior of const in Firefox. + if (RegisterID* local = generator.constRegisterFor(m_ident)) { + if (!m_init) + return local; + + return generator.emitNode(local, m_init); + } + + RefPtr<RegisterID> value = m_init ? generator.emitNode(m_init) : generator.emitLoad(0, jsUndefined()); + + ScopeChainIterator iter = generator.scopeChain()->begin(); + ScopeChainIterator end = generator.scopeChain()->end(); + size_t depth = 0; + for (; iter != end; ++iter, ++depth) { + JSObject* currentScope = iter->get(); + if (!currentScope->isVariableObject()) + continue; + JSVariableObject* currentVariableObject = static_cast<JSVariableObject*>(currentScope); + SymbolTableEntry entry = currentVariableObject->symbolTable().get(m_ident.impl()); + if (entry.isNull()) + continue; + + return generator.emitPutScopedVar(generator.scopeDepth() + depth, entry.getIndex(), value.get(), currentVariableObject->isGlobalObject() ? currentVariableObject : 0); + } + + if (generator.codeType() != EvalCode) + return value.get(); + + // FIXME: While this code should only be hit in an eval block, it will assign + // to the wrong base if m_ident exists in an intervening with scope. + RefPtr<RegisterID> base = generator.emitResolveBase(generator.newTemporary(), m_ident); + return generator.emitPutById(base.get(), m_ident, value.get()); +} + +RegisterID* ConstDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + RegisterID* result = 0; + for (ConstDeclNode* n = this; n; n = n->m_next) + result = n->emitCodeSingle(generator); + + return result; +} + +// ------------------------------ ConstStatementNode ----------------------------- + +RegisterID* ConstStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + return generator.emitNode(m_next); +} + +// ------------------------------ SourceElements ------------------------------- + + +inline StatementNode* SourceElements::lastStatement() const +{ + size_t size = m_statements.size(); + return size ? m_statements[size - 1] : 0; +} + +inline void SourceElements::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + size_t size = m_statements.size(); + for (size_t i = 0; i < size; ++i) + generator.emitNode(dst, m_statements[i]); +} + +// ------------------------------ BlockNode ------------------------------------ + +inline StatementNode* BlockNode::lastStatement() const +{ + return m_statements ? m_statements->lastStatement() : 0; +} + +inline StatementNode* BlockNode::singleStatement() const +{ + return m_statements ? m_statements->singleStatement() : 0; +} + +RegisterID* BlockNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (m_statements) + m_statements->emitBytecode(generator, dst); + return 0; +} + +// ------------------------------ EmptyStatementNode --------------------------- + +RegisterID* EmptyStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + return dst; +} + +// ------------------------------ DebuggerStatementNode --------------------------- + +RegisterID* DebuggerStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(DidReachBreakpoint, firstLine(), lastLine()); + return dst; +} + +// ------------------------------ ExprStatementNode ---------------------------- + +RegisterID* ExprStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + ASSERT(m_expr); + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + return generator.emitNode(dst, m_expr); +} + +// ------------------------------ VarStatementNode ---------------------------- + +RegisterID* VarStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + ASSERT(m_expr); + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + return generator.emitNode(m_expr); +} + +// ------------------------------ IfNode --------------------------------------- + +RegisterID* IfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + RefPtr<Label> afterThen = generator.newLabel(); + + if (m_condition->hasConditionContextCodegen()) { + RefPtr<Label> beforeThen = generator.newLabel(); + generator.emitNodeInConditionContext(m_condition, beforeThen.get(), afterThen.get(), true); + generator.emitLabel(beforeThen.get()); + } else { + RegisterID* cond = generator.emitNode(m_condition); + generator.emitJumpIfFalse(cond, afterThen.get()); + } + + generator.emitNode(dst, m_ifBlock); + generator.emitLabel(afterThen.get()); + + // FIXME: This should return the last statement executed so that it can be returned as a Completion. + return 0; +} + +// ------------------------------ IfElseNode --------------------------------------- + +RegisterID* IfElseNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + RefPtr<Label> beforeElse = generator.newLabel(); + RefPtr<Label> afterElse = generator.newLabel(); + + if (m_condition->hasConditionContextCodegen()) { + RefPtr<Label> beforeThen = generator.newLabel(); + generator.emitNodeInConditionContext(m_condition, beforeThen.get(), beforeElse.get(), true); + generator.emitLabel(beforeThen.get()); + } else { + RegisterID* cond = generator.emitNode(m_condition); + generator.emitJumpIfFalse(cond, beforeElse.get()); + } + + generator.emitNode(dst, m_ifBlock); + generator.emitJump(afterElse.get()); + + generator.emitLabel(beforeElse.get()); + + generator.emitNode(dst, m_elseBlock); + + generator.emitLabel(afterElse.get()); + + // FIXME: This should return the last statement executed so that it can be returned as a Completion. + return 0; +} + +// ------------------------------ DoWhileNode ---------------------------------- + +RegisterID* DoWhileNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Loop); + + RefPtr<Label> topOfLoop = generator.newLabel(); + generator.emitLabel(topOfLoop.get()); + generator.emitLoopHint(); + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + RefPtr<RegisterID> result = generator.emitNode(dst, m_statement); + + generator.emitLabel(scope->continueTarget()); + generator.emitDebugHook(WillExecuteStatement, m_expr->lineNo(), m_expr->lineNo()); + if (m_expr->hasConditionContextCodegen()) + generator.emitNodeInConditionContext(m_expr, topOfLoop.get(), scope->breakTarget(), false); + else { + RegisterID* cond = generator.emitNode(m_expr); + generator.emitJumpIfTrue(cond, topOfLoop.get()); + } + + generator.emitLabel(scope->breakTarget()); + return result.get(); +} + +// ------------------------------ WhileNode ------------------------------------ + +RegisterID* WhileNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Loop); + + generator.emitJump(scope->continueTarget()); + + RefPtr<Label> topOfLoop = generator.newLabel(); + generator.emitLabel(topOfLoop.get()); + generator.emitLoopHint(); + + generator.emitNode(dst, m_statement); + + generator.emitLabel(scope->continueTarget()); + generator.emitDebugHook(WillExecuteStatement, m_expr->lineNo(), m_expr->lineNo()); + + if (m_expr->hasConditionContextCodegen()) + generator.emitNodeInConditionContext(m_expr, topOfLoop.get(), scope->breakTarget(), false); + else { + RegisterID* cond = generator.emitNode(m_expr); + generator.emitJumpIfTrue(cond, topOfLoop.get()); + } + + generator.emitLabel(scope->breakTarget()); + + // FIXME: This should return the last statement executed so that it can be returned as a Completion + return 0; +} + +// ------------------------------ ForNode -------------------------------------- + +RegisterID* ForNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Loop); + + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + if (m_expr1) + generator.emitNode(generator.ignoredResult(), m_expr1); + + RefPtr<Label> condition = generator.newLabel(); + generator.emitJump(condition.get()); + + RefPtr<Label> topOfLoop = generator.newLabel(); + generator.emitLabel(topOfLoop.get()); + generator.emitLoopHint(); + + RefPtr<RegisterID> result = generator.emitNode(dst, m_statement); + + generator.emitLabel(scope->continueTarget()); + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + if (m_expr3) + generator.emitNode(generator.ignoredResult(), m_expr3); + + generator.emitLabel(condition.get()); + if (m_expr2) { + if (m_expr2->hasConditionContextCodegen()) + generator.emitNodeInConditionContext(m_expr2, topOfLoop.get(), scope->breakTarget(), false); + else { + RegisterID* cond = generator.emitNode(m_expr2); + generator.emitJumpIfTrue(cond, topOfLoop.get()); + } + } else + generator.emitJump(topOfLoop.get()); + + generator.emitLabel(scope->breakTarget()); + return result.get(); +} + +// ------------------------------ ForInNode ------------------------------------ + +RegisterID* ForInNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Loop); + + if (!m_lexpr->isLocation()) + return emitThrowReferenceError(generator, "Left side of for-in statement is not a reference."); + + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + if (m_init) + generator.emitNode(generator.ignoredResult(), m_init); + + RefPtr<RegisterID> base = generator.newTemporary(); + generator.emitNode(base.get(), m_expr); + RefPtr<RegisterID> i = generator.newTemporary(); + RefPtr<RegisterID> size = generator.newTemporary(); + RefPtr<RegisterID> expectedSubscript; + RefPtr<RegisterID> iter = generator.emitGetPropertyNames(generator.newTemporary(), base.get(), i.get(), size.get(), scope->breakTarget()); + generator.emitJump(scope->continueTarget()); + + RefPtr<Label> loopStart = generator.newLabel(); + generator.emitLabel(loopStart.get()); + generator.emitLoopHint(); + + RegisterID* propertyName; + bool optimizedForinAccess = false; + if (m_lexpr->isResolveNode()) { + const Identifier& ident = static_cast<ResolveNode*>(m_lexpr)->identifier(); + propertyName = generator.registerFor(ident); + if (!propertyName) { + propertyName = generator.newTemporary(); + RefPtr<RegisterID> protect = propertyName; + RegisterID* base = generator.emitResolveBaseForPut(generator.newTemporary(), ident); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitPutById(base, ident, propertyName); + } else { + expectedSubscript = generator.emitMove(generator.newTemporary(), propertyName); + generator.pushOptimisedForIn(expectedSubscript.get(), iter.get(), i.get(), propertyName); + optimizedForinAccess = true; + } + } else if (m_lexpr->isDotAccessorNode()) { + DotAccessorNode* assignNode = static_cast<DotAccessorNode*>(m_lexpr); + const Identifier& ident = assignNode->identifier(); + propertyName = generator.newTemporary(); + RefPtr<RegisterID> protect = propertyName; + RegisterID* base = generator.emitNode(assignNode->base()); + + generator.emitExpressionInfo(assignNode->divot(), assignNode->startOffset(), assignNode->endOffset()); + generator.emitPutById(base, ident, propertyName); + } else { + ASSERT(m_lexpr->isBracketAccessorNode()); + BracketAccessorNode* assignNode = static_cast<BracketAccessorNode*>(m_lexpr); + propertyName = generator.newTemporary(); + RefPtr<RegisterID> protect = propertyName; + RefPtr<RegisterID> base = generator.emitNode(assignNode->base()); + RegisterID* subscript = generator.emitNode(assignNode->subscript()); + + generator.emitExpressionInfo(assignNode->divot(), assignNode->startOffset(), assignNode->endOffset()); + generator.emitPutByVal(base.get(), subscript, propertyName); + } + + generator.emitNode(dst, m_statement); + + if (optimizedForinAccess) + generator.popOptimisedForIn(); + + generator.emitLabel(scope->continueTarget()); + generator.emitNextPropertyName(propertyName, base.get(), i.get(), size.get(), iter.get(), loopStart.get()); + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + generator.emitLabel(scope->breakTarget()); + return dst; +} + +// ------------------------------ ContinueNode --------------------------------- + +// ECMA 12.7 +RegisterID* ContinueNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + LabelScope* scope = generator.continueTarget(m_ident); + ASSERT(scope); + + generator.emitJumpScopes(scope->continueTarget(), scope->scopeDepth()); + return dst; +} + +// ------------------------------ BreakNode ------------------------------------ + +// ECMA 12.8 +RegisterID* BreakNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + LabelScope* scope = generator.breakTarget(m_ident); + ASSERT(scope); + + generator.emitJumpScopes(scope->breakTarget(), scope->scopeDepth()); + return dst; +} + +// ------------------------------ ReturnNode ----------------------------------- + +RegisterID* ReturnNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + ASSERT(generator.codeType() == FunctionCode); + + if (dst == generator.ignoredResult()) + dst = 0; + RegisterID* r0 = m_value ? generator.emitNode(dst, m_value) : generator.emitLoad(dst, jsUndefined()); + RefPtr<RegisterID> returnRegister; + if (generator.scopeDepth()) { + RefPtr<Label> l0 = generator.newLabel(); + if (generator.hasFinaliser() && !r0->isTemporary()) { + returnRegister = generator.emitMove(generator.newTemporary(), r0); + r0 = returnRegister.get(); + } + generator.emitJumpScopes(l0.get(), 0); + generator.emitLabel(l0.get()); + } + generator.emitDebugHook(WillLeaveCallFrame, firstLine(), lastLine()); + return generator.emitReturn(r0); +} + +// ------------------------------ WithNode ------------------------------------- + +RegisterID* WithNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + RefPtr<RegisterID> scope = generator.newTemporary(); + generator.emitNode(scope.get(), m_expr); // scope must be protected until popped + generator.emitExpressionInfo(m_divot, m_expressionLength, 0); + generator.emitPushScope(scope.get()); + RegisterID* result = generator.emitNode(dst, m_statement); + generator.emitPopScope(); + return result; +} + +// ------------------------------ CaseClauseNode -------------------------------- + +inline void CaseClauseNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (m_statements) + m_statements->emitBytecode(generator, dst); +} + +// ------------------------------ CaseBlockNode -------------------------------- + +enum SwitchKind { + SwitchUnset = 0, + SwitchNumber = 1, + SwitchString = 2, + SwitchNeither = 3 +}; + +static void processClauseList(ClauseListNode* list, Vector<ExpressionNode*, 8>& literalVector, SwitchKind& typeForTable, bool& singleCharacterSwitch, int32_t& min_num, int32_t& max_num) +{ + for (; list; list = list->getNext()) { + ExpressionNode* clauseExpression = list->getClause()->expr(); + literalVector.append(clauseExpression); + if (clauseExpression->isNumber()) { + double value = static_cast<NumberNode*>(clauseExpression)->value(); + int32_t intVal = static_cast<int32_t>(value); + if ((typeForTable & ~SwitchNumber) || (intVal != value)) { + typeForTable = SwitchNeither; + break; + } + if (intVal < min_num) + min_num = intVal; + if (intVal > max_num) + max_num = intVal; + typeForTable = SwitchNumber; + continue; + } + if (clauseExpression->isString()) { + if (typeForTable & ~SwitchString) { + typeForTable = SwitchNeither; + break; + } + const UString& value = static_cast<StringNode*>(clauseExpression)->value().ustring(); + if (singleCharacterSwitch &= value.length() == 1) { + int32_t intVal = value[0]; + if (intVal < min_num) + min_num = intVal; + if (intVal > max_num) + max_num = intVal; + } + typeForTable = SwitchString; + continue; + } + typeForTable = SwitchNeither; + break; + } +} + +SwitchInfo::SwitchType CaseBlockNode::tryOptimizedSwitch(Vector<ExpressionNode*, 8>& literalVector, int32_t& min_num, int32_t& max_num) +{ + SwitchKind typeForTable = SwitchUnset; + bool singleCharacterSwitch = true; + + processClauseList(m_list1, literalVector, typeForTable, singleCharacterSwitch, min_num, max_num); + processClauseList(m_list2, literalVector, typeForTable, singleCharacterSwitch, min_num, max_num); + + if (typeForTable == SwitchUnset || typeForTable == SwitchNeither) + return SwitchInfo::SwitchNone; + + if (typeForTable == SwitchNumber) { + int32_t range = max_num - min_num; + if (min_num <= max_num && range <= 1000 && (range / literalVector.size()) < 10) + return SwitchInfo::SwitchImmediate; + return SwitchInfo::SwitchNone; + } + + ASSERT(typeForTable == SwitchString); + + if (singleCharacterSwitch) { + int32_t range = max_num - min_num; + if (min_num <= max_num && range <= 1000 && (range / literalVector.size()) < 10) + return SwitchInfo::SwitchCharacter; + } + + return SwitchInfo::SwitchString; +} + +RegisterID* CaseBlockNode::emitBytecodeForBlock(BytecodeGenerator& generator, RegisterID* switchExpression, RegisterID* dst) +{ + RefPtr<Label> defaultLabel; + Vector<RefPtr<Label>, 8> labelVector; + Vector<ExpressionNode*, 8> literalVector; + int32_t min_num = std::numeric_limits<int32_t>::max(); + int32_t max_num = std::numeric_limits<int32_t>::min(); + SwitchInfo::SwitchType switchType = tryOptimizedSwitch(literalVector, min_num, max_num); + + if (switchType != SwitchInfo::SwitchNone) { + // Prepare the various labels + for (uint32_t i = 0; i < literalVector.size(); i++) + labelVector.append(generator.newLabel()); + defaultLabel = generator.newLabel(); + generator.beginSwitch(switchExpression, switchType); + } else { + // Setup jumps + for (ClauseListNode* list = m_list1; list; list = list->getNext()) { + RefPtr<RegisterID> clauseVal = generator.newTemporary(); + generator.emitNode(clauseVal.get(), list->getClause()->expr()); + generator.emitBinaryOp(op_stricteq, clauseVal.get(), clauseVal.get(), switchExpression, OperandTypes()); + labelVector.append(generator.newLabel()); + generator.emitJumpIfTrue(clauseVal.get(), labelVector[labelVector.size() - 1].get()); + } + + for (ClauseListNode* list = m_list2; list; list = list->getNext()) { + RefPtr<RegisterID> clauseVal = generator.newTemporary(); + generator.emitNode(clauseVal.get(), list->getClause()->expr()); + generator.emitBinaryOp(op_stricteq, clauseVal.get(), clauseVal.get(), switchExpression, OperandTypes()); + labelVector.append(generator.newLabel()); + generator.emitJumpIfTrue(clauseVal.get(), labelVector[labelVector.size() - 1].get()); + } + defaultLabel = generator.newLabel(); + generator.emitJump(defaultLabel.get()); + } + + RegisterID* result = 0; + + size_t i = 0; + for (ClauseListNode* list = m_list1; list; list = list->getNext()) { + generator.emitLabel(labelVector[i++].get()); + list->getClause()->emitBytecode(generator, dst); + } + + if (m_defaultClause) { + generator.emitLabel(defaultLabel.get()); + m_defaultClause->emitBytecode(generator, dst); + } + + for (ClauseListNode* list = m_list2; list; list = list->getNext()) { + generator.emitLabel(labelVector[i++].get()); + list->getClause()->emitBytecode(generator, dst); + } + if (!m_defaultClause) + generator.emitLabel(defaultLabel.get()); + + ASSERT(i == labelVector.size()); + if (switchType != SwitchInfo::SwitchNone) { + ASSERT(labelVector.size() == literalVector.size()); + generator.endSwitch(labelVector.size(), labelVector.data(), literalVector.data(), defaultLabel.get(), min_num, max_num); + } + return result; +} + +// ------------------------------ SwitchNode ----------------------------------- + +RegisterID* SwitchNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Switch); + + RefPtr<RegisterID> r0 = generator.emitNode(m_expr); + RegisterID* r1 = m_block->emitBytecodeForBlock(generator, r0.get(), dst); + + generator.emitLabel(scope->breakTarget()); + return r1; +} + +// ------------------------------ LabelNode ------------------------------------ + +RegisterID* LabelNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + ASSERT(!generator.breakTarget(m_name)); + + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::NamedLabel, &m_name); + RegisterID* r0 = generator.emitNode(dst, m_statement); + + generator.emitLabel(scope->breakTarget()); + return r0; +} + +// ------------------------------ ThrowNode ------------------------------------ + +RegisterID* ThrowNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + if (dst == generator.ignoredResult()) + dst = 0; + RefPtr<RegisterID> expr = generator.emitNode(m_expr); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitThrow(expr.get()); + return 0; +} + +// ------------------------------ TryNode -------------------------------------- + +RegisterID* TryNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + // NOTE: The catch and finally blocks must be labeled explicitly, so the + // optimizer knows they may be jumped to from anywhere. + + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + RefPtr<Label> tryStartLabel = generator.newLabel(); + RefPtr<Label> finallyStart; + RefPtr<RegisterID> finallyReturnAddr; + if (m_finallyBlock) { + finallyStart = generator.newLabel(); + finallyReturnAddr = generator.newTemporary(); + generator.pushFinallyContext(finallyStart.get(), finallyReturnAddr.get()); + } + + generator.emitLabel(tryStartLabel.get()); + generator.emitNode(dst, m_tryBlock); + + if (m_catchBlock) { + RefPtr<Label> catchEndLabel = generator.newLabel(); + + // Normal path: jump over the catch block. + generator.emitJump(catchEndLabel.get()); + + // Uncaught exception path: the catch block. + RefPtr<Label> here = generator.emitLabel(generator.newLabel().get()); + RefPtr<RegisterID> exceptionRegister = generator.emitCatch(generator.newTemporary(), tryStartLabel.get(), here.get()); + if (m_catchHasEval) { + RefPtr<RegisterID> dynamicScopeObject = generator.emitNewObject(generator.newTemporary()); + generator.emitPutById(dynamicScopeObject.get(), m_exceptionIdent, exceptionRegister.get()); + generator.emitMove(exceptionRegister.get(), dynamicScopeObject.get()); + generator.emitPushScope(exceptionRegister.get()); + } else + generator.emitPushNewScope(exceptionRegister.get(), m_exceptionIdent, exceptionRegister.get()); + generator.emitNode(dst, m_catchBlock); + generator.emitPopScope(); + generator.emitLabel(catchEndLabel.get()); + } + + if (m_finallyBlock) { + generator.popFinallyContext(); + // there may be important registers live at the time we jump + // to a finally block (such as for a return or throw) so we + // ref the highest register ever used as a conservative + // approach to not clobbering anything important + RefPtr<RegisterID> highestUsedRegister = generator.highestUsedRegister(); + RefPtr<Label> finallyEndLabel = generator.newLabel(); + + // Normal path: invoke the finally block, then jump over it. + generator.emitJumpSubroutine(finallyReturnAddr.get(), finallyStart.get()); + generator.emitJump(finallyEndLabel.get()); + + // Uncaught exception path: invoke the finally block, then re-throw the exception. + RefPtr<Label> here = generator.emitLabel(generator.newLabel().get()); + RefPtr<RegisterID> tempExceptionRegister = generator.emitCatch(generator.newTemporary(), tryStartLabel.get(), here.get()); + generator.emitJumpSubroutine(finallyReturnAddr.get(), finallyStart.get()); + generator.emitThrow(tempExceptionRegister.get()); + + // The finally block. + generator.emitLabel(finallyStart.get()); + generator.emitNode(dst, m_finallyBlock); + generator.emitSubroutineReturn(finallyReturnAddr.get()); + + generator.emitLabel(finallyEndLabel.get()); + } + + return dst; +} + +// ------------------------------ ScopeNode ----------------------------- + +inline void ScopeNode::emitStatementsBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (m_data->m_statements) + m_data->m_statements->emitBytecode(generator, dst); +} + +// ------------------------------ ProgramNode ----------------------------- + +RegisterID* ProgramNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + generator.emitDebugHook(WillExecuteProgram, firstLine(), lastLine()); + + RefPtr<RegisterID> dstRegister = generator.newTemporary(); + generator.emitLoad(dstRegister.get(), jsUndefined()); + emitStatementsBytecode(generator, dstRegister.get()); + + generator.emitDebugHook(DidExecuteProgram, firstLine(), lastLine()); + generator.emitEnd(dstRegister.get()); + return 0; +} + +// ------------------------------ EvalNode ----------------------------- + +RegisterID* EvalNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + generator.emitDebugHook(WillExecuteProgram, firstLine(), lastLine()); + + RefPtr<RegisterID> dstRegister = generator.newTemporary(); + generator.emitLoad(dstRegister.get(), jsUndefined()); + emitStatementsBytecode(generator, dstRegister.get()); + + generator.emitDebugHook(DidExecuteProgram, firstLine(), lastLine()); + generator.emitEnd(dstRegister.get()); + return 0; +} + +// ------------------------------ FunctionBodyNode ----------------------------- + +RegisterID* FunctionBodyNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + generator.emitDebugHook(DidEnterCallFrame, firstLine(), lastLine()); + emitStatementsBytecode(generator, generator.ignoredResult()); + + StatementNode* singleStatement = this->singleStatement(); + ReturnNode* returnNode = 0; + + // Check for a return statement at the end of a function composed of a single block. + if (singleStatement && singleStatement->isBlock()) { + StatementNode* lastStatementInBlock = static_cast<BlockNode*>(singleStatement)->lastStatement(); + if (lastStatementInBlock && lastStatementInBlock->isReturnNode()) + returnNode = static_cast<ReturnNode*>(lastStatementInBlock); + } + + // If there is no return we must automatically insert one. + if (!returnNode) { + RegisterID* r0 = generator.isConstructor() ? generator.thisRegister() : generator.emitLoad(0, jsUndefined()); + generator.emitDebugHook(WillLeaveCallFrame, firstLine(), lastLine()); + generator.emitReturn(r0); + return 0; + } + + // If there is a return statment, and it is the only statement in the function, check if this is a numeric compare. + if (static_cast<BlockNode*>(singleStatement)->singleStatement()) { + ExpressionNode* returnValueExpression = returnNode->value(); + if (returnValueExpression && returnValueExpression->isSubtract()) { + ExpressionNode* lhsExpression = static_cast<SubNode*>(returnValueExpression)->lhs(); + ExpressionNode* rhsExpression = static_cast<SubNode*>(returnValueExpression)->rhs(); + if (lhsExpression->isResolveNode() + && rhsExpression->isResolveNode() + && generator.isArgumentNumber(static_cast<ResolveNode*>(lhsExpression)->identifier(), 0) + && generator.isArgumentNumber(static_cast<ResolveNode*>(rhsExpression)->identifier(), 1)) { + + generator.setIsNumericCompareFunction(true); + } + } + } + + return 0; +} + +// ------------------------------ FuncDeclNode --------------------------------- + +RegisterID* FuncDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (dst == generator.ignoredResult()) + dst = 0; + return dst; +} + +// ------------------------------ FuncExprNode --------------------------------- + +RegisterID* FuncExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + return generator.emitNewFunctionExpression(generator.finalDestination(dst), this); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/bytecompiler/RegisterID.h b/Source/JavaScriptCore/bytecompiler/RegisterID.h new file mode 100644 index 000000000..78d49d290 --- /dev/null +++ b/Source/JavaScriptCore/bytecompiler/RegisterID.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * 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. + */ + +#ifndef RegisterID_h +#define RegisterID_h + +#include <wtf/Assertions.h> +#include <wtf/VectorTraits.h> + +namespace JSC { + + class RegisterID { + WTF_MAKE_NONCOPYABLE(RegisterID); + public: + RegisterID() + : m_refCount(0) + , m_isTemporary(false) +#ifndef NDEBUG + , m_didSetIndex(false) +#endif + { + } + + explicit RegisterID(int index) + : m_refCount(0) + , m_index(index) + , m_isTemporary(false) +#ifndef NDEBUG + , m_didSetIndex(true) +#endif + { + } + + void setIndex(int index) + { + ASSERT(!m_refCount); +#ifndef NDEBUG + m_didSetIndex = true; +#endif + m_index = index; + } + + void setTemporary() + { + m_isTemporary = true; + } + + int index() const + { + ASSERT(m_didSetIndex); + return m_index; + } + + bool isTemporary() + { + return m_isTemporary; + } + + void ref() + { + ++m_refCount; + } + + void deref() + { + --m_refCount; + ASSERT(m_refCount >= 0); + } + + int refCount() const + { + return m_refCount; + } + + private: + + int m_refCount; + int m_index; + bool m_isTemporary; +#ifndef NDEBUG + bool m_didSetIndex; +#endif + }; + +} // namespace JSC + +namespace WTF { + + template<> struct VectorTraits<JSC::RegisterID> : VectorTraitsBase<true, JSC::RegisterID> { + static const bool needsInitialization = true; + static const bool canInitializeWithMemset = true; // Default initialization just sets everything to 0 or false, so this is safe. + }; + +} // namespace WTF + +#endif // RegisterID_h |