/* * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2008 Cameron Zwarich * Copyright (C) 2012 Igalia, S.L. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #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 #include #include 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 m_profileHookRegister; ArgumentsNode* m_argumentsNode; Vector, 8> m_argv; }; struct FinallyContext { Label* finallyAddr; RegisterID* retAddrDst; }; struct ControlFlowContext { bool isFinallyBlock; FinallyContext finallyContext; }; struct ForInContext { RefPtr expectedSubscriptRegister; RefPtr iterRegister; RefPtr indexRegister; RefPtr propertyRegister; }; class ResolveResult { public: enum Flags { // The property is locally bound, in a register. RegisterFlag = 0x1, // We need to traverse the scope chain at runtime, checking for // non-strict eval and/or `with' nodes. DynamicFlag = 0x2, // The property was resolved to a definite location, and the // identifier is not needed any more. StaticFlag = 0x4, // Once we have the base object, the property will be located at a // known index. IndexedFlag = 0x8, // Skip some number of objects in the scope chain, given by "depth". ScopedFlag = 0x10, // The resolved binding is immutable. ReadOnlyFlag = 0x20, // The base object is the global object. GlobalFlag = 0x40 }; enum Type { // The property is local, and stored in a register. Register = RegisterFlag | StaticFlag, // A read-only local, created by "const". ReadOnlyRegister = RegisterFlag | ReadOnlyFlag | StaticFlag, // The property is statically scoped free variable. Its coordinates // are in "index" and "depth". Lexical = IndexedFlag | ScopedFlag | StaticFlag, // A read-only Lexical, created by "const". ReadOnlyLexical = IndexedFlag | ScopedFlag | ReadOnlyFlag | StaticFlag, // The property was not bound lexically, so at runtime we should // look directly in the global object. Global = GlobalFlag, // Like Global, but we could actually resolve the property to a // DontDelete property in the global object, for instance, any // binding created with "var" at the top level. At runtime we'll // just index into the global object. IndexedGlobal = IndexedFlag | GlobalFlag | StaticFlag, // Like IndexedGlobal, but the property is also read-only, like NaN, // Infinity, or undefined. ReadOnlyIndexedGlobal = IndexedFlag | ReadOnlyFlag | GlobalFlag | StaticFlag, // The property could not be resolved statically, due to the // presence of `with' blocks. At runtime we'll have to walk the // scope chain. ScopedFlag is set to indicate that "depth" will // hold some number of nodes to skip in the scope chain, before // beginning the search. Dynamic = DynamicFlag | ScopedFlag, // The property was located as a statically scoped free variable, // but while traversing the scope chain, there was an intermediate // activation that used non-strict `eval'. At runtime we'll have to // check for the absence of this property in those intervening // scopes. DynamicLexical = DynamicFlag | IndexedFlag | ScopedFlag, // Like ReadOnlyLexical, but with intervening non-strict `eval'. DynamicReadOnlyLexical = DynamicFlag | IndexedFlag | ScopedFlag | ReadOnlyFlag, // Like Global, but with intervening non-strict `eval'. As with // Dynamic, ScopeFlag is set to indicate that "depth" does indeed // store a number of frames to skip before doing the dynamic checks. DynamicGlobal = DynamicFlag | GlobalFlag | ScopedFlag, // Like IndexedGlobal, but with intervening non-strict `eval'. DynamicIndexedGlobal = DynamicFlag | IndexedFlag | GlobalFlag | ScopedFlag, // Like ReadOnlyIndexedGlobal, but with intervening non-strict // `eval'. DynamicReadOnlyIndexedGlobal = DynamicFlag | IndexedFlag | ReadOnlyFlag | GlobalFlag | ScopedFlag, }; static ResolveResult registerResolve(RegisterID *local, unsigned flags) { return ResolveResult(Register | flags, local, missingSymbolMarker(), 0, 0); } static ResolveResult dynamicResolve(size_t depth) { return ResolveResult(Dynamic, 0, missingSymbolMarker(), depth, 0); } static ResolveResult lexicalResolve(int index, size_t depth, unsigned flags) { unsigned type = (flags & DynamicFlag) ? DynamicLexical : Lexical; return ResolveResult(type | flags, 0, index, depth, 0); } static ResolveResult indexedGlobalResolve(int index, JSObject *globalObject, unsigned flags) { return ResolveResult(IndexedGlobal | flags, 0, index, 0, globalObject); } static ResolveResult dynamicIndexedGlobalResolve(int index, size_t depth, JSObject *globalObject, unsigned flags) { return ResolveResult(DynamicIndexedGlobal | flags, 0, index, depth, globalObject); } static ResolveResult globalResolve(JSObject *globalObject) { return ResolveResult(Global, 0, missingSymbolMarker(), 0, globalObject); } static ResolveResult dynamicGlobalResolve(size_t dynamicDepth, JSObject *globalObject) { return ResolveResult(DynamicGlobal, 0, missingSymbolMarker(), dynamicDepth, globalObject); } unsigned type() const { return m_type; } // Returns the register corresponding to a local variable, or 0 if no // such register exists. Registers returned by ResolveResult::local() do // not require explicit reference counting. RegisterID* local() const { return m_local; } int index() const { ASSERT (isIndexed() || isRegister()); return m_index; } size_t depth() const { ASSERT(isScoped()); return m_depth; } JSObject* globalObject() const { ASSERT(isGlobal()); ASSERT(m_globalObject); return m_globalObject; } bool isRegister() const { return m_type & RegisterFlag; } bool isDynamic() const { return m_type & DynamicFlag; } bool isStatic() const { return m_type & StaticFlag; } bool isIndexed() const { return m_type & IndexedFlag; } bool isScoped() const { return m_type & ScopedFlag; } bool isReadOnly() const { return (m_type & ReadOnlyFlag) && !isDynamic(); } bool isGlobal() const { return m_type & GlobalFlag; } private: ResolveResult(unsigned type, RegisterID* local, int index, size_t depth, JSObject* globalObject) : m_type(type) , m_index(index) , m_local(local) , m_depth(depth) , m_globalObject(globalObject) { #ifndef NDEBUG checkValidity(); #endif } #ifndef NDEBUG void checkValidity(); #endif unsigned m_type; int m_index; // Index in scope, if IndexedFlag is set RegisterID* m_local; // Local register, if RegisterFlag is set size_t m_depth; // Depth in scope chain, if ScopedFlag is set JSObject* m_globalObject; // If GlobalFlag is set. }; 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(); bool isArgumentNumber(const Identifier&, int); void setIsNumericCompareFunction(bool isNumericCompareFunction); bool willResolveToArguments(const Identifier&); RegisterID* uncheckedRegisterForArguments(); // Resolve an identifier, given the current compile-time scope chain. ResolveResult resolve(const Identifier&); // Behaves as resolve does, but ignores dynamic scope as // dynamic scope should not interfere with const initialisation ResolveResult resolveConstDecl(const Identifier&); // Returns the register storing "this" RegisterID* thisRegister() { return &m_thisRegister; } // 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 newLabelScope(LabelScope::Type, const Identifier* = 0); PassRefPtr