/* * Copyright (C) 2008 Apple Inc. All rights reserved. * Copyright (C) 2008 Cameron Zwarich * * 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 CodeBlock_h #define CodeBlock_h #include "Instruction.h" #include "JSGlobalObject.h" #include "nodes.h" #include "Parser.h" #include "SourceCode.h" #include "ustring.h" #include #include namespace JSC { class ExecState; enum CodeType { GlobalCode, EvalCode, FunctionCode }; static ALWAYS_INLINE int missingThisObjectMarker() { return std::numeric_limits::max(); } struct HandlerInfo { uint32_t start; uint32_t end; uint32_t target; uint32_t scopeDepth; void* nativeCode; }; struct ExpressionRangeInfo { enum { MaxOffset = (1 << 7) - 1, MaxDivot = (1 << 25) - 1 }; uint32_t instructionOffset : 25; uint32_t divotPoint : 25; uint32_t startOffset : 7; uint32_t endOffset : 7; }; struct LineInfo { uint32_t instructionOffset; int32_t lineNumber; }; struct OffsetLocation { int32_t branchOffset; #if ENABLE(CTI) void* ctiOffset; #endif }; struct StructureStubInfo { StructureStubInfo(unsigned opcodeIndex) : opcodeIndex(opcodeIndex) , stubRoutine(0) , callReturnLocation(0) , hotPathBegin(0) { } unsigned opcodeIndex; void* stubRoutine; void* callReturnLocation; void* hotPathBegin; }; struct CallLinkInfo { CallLinkInfo() : callReturnLocation(0) , hotPathBegin(0) , hotPathOther(0) , coldPathOther(0) , callee(0) { } unsigned opcodeIndex; void* callReturnLocation; void* hotPathBegin; void* hotPathOther; void* coldPathOther; CodeBlock* callee; unsigned position; void setUnlinked() { callee = 0; } bool isLinked() { return callee; } }; inline void* getStructureStubInfoReturnLocation(StructureStubInfo* structureStubInfo) { return structureStubInfo->callReturnLocation; } // Binary chop algorithm, calls valueAtPosition on pre-sorted elements in array, // compares result with key (KeyTypes should be comparable with '--', '<', '>'). // Optimized for cases where the array contains the key, checked by assertions. template inline ArrayType* binaryChop(ArrayType* array, size_t size, KeyType key) { // The array must contain at least one element (pre-condition, array does conatin key). // If the array only contains one element, no need to do the comparison. while (size > 1) { // Pick an element to check, half way through the array, and read the value. int pos = (size - 1) >> 1; KeyType val = valueAtPosition(&array[pos]); // If the key matches, success! if (val == key) return &array[pos]; // The item we are looking for is smaller than the item being check; reduce the value of 'size', // chopping off the right hand half of the array. else if (key < val) size = pos; // Discard all values in the left hand half of the array, up to and including the item at pos. else { size -= (pos + 1); array += (pos + 1); } // 'size' should never reach zero. ASSERT(size); } // If we reach this point we've chopped down to one element, no need to check it matches ASSERT(size == 1); ASSERT(key == valueAtPosition(&array[0])); return &array[0]; } struct StringJumpTable { typedef HashMap, OffsetLocation> StringOffsetTable; StringOffsetTable offsetTable; #if ENABLE(CTI) void* ctiDefault; // FIXME: it should not be necessary to store this. #endif inline int32_t offsetForValue(UString::Rep* value, int32_t defaultOffset) { StringOffsetTable::const_iterator end = offsetTable.end(); StringOffsetTable::const_iterator loc = offsetTable.find(value); if (loc == end) return defaultOffset; return loc->second.branchOffset; } #if ENABLE(CTI) inline void* ctiForValue(UString::Rep* value) { StringOffsetTable::const_iterator end = offsetTable.end(); StringOffsetTable::const_iterator loc = offsetTable.find(value); if (loc == end) return ctiDefault; return loc->second.ctiOffset; } #endif }; struct SimpleJumpTable { // FIXME: The two Vectors can be combind into one Vector Vector branchOffsets; int32_t min; #if ENABLE(CTI) Vector ctiOffsets; void* ctiDefault; #endif int32_t offsetForValue(int32_t value, int32_t defaultOffset); void add(int32_t key, int32_t offset) { if (!branchOffsets[key]) branchOffsets[key] = offset; } #if ENABLE(CTI) inline void* ctiForValue(int32_t value) { if (value >= min && static_cast(value - min) < ctiOffsets.size()) return ctiOffsets[value - min]; return ctiDefault; } #endif }; class EvalCodeCache { public: PassRefPtr get(ExecState* exec, const UString& evalSource, ScopeChainNode* scopeChain, JSValue*& exceptionValue) { RefPtr evalNode; if (evalSource.size() < maxCacheableSourceLength && (*scopeChain->begin())->isVariableObject()) evalNode = cacheMap.get(evalSource.rep()); if (!evalNode) { int errLine; UString errMsg; SourceCode source = makeSource(evalSource); evalNode = exec->globalData().parser->parse(exec, exec->dynamicGlobalObject()->debugger(), source, &errLine, &errMsg); if (evalNode) { if (evalSource.size() < maxCacheableSourceLength && (*scopeChain->begin())->isVariableObject() && cacheMap.size() < maxCacheEntries) cacheMap.set(evalSource.rep(), evalNode); } else { exceptionValue = Error::create(exec, SyntaxError, errMsg, errLine, source.provider()->asID(), NULL); return 0; } } return evalNode.release(); } private: static const int maxCacheableSourceLength = 256; static const int maxCacheEntries = 64; HashMap, RefPtr > cacheMap; }; struct CodeBlock { CodeBlock(ScopeNode* ownerNode, CodeType codeType, PassRefPtr sourceProvider, unsigned sourceOffset) : ownerNode(ownerNode) , globalData(0) #if ENABLE(CTI) , ctiCode(0) #endif , numCalleeRegisters(0) , numConstants(0) , numVars(0) , numParameters(0) , needsFullScopeChain(ownerNode->needsActivation()) , usesEval(ownerNode->usesEval()) , codeType(codeType) , source(sourceProvider) , sourceOffset(sourceOffset) { ASSERT(source); } ~CodeBlock(); #if ENABLE(CTI) void unlinkCallers(); #endif void addCaller(CallLinkInfo* caller) { caller->callee = this; caller->position = linkedCallerList.size(); linkedCallerList.append(caller); } void removeCaller(CallLinkInfo* caller) { unsigned pos = caller->position; unsigned lastPos = linkedCallerList.size() - 1; if (pos != lastPos) { linkedCallerList[pos] = linkedCallerList[lastPos]; linkedCallerList[pos]->position = pos; } linkedCallerList.shrink(lastPos); } #if !defined(NDEBUG) || ENABLE_OPCODE_SAMPLING void dump(ExecState*) const; void printStructureIDs(const Instruction*) const; void printStructureID(const char* name, const Instruction*, int operand) const; #endif int expressionRangeForVPC(const Instruction*, int& divot, int& startOffset, int& endOffset); int lineNumberForVPC(const Instruction* vPC); bool getHandlerForVPC(const Instruction* vPC, Instruction*& target, int& scopeDepth); void* nativeExceptionCodeForHandlerVPC(const Instruction* handlerVPC); void mark(); void refStructureIDs(Instruction* vPC) const; void derefStructureIDs(Instruction* vPC) const; StructureStubInfo& getStubInfo(void* returnAddress) { return *(binaryChop(propertyAccessInstructions.begin(), propertyAccessInstructions.size(), returnAddress)); } ScopeNode* ownerNode; JSGlobalData* globalData; #if ENABLE(CTI) void* ctiCode; #endif int numCalleeRegisters; // NOTE: numConstants holds the number of constant registers allocated // by the code generator, not the number of constant registers used. // (Duplicate constants are uniqued during code generation, and spare // constant registers may be allocated.) int numConstants; int numVars; int numParameters; int thisRegister; bool needsFullScopeChain; bool usesEval; bool usesArguments; CodeType codeType; RefPtr source; unsigned sourceOffset; Vector instructions; Vector globalResolveInstructions; Vector propertyAccessInstructions; Vector callLinkInfos; Vector linkedCallerList; // Constant pool Vector identifiers; Vector > functions; Vector > functionExpressions; Vector constantRegisters; Vector unexpectedConstants; Vector > regexps; Vector exceptionHandlers; Vector expressionInfo; Vector lineInfo; Vector immediateSwitchJumpTables; Vector characterSwitchJumpTables; Vector stringSwitchJumpTables; HashSet::Hash, WTF::UnsignedWithZeroKeyHashTraits > labels; #if ENABLE(CTI) HashMap ctiReturnAddressVPCMap; #endif EvalCodeCache evalCodeCache; private: #if !defined(NDEBUG) || ENABLE(OPCODE_SAMPLING) void dump(ExecState*, const Vector::const_iterator& begin, Vector::const_iterator&) const; #endif }; // Program code is not marked by any function, so we make the global object // responsible for marking it. struct ProgramCodeBlock : public CodeBlock { ProgramCodeBlock(ScopeNode* ownerNode, CodeType codeType, JSGlobalObject* globalObject, PassRefPtr sourceProvider) : CodeBlock(ownerNode, codeType, sourceProvider, 0) , globalObject(globalObject) { globalObject->codeBlocks().add(this); } ~ProgramCodeBlock() { if (globalObject) globalObject->codeBlocks().remove(this); } JSGlobalObject* globalObject; // For program and eval nodes, the global object that marks the constant pool. }; struct EvalCodeBlock : public ProgramCodeBlock { EvalCodeBlock(ScopeNode* ownerNode, JSGlobalObject* globalObject, PassRefPtr sourceProvider) : ProgramCodeBlock(ownerNode, EvalCode, globalObject, sourceProvider) { } }; } // namespace JSC #endif // CodeBlock_h