|  | /* | 
|  | * 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 "PrototypeFunction.h" | 
|  | #include "JSFunction.h" | 
|  | #include "Interpreter.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 | 
|  | } | 
|  |  | 
|  | void BytecodeGenerator::generate() | 
|  | { | 
|  | m_codeBlock->setThisRegister(m_thisRegister.index()); | 
|  |  | 
|  | m_scopeNode->emitBytecode(*this); | 
|  |  | 
|  | #ifndef NDEBUG | 
|  | m_codeBlock->setInstructionCount(m_codeBlock->instructions().size()); | 
|  |  | 
|  | if (s_dumpsGeneratedCode) | 
|  | m_codeBlock->dump(m_scopeChain->globalObject()->globalExec()); | 
|  | #endif | 
|  |  | 
|  | if ((m_codeType == FunctionCode && !m_codeBlock->needsFullScopeChain() && !m_codeBlock->usesArguments()) || m_codeType == EvalCode) | 
|  | symbolTable().clear(); | 
|  |  | 
|  | m_codeBlock->setIsNumericCompareFunction(instructions() == m_globalData->numericCompareFunction(m_scopeChain->globalObject()->globalExec())); | 
|  |  | 
|  | #if !ENABLE(OPCODE_SAMPLING) | 
|  | if (!m_regeneratingForExceptionInfo && (m_codeType == FunctionCode || m_codeType == EvalCode)) | 
|  | m_codeBlock->clearExceptionInfo(); | 
|  | #endif | 
|  |  | 
|  | m_codeBlock->shrinkToFit(); | 
|  | } | 
|  |  | 
|  | 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.ustring().rep(), newEntry); | 
|  |  | 
|  | if (!result.second) { | 
|  | r0 = ®isterFor(result.first->second.getIndex()); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | ++m_codeBlock->m_numVars; | 
|  | r0 = newRegister(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool BytecodeGenerator::addGlobalVar(const Identifier& ident, bool isConstant, RegisterID*& r0) | 
|  | { | 
|  | int index = m_nextGlobalIndex; | 
|  | SymbolTableEntry newEntry(index, isConstant ? ReadOnly : 0); | 
|  | pair<SymbolTable::iterator, bool> result = symbolTable().add(ident.ustring().rep(), newEntry); | 
|  |  | 
|  | if (!result.second) | 
|  | index = result.first->second.getIndex(); | 
|  | else { | 
|  | --m_nextGlobalIndex; | 
|  | m_globals.append(index + m_globalVarStorageOffset); | 
|  | } | 
|  |  | 
|  | r0 = ®isterFor(index); | 
|  | return result.second; | 
|  | } | 
|  |  | 
|  | void BytecodeGenerator::preserveLastVar() | 
|  | { | 
|  | if ((m_firstConstantIndex = m_calleeRegisters.size()) != 0) | 
|  | m_lastVar = &m_calleeRegisters.last(); | 
|  | } | 
|  |  | 
|  | BytecodeGenerator::BytecodeGenerator(ProgramNode* programNode, const Debugger* debugger, const ScopeChain& scopeChain, SymbolTable* symbolTable, ProgramCodeBlock* codeBlock) | 
|  | : m_shouldEmitDebugHooks(!!debugger) | 
|  | , m_shouldEmitProfileHooks(scopeChain.globalObject()->supportsProfiling()) | 
|  | , m_scopeChain(&scopeChain) | 
|  | , m_symbolTable(symbolTable) | 
|  | , m_scopeNode(programNode) | 
|  | , m_codeBlock(codeBlock) | 
|  | , m_thisRegister(RegisterFile::ProgramCodeThisRegister) | 
|  | , m_finallyDepth(0) | 
|  | , m_dynamicScopeDepth(0) | 
|  | , m_baseScopeDepth(0) | 
|  | , m_codeType(GlobalCode) | 
|  | , m_nextGlobalIndex(-1) | 
|  | , m_nextConstantOffset(0) | 
|  | , m_globalConstantIndex(0) | 
|  | , m_globalData(&scopeChain.globalObject()->globalExec()->globalData()) | 
|  | , m_lastOpcodeID(op_end) | 
|  | , m_emitNodeDepth(0) | 
|  | , m_regeneratingForExceptionInfo(false) | 
|  | , m_codeBlockBeingRegeneratedFrom(0) | 
|  | { | 
|  | 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" | 
|  |  | 
|  | JSGlobalObject* globalObject = scopeChain.globalObject(); | 
|  | ExecState* exec = globalObject->globalExec(); | 
|  | RegisterFile* registerFile = &exec->globalData().interpreter->registerFile(); | 
|  |  | 
|  | // Shift register indexes in generated code to elide registers allocated by intermediate stack frames. | 
|  | m_globalVarStorageOffset = -RegisterFile::CallFrameHeaderSize - m_codeBlock->m_numParameters - registerFile->size(); | 
|  |  | 
|  | // Add previously defined symbols to bookkeeping. | 
|  | m_globals.grow(symbolTable->size()); | 
|  | SymbolTable::iterator end = symbolTable->end(); | 
|  | for (SymbolTable::iterator it = symbolTable->begin(); it != end; ++it) | 
|  | registerFor(it->second.getIndex()).setIndex(it->second.getIndex() + m_globalVarStorageOffset); | 
|  |  | 
|  | BatchedTransitionOptimizer optimizer(globalObject); | 
|  |  | 
|  | const VarStack& varStack = programNode->varStack(); | 
|  | const FunctionStack& functionStack = programNode->functionStack(); | 
|  | bool canOptimizeNewGlobals = symbolTable->size() + functionStack.size() + varStack.size() < registerFile->maxGlobals(); | 
|  | if (canOptimizeNewGlobals) { | 
|  | // Shift new symbols so they get stored prior to existing symbols. | 
|  | m_nextGlobalIndex -= symbolTable->size(); | 
|  |  | 
|  | for (size_t i = 0; i < functionStack.size(); ++i) { | 
|  | FunctionBodyNode* function = functionStack[i]; | 
|  | globalObject->removeDirect(function->ident()); // Make sure our new function is not shadowed by an old property. | 
|  | emitNewFunction(addGlobalVar(function->ident(), false), function); | 
|  | } | 
|  |  | 
|  | Vector<RegisterID*, 32> newVars; | 
|  | for (size_t i = 0; i < varStack.size(); ++i) | 
|  | if (!globalObject->hasProperty(exec, *varStack[i].first)) | 
|  | newVars.append(addGlobalVar(*varStack[i].first, varStack[i].second & DeclarationStacks::IsConstant)); | 
|  |  | 
|  | preserveLastVar(); | 
|  |  | 
|  | for (size_t i = 0; i < newVars.size(); ++i) | 
|  | emitLoad(newVars[i], jsUndefined()); | 
|  | } else { | 
|  | for (size_t i = 0; i < functionStack.size(); ++i) { | 
|  | FunctionBodyNode* function = functionStack[i]; | 
|  | globalObject->putWithAttributes(exec, function->ident(), new (exec) JSFunction(exec, makeFunction(exec, function), scopeChain.node()), DontDelete); | 
|  | } | 
|  | for (size_t i = 0; i < varStack.size(); ++i) { | 
|  | if (globalObject->hasProperty(exec, *varStack[i].first)) | 
|  | continue; | 
|  | int attributes = DontDelete; | 
|  | if (varStack[i].second & DeclarationStacks::IsConstant) | 
|  | attributes |= ReadOnly; | 
|  | globalObject->putWithAttributes(exec, *varStack[i].first, jsUndefined(), attributes); | 
|  | } | 
|  |  | 
|  | preserveLastVar(); | 
|  | } | 
|  | } | 
|  |  | 
|  | BytecodeGenerator::BytecodeGenerator(FunctionBodyNode* functionBody, const Debugger* debugger, const ScopeChain& scopeChain, SymbolTable* symbolTable, CodeBlock* codeBlock) | 
|  | : m_shouldEmitDebugHooks(!!debugger) | 
|  | , m_shouldEmitProfileHooks(scopeChain.globalObject()->supportsProfiling()) | 
|  | , m_scopeChain(&scopeChain) | 
|  | , m_symbolTable(symbolTable) | 
|  | , m_scopeNode(functionBody) | 
|  | , m_codeBlock(codeBlock) | 
|  | , m_finallyDepth(0) | 
|  | , m_dynamicScopeDepth(0) | 
|  | , m_baseScopeDepth(0) | 
|  | , m_codeType(FunctionCode) | 
|  | , m_nextConstantOffset(0) | 
|  | , m_globalConstantIndex(0) | 
|  | , m_globalData(&scopeChain.globalObject()->globalExec()->globalData()) | 
|  | , m_lastOpcodeID(op_end) | 
|  | , m_emitNodeDepth(0) | 
|  | , m_regeneratingForExceptionInfo(false) | 
|  | , m_codeBlockBeingRegeneratedFrom(0) | 
|  | { | 
|  | if (m_shouldEmitDebugHooks) | 
|  | m_codeBlock->setNeedsFullScopeChain(true); | 
|  |  | 
|  | codeBlock->setGlobalData(m_globalData); | 
|  |  | 
|  | bool usesArguments = functionBody->usesArguments(); | 
|  | codeBlock->setUsesArguments(usesArguments); | 
|  | if (usesArguments) { | 
|  | m_argumentsRegister.setIndex(RegisterFile::OptionalCalleeArguments); | 
|  | addVar(propertyNames().arguments, false); | 
|  | } | 
|  |  | 
|  | if (m_codeBlock->needsFullScopeChain()) { | 
|  | ++m_codeBlock->m_numVars; | 
|  | m_activationRegisterIndex = newRegister()->index(); | 
|  | emitOpcode(op_enter_with_activation); | 
|  | instructions().append(m_activationRegisterIndex); | 
|  | } else | 
|  | emitOpcode(op_enter); | 
|  |  | 
|  | if (usesArguments) { | 
|  | emitOpcode(op_init_arguments); | 
|  |  | 
|  | // 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); | 
|  | } | 
|  |  | 
|  | const DeclarationStacks::FunctionStack& functionStack = functionBody->functionStack(); | 
|  | for (size_t i = 0; i < functionStack.size(); ++i) { | 
|  | FunctionBodyNode* function = functionStack[i]; | 
|  | const Identifier& ident = function->ident(); | 
|  | m_functions.add(ident.ustring().rep()); | 
|  | emitNewFunction(addVar(ident, false), function); | 
|  | } | 
|  |  | 
|  | const DeclarationStacks::VarStack& varStack = functionBody->varStack(); | 
|  | for (size_t i = 0; i < varStack.size(); ++i) | 
|  | addVar(*varStack[i].first, varStack[i].second & DeclarationStacks::IsConstant); | 
|  |  | 
|  | FunctionParameters& parameters = *functionBody->parameters(); | 
|  | size_t parameterCount = parameters.size(); | 
|  | m_nextParameterIndex = -RegisterFile::CallFrameHeaderSize - parameterCount - 1; | 
|  | m_parameters.grow(1 + parameterCount); // reserve space for "this" | 
|  |  | 
|  | // Add "this" as a parameter | 
|  | m_thisRegister.setIndex(m_nextParameterIndex); | 
|  | ++m_nextParameterIndex; | 
|  | ++m_codeBlock->m_numParameters; | 
|  |  | 
|  | if (functionBody->usesThis() || m_shouldEmitDebugHooks) { | 
|  | emitOpcode(op_convert_this); | 
|  | instructions().append(m_thisRegister.index()); | 
|  | } | 
|  |  | 
|  | for (size_t i = 0; i < parameterCount; ++i) | 
|  | addParameter(parameters[i]); | 
|  |  | 
|  | preserveLastVar(); | 
|  | } | 
|  |  | 
|  | BytecodeGenerator::BytecodeGenerator(EvalNode* evalNode, const Debugger* debugger, const ScopeChain& scopeChain, SymbolTable* symbolTable, EvalCodeBlock* codeBlock) | 
|  | : m_shouldEmitDebugHooks(!!debugger) | 
|  | , m_shouldEmitProfileHooks(scopeChain.globalObject()->supportsProfiling()) | 
|  | , m_scopeChain(&scopeChain) | 
|  | , m_symbolTable(symbolTable) | 
|  | , m_scopeNode(evalNode) | 
|  | , m_codeBlock(codeBlock) | 
|  | , m_thisRegister(RegisterFile::ProgramCodeThisRegister) | 
|  | , m_finallyDepth(0) | 
|  | , m_dynamicScopeDepth(0) | 
|  | , m_baseScopeDepth(codeBlock->baseScopeDepth()) | 
|  | , m_codeType(EvalCode) | 
|  | , m_nextConstantOffset(0) | 
|  | , m_globalConstantIndex(0) | 
|  | , m_globalData(&scopeChain.globalObject()->globalExec()->globalData()) | 
|  | , m_lastOpcodeID(op_end) | 
|  | , m_emitNodeDepth(0) | 
|  | , m_regeneratingForExceptionInfo(false) | 
|  | , m_codeBlockBeingRegeneratedFrom(0) | 
|  | { | 
|  | 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); | 
|  |  | 
|  | preserveLastVar(); | 
|  | } | 
|  |  | 
|  | RegisterID* BytecodeGenerator::addParameter(const Identifier& ident) | 
|  | { | 
|  | // Parameters overwrite var declarations, but not function declarations. | 
|  | RegisterID* result = 0; | 
|  | UString::Rep* rep = ident.ustring().rep(); | 
|  | if (!m_functions.contains(rep)) { | 
|  | symbolTable().set(rep, m_nextParameterIndex); | 
|  | RegisterID& parameter = registerFor(m_nextParameterIndex); | 
|  | parameter.setIndex(m_nextParameterIndex); | 
|  | result = ¶meter; | 
|  | } | 
|  |  | 
|  | // To maintain the calling convention, we have to allocate unique space for | 
|  | // each parameter, even if the parameter doesn't make it into the symbol table. | 
|  | ++m_nextParameterIndex; | 
|  | ++m_codeBlock->m_numParameters; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | RegisterID* BytecodeGenerator::registerFor(const Identifier& ident) | 
|  | { | 
|  | if (ident == propertyNames().thisIdentifier) | 
|  | return &m_thisRegister; | 
|  |  | 
|  | if (!shouldOptimizeLocals()) | 
|  | return 0; | 
|  |  | 
|  | SymbolTableEntry entry = symbolTable().get(ident.ustring().rep()); | 
|  | if (entry.isNull()) | 
|  | return 0; | 
|  |  | 
|  | if (ident == propertyNames().arguments) | 
|  | createArgumentsIfNecessary(); | 
|  |  | 
|  | return ®isterFor(entry.getIndex()); | 
|  | } | 
|  |  | 
|  | bool BytecodeGenerator::willResolveToArguments(const Identifier& ident) | 
|  | { | 
|  | if (ident != propertyNames().arguments) | 
|  | return false; | 
|  |  | 
|  | if (!shouldOptimizeLocals()) | 
|  | return false; | 
|  |  | 
|  | SymbolTableEntry entry = symbolTable().get(ident.ustring().rep()); | 
|  | 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.ustring().rep()); | 
|  | ASSERT(!entry.isNull()); | 
|  | return ®isterFor(entry.getIndex()); | 
|  | } | 
|  |  | 
|  | RegisterID* BytecodeGenerator::constRegisterFor(const Identifier& ident) | 
|  | { | 
|  | if (m_codeType == EvalCode) | 
|  | return 0; | 
|  |  | 
|  | SymbolTableEntry entry = symbolTable().get(ident.ustring().rep()); | 
|  | if (entry.isNull()) | 
|  | return 0; | 
|  |  | 
|  | return ®isterFor(entry.getIndex()); | 
|  | } | 
|  |  | 
|  | bool BytecodeGenerator::isLocal(const Identifier& ident) | 
|  | { | 
|  | if (ident == propertyNames().thisIdentifier) | 
|  | return true; | 
|  |  | 
|  | return shouldOptimizeLocals() && symbolTable().contains(ident.ustring().rep()); | 
|  | } | 
|  |  | 
|  | bool BytecodeGenerator::isLocalConstant(const Identifier& ident) | 
|  | { | 
|  | return symbolTable().get(ident.ustring().rep()).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) | 
|  | { | 
|  | instructions().append(globalData()->interpreter->getOpcode(opcodeID)); | 
|  | m_lastOpcodeID = opcodeID; | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | void ALWAYS_INLINE BytecodeGenerator::rewindUnaryOp() | 
|  | { | 
|  | ASSERT(instructions().size() >= 3); | 
|  | instructions().shrink(instructions().size() - 3); | 
|  | } | 
|  |  | 
|  | 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_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_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(m_scopeChain->globalObject()->d()->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(m_scopeChain->globalObject()->d()->applyFunction); | 
|  | instructions().append(target->bind(begin, instructions().size())); | 
|  | return target; | 
|  | } | 
|  |  | 
|  | unsigned BytecodeGenerator::addConstant(const Identifier& ident) | 
|  | { | 
|  | UString::Rep* rep = ident.ustring().rep(); | 
|  | 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->addConstantRegister(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()).jsValue().isString()) { | 
|  | const UString& value = asString(m_codeBlock->constantRegister(src2->index()).jsValue())->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 JSNumberCell each time. | 
|  | // Later we can do the extra work to handle that like the other cases. | 
|  | if (number == HashTraits<double>::emptyValue() || HashTraits<double>::isDeletedValue(number)) | 
|  | return emitLoad(dst, jsNumber(globalData(), number)); | 
|  | JSValue& valueInMap = m_numberMap.add(number, JSValue()).first->second; | 
|  | if (!valueInMap) | 
|  | valueInMap = jsNumber(globalData(), number); | 
|  | return emitLoad(dst, valueInMap); | 
|  | } | 
|  |  | 
|  | RegisterID* BytecodeGenerator::emitLoad(RegisterID* dst, const Identifier& identifier) | 
|  | { | 
|  | JSString*& stringInMap = m_stringMap.add(identifier.ustring().rep(), 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; | 
|  | 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; | 
|  | if (!currentScope->isVariableObject()) | 
|  | break; | 
|  | JSVariableObject* currentVariableObject = static_cast<JSVariableObject*>(currentScope); | 
|  | SymbolTableEntry entry = currentVariableObject->symbolTable().get(property.ustring().rep()); | 
|  |  | 
|  | // Found the property | 
|  | if (!entry.isNull()) { | 
|  | if (entry.isReadOnly() && forWriting) { | 
|  | stackDepth = 0; | 
|  | index = missingSymbolMarker(); | 
|  | if (++iter == end) | 
|  | globalObject = currentVariableObject; | 
|  | return false; | 
|  | } | 
|  | stackDepth = depth; | 
|  | 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; | 
|  | index = missingSymbolMarker(); | 
|  | JSObject* scope = *iter; | 
|  | if (++iter == end) | 
|  | globalObject = scope; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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 (globalObject) { | 
|  | bool forceGlobalResolve = false; | 
|  | if (m_regeneratingForExceptionInfo) { | 
|  | #if ENABLE(JIT) | 
|  | forceGlobalResolve = m_codeBlockBeingRegeneratedFrom->hasGlobalResolveInfoAtBytecodeOffset(instructions().size()); | 
|  | #else | 
|  | forceGlobalResolve = m_codeBlockBeingRegeneratedFrom->hasGlobalResolveInstructionAtBytecodeOffset(instructions().size()); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | if (index != missingSymbolMarker() && !forceGlobalResolve && !requiresDynamicChecks) { | 
|  | // Directly index the property lookup across multiple scopes. | 
|  | return emitGetScopedVar(dst, depth, index, globalObject); | 
|  | } | 
|  |  | 
|  | #if ENABLE(JIT) | 
|  | m_codeBlock->addGlobalResolveInfo(instructions().size()); | 
|  | #else | 
|  | m_codeBlock->addGlobalResolveInstruction(instructions().size()); | 
|  | #endif | 
|  | emitOpcode(requiresDynamicChecks ? op_resolve_global_dynamic : op_resolve_global); | 
|  | instructions().append(dst->index()); | 
|  | instructions().append(globalObject); | 
|  | 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) { | 
|  | emitOpcode(op_get_global_var); | 
|  | instructions().append(dst->index()); | 
|  | instructions().append(asCell(globalObject)); | 
|  | 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(asCell(globalObject)); | 
|  | 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)); | 
|  | return dst; | 
|  | } | 
|  |  | 
|  | // Global object is the base | 
|  | return emitLoad(dst, JSValue(globalObject)); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | if (m_regeneratingForExceptionInfo) { | 
|  | #if ENABLE(JIT) | 
|  | forceGlobalResolve = m_codeBlockBeingRegeneratedFrom->hasGlobalResolveInfoAtBytecodeOffset(instructions().size()); | 
|  | #else | 
|  | forceGlobalResolve = m_codeBlockBeingRegeneratedFrom->hasGlobalResolveInstructionAtBytecodeOffset(instructions().size()); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | // 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 ENABLE(JIT) | 
|  | m_codeBlock->addGlobalResolveInfo(instructions().size()); | 
|  | #else | 
|  | m_codeBlock->addGlobalResolveInstruction(instructions().size()); | 
|  | #endif | 
|  | emitOpcode(requiresDynamicChecks ? op_resolve_global_dynamic : op_resolve_global); | 
|  | instructions().append(propDst->index()); | 
|  | instructions().append(globalObject); | 
|  | 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(JIT) | 
|  | m_codeBlock->addStructureStubInfo(StructureStubInfo(access_get_by_id)); | 
|  | #else | 
|  | 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::emitPutById(RegisterID* base, const Identifier& property, RegisterID* value) | 
|  | { | 
|  | #if ENABLE(JIT) | 
|  | m_codeBlock->addStructureStubInfo(StructureStubInfo(access_put_by_id)); | 
|  | #else | 
|  | 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); | 
|  | 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::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; | 
|  | } | 
|  |  | 
|  | RegisterID* BytecodeGenerator::emitNewArray(RegisterID* dst, ElementNode* elements) | 
|  | { | 
|  | 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) | 
|  | { | 
|  | unsigned index = m_codeBlock->addFunctionDecl(makeFunction(m_globalData, function)); | 
|  |  | 
|  | emitOpcode(op_new_func); | 
|  | instructions().append(dst->index()); | 
|  | instructions().append(index); | 
|  | 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)); | 
|  |  | 
|  | emitOpcode(op_new_func_exp); | 
|  | instructions().append(r0->index()); | 
|  | instructions().append(index); | 
|  | return r0; | 
|  | } | 
|  |  | 
|  | RegisterID* BytecodeGenerator::emitCall(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, ArgumentsNode* argumentsNode, unsigned divot, unsigned startOffset, unsigned endOffset) | 
|  | { | 
|  | return emitCall(op_call, dst, func, thisRegister, argumentsNode, divot, startOffset, endOffset); | 
|  | } | 
|  |  | 
|  | void BytecodeGenerator::createArgumentsIfNecessary() | 
|  | { | 
|  | if (m_codeBlock->usesArguments() && m_codeType == FunctionCode) | 
|  | emitOpcode(op_create_arguments); | 
|  | } | 
|  |  | 
|  | RegisterID* BytecodeGenerator::emitCallEval(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, ArgumentsNode* argumentsNode, unsigned divot, unsigned startOffset, unsigned endOffset) | 
|  | { | 
|  | createArgumentsIfNecessary(); | 
|  | return emitCall(op_call_eval, dst, func, thisRegister, argumentsNode, divot, startOffset, endOffset); | 
|  | } | 
|  |  | 
|  | RegisterID* BytecodeGenerator::emitCall(OpcodeID opcodeID, RegisterID* dst, RegisterID* func, RegisterID* thisRegister, ArgumentsNode* argumentsNode, unsigned divot, unsigned startOffset, unsigned endOffset) | 
|  | { | 
|  | ASSERT(opcodeID == op_call || opcodeID == op_call_eval); | 
|  | ASSERT(func->refCount()); | 
|  | ASSERT(thisRegister->refCount()); | 
|  |  | 
|  | RegisterID* originalFunc = func; | 
|  | if (m_shouldEmitProfileHooks) { | 
|  | // If codegen decided to recycle func as this call's destination register, | 
|  | // we need to undo that optimization here so that func will still be around | 
|  | // for the sake of op_profile_did_call. | 
|  | if (dst == func) { | 
|  | RefPtr<RegisterID> movedThisRegister = emitMove(newTemporary(), thisRegister); | 
|  | RefPtr<RegisterID> movedFunc = emitMove(thisRegister, func); | 
|  |  | 
|  | thisRegister = movedThisRegister.release().releaseRef(); | 
|  | func = movedFunc.release().releaseRef(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Generate code for arguments. | 
|  | Vector<RefPtr<RegisterID>, 16> argv; | 
|  | argv.append(thisRegister); | 
|  | for (ArgumentListNode* n = argumentsNode->m_listNode; n; n = n->m_next) { | 
|  | argv.append(newTemporary()); | 
|  | // op_call requires the arguments to be a sequential range of registers | 
|  | ASSERT(argv[argv.size() - 1]->index() == argv[argv.size() - 2]->index() + 1); | 
|  | emitNode(argv.last().get(), 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(func->index()); | 
|  |  | 
|  | #if ENABLE(JIT) | 
|  | m_codeBlock->addFunctionRegisterInfo(instructions().size(), func->index()); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | emitExpressionInfo(divot, startOffset, endOffset); | 
|  |  | 
|  | #if ENABLE(JIT) | 
|  | m_codeBlock->addCallLinkInfo(); | 
|  | #endif | 
|  |  | 
|  | // Emit call. | 
|  | emitOpcode(opcodeID); | 
|  | instructions().append(dst->index()); // dst | 
|  | instructions().append(func->index()); // func | 
|  | instructions().append(argv.size()); // argCount | 
|  | instructions().append(argv[0]->index() + argv.size() + RegisterFile::CallFrameHeaderSize); // registerOffset | 
|  |  | 
|  | if (m_shouldEmitProfileHooks) { | 
|  | emitOpcode(op_profile_did_call); | 
|  | instructions().append(func->index()); | 
|  |  | 
|  | if (dst == originalFunc) { | 
|  | thisRegister->deref(); | 
|  | func->deref(); | 
|  | } | 
|  | } | 
|  |  | 
|  | return dst; | 
|  | } | 
|  |  | 
|  | RegisterID* BytecodeGenerator::emitLoadVarargs(RegisterID* argCountDst, RegisterID* arguments) | 
|  | { | 
|  | ASSERT(argCountDst->index() < arguments->index()); | 
|  | emitOpcode(op_load_varargs); | 
|  | instructions().append(argCountDst->index()); | 
|  | instructions().append(arguments->index()); | 
|  | return argCountDst; | 
|  | } | 
|  |  | 
|  | RegisterID* BytecodeGenerator::emitCallVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* argCountRegister, unsigned divot, unsigned startOffset, unsigned endOffset) | 
|  | { | 
|  | ASSERT(func->refCount()); | 
|  | ASSERT(thisRegister->refCount()); | 
|  | ASSERT(dst != func); | 
|  | if (m_shouldEmitProfileHooks) { | 
|  | emitOpcode(op_profile_will_call); | 
|  | instructions().append(func->index()); | 
|  |  | 
|  | #if ENABLE(JIT) | 
|  | m_codeBlock->addFunctionRegisterInfo(instructions().size(), func->index()); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | emitExpressionInfo(divot, startOffset, endOffset); | 
|  |  | 
|  | // Emit call. | 
|  | emitOpcode(op_call_varargs); | 
|  | instructions().append(dst->index()); // dst | 
|  | instructions().append(func->index()); // func | 
|  | instructions().append(argCountRegister->index()); // arg count | 
|  | instructions().append(thisRegister->index() + RegisterFile::CallFrameHeaderSize); // initial registerOffset | 
|  | if (m_shouldEmitProfileHooks) { | 
|  | emitOpcode(op_profile_did_call); | 
|  | instructions().append(func->index()); | 
|  | } | 
|  | return dst; | 
|  | } | 
|  |  | 
|  | RegisterID* BytecodeGenerator::emitReturn(RegisterID* src) | 
|  | { | 
|  | if (m_codeBlock->needsFullScopeChain()) { | 
|  | emitOpcode(op_tear_off_activation); | 
|  | instructions().append(m_activationRegisterIndex); | 
|  | } else if (m_codeBlock->usesArguments() && m_codeBlock->m_numParameters > 1) | 
|  | emitOpcode(op_tear_off_arguments); | 
|  |  | 
|  | 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, ArgumentsNode* argumentsNode, unsigned divot, unsigned startOffset, unsigned endOffset) | 
|  | { | 
|  | ASSERT(func->refCount()); | 
|  |  | 
|  | RegisterID* originalFunc = func; | 
|  | if (m_shouldEmitProfileHooks) { | 
|  | // If codegen decided to recycle func as this call's destination register, | 
|  | // we need to undo that optimization here so that func will still be around | 
|  | // for the sake of op_profile_did_call. | 
|  | if (dst == func) { | 
|  | RefPtr<RegisterID> movedFunc = emitMove(newTemporary(), func); | 
|  | func = movedFunc.release().releaseRef(); | 
|  | } | 
|  | } | 
|  |  | 
|  | RefPtr<RegisterID> funcProto = newTemporary(); | 
|  |  | 
|  | // Generate code for arguments. | 
|  | Vector<RefPtr<RegisterID>, 16> argv; | 
|  | argv.append(newTemporary()); // reserve space for "this" | 
|  | for (ArgumentListNode* n = argumentsNode ? argumentsNode->m_listNode : 0; n; n = n->m_next) { | 
|  | argv.append(newTemporary()); | 
|  | // op_construct requires the arguments to be a sequential range of registers | 
|  | ASSERT(argv[argv.size() - 1]->index() == argv[argv.size() - 2]->index() + 1); | 
|  | emitNode(argv.last().get(), n); | 
|  | } | 
|  |  | 
|  | if (m_shouldEmitProfileHooks) { | 
|  | emitOpcode(op_profile_will_call); | 
|  | instructions().append(func->index()); | 
|  | } | 
|  |  | 
|  | // Load prototype. | 
|  | emitExpressionInfo(divot, startOffset, endOffset); | 
|  | emitGetByIdExceptionInfo(op_construct); | 
|  | emitGetById(funcProto.get(), func, globalData()->propertyNames->prototype); | 
|  |  | 
|  | // 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); | 
|  |  | 
|  | #if ENABLE(JIT) | 
|  | m_codeBlock->addCallLinkInfo(); | 
|  | #endif | 
|  |  | 
|  | emitOpcode(op_construct); | 
|  | instructions().append(dst->index()); // dst | 
|  | instructions().append(func->index()); // func | 
|  | instructions().append(argv.size()); // argCount | 
|  | instructions().append(argv[0]->index() + argv.size() + RegisterFile::CallFrameHeaderSize); // registerOffset | 
|  | instructions().append(funcProto->index()); // proto | 
|  | instructions().append(argv[0]->index()); // thisRegister | 
|  |  | 
|  | emitOpcode(op_construct_verify); | 
|  | instructions().append(dst->index()); | 
|  | instructions().append(argv[0]->index()); | 
|  |  | 
|  | if (m_shouldEmitProfileHooks) { | 
|  | emitOpcode(op_profile_did_call); | 
|  | instructions().append(func->index()); | 
|  |  | 
|  | if (dst == originalFunc) | 
|  | func->deref(); | 
|  | } | 
|  |  | 
|  | 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++; | 
|  | createArgumentsIfNecessary(); | 
|  |  | 
|  | 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) | 
|  | { | 
|  | #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; | 
|  | } | 
|  |  | 
|  | RegisterID* BytecodeGenerator::emitNewError(RegisterID* dst, ErrorType type, JSValue message) | 
|  | { | 
|  | emitOpcode(op_new_error); | 
|  | instructions().append(dst->index()); | 
|  | instructions().append(static_cast<int>(type)); | 
|  | instructions().append(addConstantValue(message)->index()); | 
|  | return dst; | 
|  | } | 
|  |  | 
|  | 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++; | 
|  |  | 
|  | createArgumentsIfNecessary(); | 
|  |  | 
|  | 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()); | 
|  | UString::Rep* clause = static_cast<StringNode*>(node)->value().ustring().rep(); | 
|  | ASSERT(clause->length() == 1); | 
|  |  | 
|  | int32_t key = clause->characters()[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()); | 
|  | UString::Rep* clause = static_cast<StringNode*>(nodes[i])->value().ustring().rep(); | 
|  | 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. | 
|  | emitExpressionInfo(0, 0, 0); | 
|  | RegisterID* exception = emitNewError(newTemporary(), SyntaxError, jsString(globalData(), "Expression too deep")); | 
|  | emitThrow(exception); | 
|  | return exception; | 
|  | } | 
|  |  | 
|  | } // namespace JSC |