diff options
| author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-05-24 08:28:08 +0000 |
|---|---|---|
| committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-05-24 08:28:08 +0000 |
| commit | a4e969f4965059196ca948db781e52f7cfebf19e (patch) | |
| tree | 6ca352808c8fdc52006a0f33f6ae3c593b23867d /Source/JavaScriptCore/interpreter | |
| parent | 41386e9cb918eed93b3f13648cbef387e371e451 (diff) | |
| download | WebKitGtk-tarball-a4e969f4965059196ca948db781e52f7cfebf19e.tar.gz | |
webkitgtk-2.12.3webkitgtk-2.12.3
Diffstat (limited to 'Source/JavaScriptCore/interpreter')
20 files changed, 1244 insertions, 1951 deletions
diff --git a/Source/JavaScriptCore/interpreter/AbstractPC.cpp b/Source/JavaScriptCore/interpreter/AbstractPC.cpp index 8600b7228..09b24d5dc 100644 --- a/Source/JavaScriptCore/interpreter/AbstractPC.cpp +++ b/Source/JavaScriptCore/interpreter/AbstractPC.cpp @@ -27,9 +27,9 @@ #include "AbstractPC.h" #include "CallFrame.h" -#include "VM.h" #include "JSObject.h" - +#include "JSCInlines.h" +#include "VM.h" namespace JSC { diff --git a/Source/JavaScriptCore/interpreter/AbstractPC.h b/Source/JavaScriptCore/interpreter/AbstractPC.h index c30027d9e..ac2aaa7ad 100644 --- a/Source/JavaScriptCore/interpreter/AbstractPC.h +++ b/Source/JavaScriptCore/interpreter/AbstractPC.h @@ -27,7 +27,6 @@ #define AbstractPC_h #include "MacroAssemblerCodeRef.h" -#include <wtf/Platform.h> namespace JSC { diff --git a/Source/JavaScriptCore/interpreter/CachedCall.h b/Source/JavaScriptCore/interpreter/CachedCall.h index 2ca3e9794..30cad2a14 100644 --- a/Source/JavaScriptCore/interpreter/CachedCall.h +++ b/Source/JavaScriptCore/interpreter/CachedCall.h @@ -43,7 +43,7 @@ namespace JSC { , m_interpreter(callFrame->interpreter()) , m_entryScope(callFrame->vm(), function->scope()->globalObject()) { - ASSERT(!function->isHostFunction()); + ASSERT(!function->isHostFunctionNonInline()); if (callFrame->vm().isSafeToRecurse()) { m_arguments.resize(argumentCount); m_closure = m_interpreter->prepareForRepeatCall(function->jsExecutable(), callFrame, &m_protoCallFrame, function, argumentCount + 1, function->scope(), m_arguments.data()); diff --git a/Source/JavaScriptCore/interpreter/CallFrame.cpp b/Source/JavaScriptCore/interpreter/CallFrame.cpp index a226e9848..3d3897b6b 100644 --- a/Source/JavaScriptCore/interpreter/CallFrame.cpp +++ b/Source/JavaScriptCore/interpreter/CallFrame.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2013 Apple Inc. All Rights Reserved. + * Copyright (C) 2008, 2013, 2014 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,14 +26,74 @@ #include "config.h" #include "CallFrame.h" -#include "CallFrameInlines.h" #include "CodeBlock.h" +#include "InlineCallFrame.h" #include "Interpreter.h" -#include "Operations.h" +#include "JSLexicalEnvironment.h" +#include "JSCInlines.h" #include "VMEntryScope.h" +#include <wtf/StringPrintStream.h> namespace JSC { +bool CallFrame::callSiteBitsAreBytecodeOffset() const +{ + ASSERT(codeBlock()); + switch (codeBlock()->jitType()) { + case JITCode::InterpreterThunk: + case JITCode::BaselineJIT: + return true; + case JITCode::None: + case JITCode::HostCallThunk: + RELEASE_ASSERT_NOT_REACHED(); + return false; + default: + return false; + } + + RELEASE_ASSERT_NOT_REACHED(); + return false; +} + +bool CallFrame::callSiteBitsAreCodeOriginIndex() const +{ + ASSERT(codeBlock()); + switch (codeBlock()->jitType()) { + case JITCode::DFGJIT: + case JITCode::FTLJIT: + return true; + case JITCode::None: + case JITCode::HostCallThunk: + RELEASE_ASSERT_NOT_REACHED(); + return false; + default: + return false; + } + + RELEASE_ASSERT_NOT_REACHED(); + return false; +} + +unsigned CallFrame::callSiteAsRawBits() const +{ + return this[JSStack::ArgumentCount].tag(); +} + +SUPPRESS_ASAN unsigned CallFrame::unsafeCallSiteAsRawBits() const +{ + return this[JSStack::ArgumentCount].unsafeTag(); +} + +CallSiteIndex CallFrame::callSiteIndex() const +{ + return CallSiteIndex(callSiteAsRawBits()); +} + +SUPPRESS_ASAN CallSiteIndex CallFrame::unsafeCallSiteIndex() const +{ + return CallSiteIndex(unsafeCallSiteAsRawBits()); +} + #ifndef NDEBUG JSStack* CallFrame::stack() { @@ -43,63 +103,63 @@ JSStack* CallFrame::stack() #endif #if USE(JSVALUE32_64) -unsigned CallFrame::locationAsBytecodeOffset() const +Instruction* CallFrame::currentVPC() const { - ASSERT(codeBlock()); - ASSERT(hasLocationAsBytecodeOffset()); - return currentVPC() - codeBlock()->instructions().begin(); + return bitwise_cast<Instruction*>(callSiteIndex().bits()); +} + +void CallFrame::setCurrentVPC(Instruction* vpc) +{ + CallSiteIndex callSite(vpc); + this[JSStack::ArgumentCount].tag() = callSite.bits(); } -void CallFrame::setLocationAsBytecodeOffset(unsigned offset) +unsigned CallFrame::callSiteBitsAsBytecodeOffset() const { ASSERT(codeBlock()); - setCurrentVPC(codeBlock()->instructions().begin() + offset); - ASSERT(hasLocationAsBytecodeOffset()); + ASSERT(callSiteBitsAreBytecodeOffset()); + return currentVPC() - codeBlock()->instructions().begin(); } -#else + +#else // USE(JSVALUE32_64) Instruction* CallFrame::currentVPC() const { - return codeBlock()->instructions().begin() + locationAsBytecodeOffset(); + ASSERT(callSiteBitsAreBytecodeOffset()); + return codeBlock()->instructions().begin() + callSiteBitsAsBytecodeOffset(); } + void CallFrame::setCurrentVPC(Instruction* vpc) { - setLocationAsBytecodeOffset(vpc - codeBlock()->instructions().begin()); + CallSiteIndex callSite(vpc - codeBlock()->instructions().begin()); + this[JSStack::ArgumentCount].tag() = static_cast<int32_t>(callSite.bits()); } -#endif - -#if ENABLE(DFG_JIT) -unsigned CallFrame::bytecodeOffsetFromCodeOriginIndex() -{ - ASSERT(hasLocationAsCodeOriginIndex()); - CodeBlock* codeBlock = this->codeBlock(); - ASSERT(codeBlock); - - CodeOrigin codeOrigin; - unsigned index = locationAsCodeOriginIndex(); - ASSERT(codeBlock->canGetCodeOrigin(index)); - codeOrigin = codeBlock->codeOrigin(index); - for (InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame; inlineCallFrame;) { - if (inlineCallFrame->baselineCodeBlock() == codeBlock) - return codeOrigin.bytecodeIndex; - - codeOrigin = inlineCallFrame->caller; - inlineCallFrame = codeOrigin.inlineCallFrame; - } - return codeOrigin.bytecodeIndex; +unsigned CallFrame::callSiteBitsAsBytecodeOffset() const +{ + ASSERT(codeBlock()); + ASSERT(callSiteBitsAreBytecodeOffset()); + return callSiteIndex().bits(); } -#endif // ENABLE(DFG_JIT) - +#endif + unsigned CallFrame::bytecodeOffset() { if (!codeBlock()) return 0; #if ENABLE(DFG_JIT) - if (hasLocationAsCodeOriginIndex()) - return bytecodeOffsetFromCodeOriginIndex(); + if (callSiteBitsAreCodeOriginIndex()) { + ASSERT(codeBlock()); + CodeOrigin codeOrigin = this->codeOrigin(); + for (InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame; inlineCallFrame;) { + codeOrigin = inlineCallFrame->directCaller; + inlineCallFrame = codeOrigin.inlineCallFrame; + } + return codeOrigin.bytecodeIndex; + } #endif - return locationAsBytecodeOffset(); + ASSERT(callSiteBitsAreBytecodeOffset()); + return callSiteBitsAsBytecodeOffset(); } CodeOrigin CallFrame::codeOrigin() @@ -107,20 +167,20 @@ CodeOrigin CallFrame::codeOrigin() if (!codeBlock()) return CodeOrigin(0); #if ENABLE(DFG_JIT) - if (hasLocationAsCodeOriginIndex()) { - unsigned index = locationAsCodeOriginIndex(); + if (callSiteBitsAreCodeOriginIndex()) { + CallSiteIndex index = callSiteIndex(); ASSERT(codeBlock()->canGetCodeOrigin(index)); return codeBlock()->codeOrigin(index); } #endif - return CodeOrigin(locationAsBytecodeOffset()); + return CodeOrigin(callSiteBitsAsBytecodeOffset()); } -Register* CallFrame::frameExtentInternal() +Register* CallFrame::topOfFrameInternal() { CodeBlock* codeBlock = this->codeBlock(); ASSERT(codeBlock); - return registers() + virtualRegisterForLocal(codeBlock->frameRegisterCount()).offset(); + return registers() + codeBlock->stackPointerOffset(); } JSGlobalObject* CallFrame::vmEntryGlobalObject() @@ -134,4 +194,84 @@ JSGlobalObject* CallFrame::vmEntryGlobalObject() return vm().entryScope->globalObject(); } +CallFrame* CallFrame::callerFrame(VMEntryFrame*& currVMEntryFrame) +{ + if (callerFrameOrVMEntryFrame() == currVMEntryFrame) { + VMEntryRecord* currVMEntryRecord = vmEntryRecord(currVMEntryFrame); + currVMEntryFrame = currVMEntryRecord->prevTopVMEntryFrame(); + return currVMEntryRecord->prevTopCallFrame(); + } + return static_cast<CallFrame*>(callerFrameOrVMEntryFrame()); +} + +SUPPRESS_ASAN CallFrame* CallFrame::unsafeCallerFrame(VMEntryFrame*& currVMEntryFrame) +{ + if (unsafeCallerFrameOrVMEntryFrame() == currVMEntryFrame) { + VMEntryRecord* currVMEntryRecord = vmEntryRecord(currVMEntryFrame); + currVMEntryFrame = currVMEntryRecord->unsafePrevTopVMEntryFrame(); + return currVMEntryRecord->unsafePrevTopCallFrame(); + } + return static_cast<CallFrame*>(unsafeCallerFrameOrVMEntryFrame()); +} + +String CallFrame::friendlyFunctionName() +{ + CodeBlock* codeBlock = this->codeBlock(); + if (!codeBlock) + return emptyString(); + + switch (codeBlock->codeType()) { + case EvalCode: + return ASCIILiteral("eval code"); + case ModuleCode: + return ASCIILiteral("module code"); + case GlobalCode: + return ASCIILiteral("global code"); + case FunctionCode: + if (callee()) + return getCalculatedDisplayName(this, callee()); + return emptyString(); + } + + ASSERT_NOT_REACHED(); + return emptyString(); +} + +void CallFrame::dump(PrintStream& out) +{ + if (CodeBlock* codeBlock = this->codeBlock()) { + out.print(codeBlock->inferredName(), "#", codeBlock->hashAsStringIfPossible(), " [", codeBlock->jitType(), "]"); + + out.print("("); + thisValue().dumpForBacktrace(out); + + for (size_t i = 0; i < argumentCount(); ++i) { + out.print(", "); + JSValue value = argument(i); + value.dumpForBacktrace(out); + } + + out.print(")"); + + return; + } + + out.print(returnPC()); +} + +const char* CallFrame::describeFrame() +{ + const size_t bufferSize = 200; + static char buffer[bufferSize + 1]; + + WTF::StringPrintStream stringStream; + + dump(stringStream); + + strncpy(buffer, stringStream.toCString().data(), bufferSize); + buffer[bufferSize] = '\0'; + + return buffer; +} + } // namespace JSC diff --git a/Source/JavaScriptCore/interpreter/CallFrame.h b/Source/JavaScriptCore/interpreter/CallFrame.h index 48fbcd779..980b92fb2 100644 --- a/Source/JavaScriptCore/interpreter/CallFrame.h +++ b/Source/JavaScriptCore/interpreter/CallFrame.h @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2003, 2007, 2008, 2011, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2007, 2008, 2011, 2013-2015 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,30 +24,55 @@ #define CallFrame_h #include "AbstractPC.h" -#include "VM.h" #include "JSStack.h" #include "MacroAssemblerCodeRef.h" #include "Register.h" #include "StackVisitor.h" +#include "VM.h" +#include "VMEntryRecord.h" namespace JSC { class Arguments; - class JSActivation; class Interpreter; class JSScope; + struct CallSiteIndex { + CallSiteIndex() + : m_bits(UINT_MAX) + { + } + + explicit CallSiteIndex(uint32_t bits) + : m_bits(bits) + { } +#if USE(JSVALUE32_64) + explicit CallSiteIndex(Instruction* instruction) + : m_bits(bitwise_cast<uint32_t>(instruction)) + { } +#endif + + explicit operator bool() const { return m_bits != UINT_MAX; } + + inline uint32_t bits() const { return m_bits; } + + private: + uint32_t m_bits; + }; + // Represents the current state of script execution. // Passed as the first argument to most functions. class ExecState : private Register { public: JSValue calleeAsValue() const { return this[JSStack::Callee].jsValue(); } - JSObject* callee() const { return this[JSStack::Callee].function(); } + JSObject* callee() const { return this[JSStack::Callee].object(); } + SUPPRESS_ASAN JSValue unsafeCallee() const { return this[JSStack::Callee].asanUnsafeJSValue(); } CodeBlock* codeBlock() const { return this[JSStack::CodeBlock].Register::codeBlock(); } - JSScope* scope() const + SUPPRESS_ASAN CodeBlock* unsafeCodeBlock() const { return this[JSStack::CodeBlock].Register::asanUnsafeCodeBlock(); } + JSScope* scope(int scopeRegisterOffset) const { - ASSERT(this[JSStack::ScopeChain].Register::scope()); - return this[JSStack::ScopeChain].Register::scope(); + ASSERT(this[scopeRegisterOffset].Register::scope()); + return this[scopeRegisterOffset].Register::scope(); } // Global object in which execution began. @@ -69,49 +94,33 @@ namespace JSC { // But they're used in many places in legacy code, so they're not going away any time soon. void clearException() { vm().clearException(); } - void clearSupplementaryExceptionInfo() - { - vm().clearExceptionStack(); - } - JSValue exception() const { return vm().exception(); } - bool hadException() const { return !vm().exception().isEmpty(); } + Exception* exception() const { return vm().exception(); } + bool hadException() const { return !!vm().exception(); } + Exception* lastException() const { return vm().lastException(); } + void clearLastException() { vm().clearLastException(); } + + AtomicStringTable* atomicStringTable() const { return vm().atomicStringTable(); } const CommonIdentifiers& propertyNames() const { return *vm().propertyNames; } const MarkedArgumentBuffer& emptyList() const { return *vm().emptyList; } Interpreter* interpreter() { return vm().interpreter; } Heap* heap() { return &vm().heap; } -#ifndef NDEBUG - void dumpCaller(); -#endif - static const HashTable& arrayConstructorTable(VM& vm) { return *vm.arrayConstructorTable; } - static const HashTable& arrayPrototypeTable(VM& vm) { return *vm.arrayPrototypeTable; } - static const HashTable& booleanPrototypeTable(VM& vm) { return *vm.booleanPrototypeTable; } - static const HashTable& dataViewTable(VM& vm) { return *vm.dataViewTable; } - static const HashTable& dateTable(VM& vm) { return *vm.dateTable; } - static const HashTable& dateConstructorTable(VM& vm) { return *vm.dateConstructorTable; } - static const HashTable& errorPrototypeTable(VM& vm) { return *vm.errorPrototypeTable; } - static const HashTable& globalObjectTable(VM& vm) { return *vm.globalObjectTable; } - static const HashTable& jsonTable(VM& vm) { return *vm.jsonTable; } - static const HashTable& numberConstructorTable(VM& vm) { return *vm.numberConstructorTable; } - static const HashTable& numberPrototypeTable(VM& vm) { return *vm.numberPrototypeTable; } - static const HashTable& objectConstructorTable(VM& vm) { return *vm.objectConstructorTable; } - static const HashTable& privateNamePrototypeTable(VM& vm) { return *vm.privateNamePrototypeTable; } - static const HashTable& regExpTable(VM& vm) { return *vm.regExpTable; } - static const HashTable& regExpConstructorTable(VM& vm) { return *vm.regExpConstructorTable; } - static const HashTable& regExpPrototypeTable(VM& vm) { return *vm.regExpPrototypeTable; } - static const HashTable& stringConstructorTable(VM& vm) { return *vm.stringConstructorTable; } -#if ENABLE(PROMISES) - static const HashTable& promisePrototypeTable(VM& vm) { return *vm.promisePrototypeTable; } - static const HashTable& promiseConstructorTable(VM& vm) { return *vm.promiseConstructorTable; } -#endif + static CallFrame* create(Register* callFrameBase) { return static_cast<CallFrame*>(callFrameBase); } Register* registers() { return this; } + const Register* registers() const { return this; } CallFrame& operator=(const Register& r) { *static_cast<Register*>(this) = r; return *this; } - CallFrame* callerFrame() const { return callerFrameAndPC().callerFrame; } + CallFrame* callerFrame() const { return static_cast<CallFrame*>(callerFrameOrVMEntryFrame()); } + void* callerFrameOrVMEntryFrame() const { return callerFrameAndPC().callerFrame; } + SUPPRESS_ASAN void* unsafeCallerFrameOrVMEntryFrame() const { return unsafeCallerFrameAndPC().callerFrame; } + + CallFrame* unsafeCallerFrame(VMEntryFrame*&); + JS_EXPORT_PRIVATE CallFrame* callerFrame(VMEntryFrame*&); + static ptrdiff_t callerFrameOffset() { return OBJECT_OFFSETOF(CallerFrameAndPC, callerFrame); } ReturnAddressPtr returnPC() const { return ReturnAddressPtr(callerFrameAndPC().pc); } @@ -120,51 +129,17 @@ namespace JSC { static ptrdiff_t returnPCOffset() { return OBJECT_OFFSETOF(CallerFrameAndPC, pc); } AbstractPC abstractReturnPC(VM& vm) { return AbstractPC(vm, this); } - class Location { - public: - static inline uint32_t decode(uint32_t bits); - - static inline bool isBytecodeLocation(uint32_t bits); -#if USE(JSVALUE64) - static inline uint32_t encodeAsBytecodeOffset(uint32_t bits); -#else - static inline uint32_t encodeAsBytecodeInstruction(Instruction*); -#endif - - static inline bool isCodeOriginIndex(uint32_t bits); - static inline uint32_t encodeAsCodeOriginIndex(uint32_t bits); - - private: - enum TypeTag { - BytecodeLocationTag = 0, - CodeOriginIndexTag = 1, - }; - - static inline uint32_t encode(TypeTag, uint32_t bits); - - static const uint32_t s_mask = 0x1; -#if USE(JSVALUE64) - static const uint32_t s_shift = 31; - static const uint32_t s_shiftedMask = s_mask << s_shift; -#else - static const uint32_t s_shift = 1; -#endif - }; - - bool hasLocationAsBytecodeOffset() const; - bool hasLocationAsCodeOriginIndex() const; + bool callSiteBitsAreBytecodeOffset() const; + bool callSiteBitsAreCodeOriginIndex() const; - unsigned locationAsRawBits() const; - unsigned locationAsBytecodeOffset() const; - unsigned locationAsCodeOriginIndex() const; - - void setLocationAsRawBits(unsigned); - void setLocationAsBytecodeOffset(unsigned); + unsigned callSiteAsRawBits() const; + unsigned unsafeCallSiteAsRawBits() const; + CallSiteIndex callSiteIndex() const; + CallSiteIndex unsafeCallSiteIndex() const; + private: + unsigned callSiteBitsAsBytecodeOffset() const; + public: -#if ENABLE(DFG_JIT) - unsigned bytecodeOffsetFromCodeOriginIndex(); -#endif - // This will try to get you the bytecode offset, but you should be aware that // this bytecode offset may be bogus in the presence of inlining. This will // also return 0 if the call frame has no notion of bytecode offsets (for @@ -176,51 +151,37 @@ namespace JSC { // CodeOrigin(0) if we're in native code. CodeOrigin codeOrigin(); - Register* frameExtent() + Register* topOfFrame() { - if (isVMEntrySentinel() || !codeBlock()) - return registers() - 1; - return frameExtentInternal(); + if (!codeBlock()) + return registers(); + return topOfFrameInternal(); } - Register* frameExtentInternal(); - -#if USE(JSVALUE32_64) - Instruction* currentVPC() const - { - ASSERT(!isVMEntrySentinel()); - return bitwise_cast<Instruction*>(this[JSStack::ArgumentCount].tag()); - } - void setCurrentVPC(Instruction* vpc) - { - ASSERT(!isVMEntrySentinel()); - this[JSStack::ArgumentCount].tag() = bitwise_cast<int32_t>(vpc); - } -#else - Instruction* currentVPC() const; + Instruction* currentVPC() const; // This only makes sense in the LLInt and baseline. void setCurrentVPC(Instruction* vpc); -#endif void setCallerFrame(CallFrame* frame) { callerFrameAndPC().callerFrame = frame; } - void setScope(JSScope* scope) { static_cast<Register*>(this)[JSStack::ScopeChain] = scope; } - - ALWAYS_INLINE void init(CodeBlock* codeBlock, Instruction* vPC, JSScope* scope, - CallFrame* callerFrame, int argc, JSObject* callee) - { - ASSERT(callerFrame == noCaller() || callerFrame->isVMEntrySentinel() || callerFrame->stack()->containsAddress(this)); - - setCodeBlock(codeBlock); - setScope(scope); - setCallerFrame(callerFrame); - setReturnPC(vPC); // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*. - setArgumentCountIncludingThis(argc); // original argument count (for the sake of the "arguments" object) - setCallee(callee); + void setScope(int scopeRegisterOffset, JSScope* scope) { static_cast<Register*>(this)[scopeRegisterOffset] = scope; } + + ALWAYS_INLINE void init(CodeBlock* codeBlock, Instruction* vPC, + CallFrame* callerFrame, int argc, JSObject* callee) + { + ASSERT(callerFrame == noCaller() || callerFrame->stack()->containsAddress(this)); + + setCodeBlock(codeBlock); + setCallerFrame(callerFrame); + setReturnPC(vPC); // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*. + setArgumentCountIncludingThis(argc); // original argument count (for the sake of the "arguments" object) + setCallee(callee); } // Read a register from the codeframe (or constant from the CodeBlock). Register& r(int); + Register& r(VirtualRegister); // Read a register for a non-constant Register& uncheckedR(int); + Register& uncheckedR(VirtualRegister); // Access to arguments as passed. (After capture, arguments may move to a different location.) size_t argumentCount() const { return argumentCountIncludingThis() - 1; } @@ -253,54 +214,36 @@ namespace JSC { this[argumentOffset(argument)] = value; } + JSValue getArgumentUnsafe(size_t argIndex) + { + // User beware! This method does not verify that there is a valid + // argument at the specified argIndex. This is used for debugging + // and verification code only. The caller is expected to know what + // he/she is doing when calling this method. + return this[argumentOffset(argIndex)].jsValue(); + } + static int thisArgumentOffset() { return argumentOffsetIncludingThis(0); } JSValue thisValue() { return this[thisArgumentOffset()].jsValue(); } void setThisValue(JSValue value) { this[thisArgumentOffset()] = value; } + // Under the constructor implemented in C++, thisValue holds the newTarget instead of the automatically constructed value. + // The result of this function is only effective under the "construct" context. + JSValue newTarget() { return thisValue(); } + JSValue argumentAfterCapture(size_t argument); static int offsetFor(size_t argumentCountIncludingThis) { return argumentCountIncludingThis + JSStack::ThisArgument - 1; } - // FIXME: Remove these. - int hostThisRegister() { return thisArgumentOffset(); } - JSValue hostThisValue() { return thisValue(); } - static CallFrame* noCaller() { return 0; } - bool isVMEntrySentinel() const - { - return !!this && codeBlock() == vmEntrySentinelCodeBlock(); - } - - CallFrame* vmEntrySentinelCallerFrame() const - { - ASSERT(isVMEntrySentinel()); - return this[JSStack::ScopeChain].callFrame(); - } - - void initializeVMEntrySentinelFrame(CallFrame* callFrame) - { - setCallerFrame(noCaller()); - setReturnPC(0); - setCodeBlock(vmEntrySentinelCodeBlock()); - static_cast<Register*>(this)[JSStack::ScopeChain] = callFrame; - setCallee(0); - setArgumentCountIncludingThis(0); - } - - CallFrame* callerFrameSkippingVMEntrySentinel() - { - CallFrame* caller = callerFrame(); - if (caller->isVMEntrySentinel()) - return caller->vmEntrySentinelCallerFrame(); - return caller; - } - void setArgumentCountIncludingThis(int count) { static_cast<Register*>(this)[JSStack::ArgumentCount].payload() = count; } - void setCallee(JSObject* callee) { static_cast<Register*>(this)[JSStack::Callee] = Register::withCallee(callee); } + void setCallee(JSObject* callee) { static_cast<Register*>(this)[JSStack::Callee] = callee; } void setCodeBlock(CodeBlock* codeBlock) { static_cast<Register*>(this)[JSStack::CodeBlock] = codeBlock; } void setReturnPC(void* value) { callerFrameAndPC().pc = reinterpret_cast<Instruction*>(value); } + String friendlyFunctionName(); + // CallFrame::iterate() expects a Functor that implements the following method: // StackVisitor::Status operator()(StackVisitor&); @@ -309,8 +252,10 @@ namespace JSC { StackVisitor::visit<Functor>(this, functor); } + void dump(PrintStream&); + JS_EXPORT_PRIVATE const char* describeFrame(); + private: - static const intptr_t s_VMEntrySentinel = 1; #ifndef NDEBUG JSStack* stack(); @@ -318,6 +263,8 @@ namespace JSC { ExecState(); ~ExecState(); + Register* topOfFrameInternal(); + // The following are for internal use in debugging and verification // code only and not meant as an API for general usage: @@ -338,22 +285,11 @@ namespace JSC { return argIndex; } - JSValue getArgumentUnsafe(size_t argIndex) - { - // User beware! This method does not verify that there is a valid - // argument at the specified argIndex. This is used for debugging - // and verification code only. The caller is expected to know what - // he/she is doing when calling this method. - return this[argumentOffset(argIndex)].jsValue(); - } - CallerFrameAndPC& callerFrameAndPC() { return *reinterpret_cast<CallerFrameAndPC*>(this); } const CallerFrameAndPC& callerFrameAndPC() const { return *reinterpret_cast<const CallerFrameAndPC*>(this); } - - static CodeBlock* vmEntrySentinelCodeBlock() { return reinterpret_cast<CodeBlock*>(s_VMEntrySentinel); } + SUPPRESS_ASAN const CallerFrameAndPC& unsafeCallerFrameAndPC() const { return *reinterpret_cast<const CallerFrameAndPC*>(this); } friend class JSStack; - friend class VMInspector; }; } // namespace JSC diff --git a/Source/JavaScriptCore/interpreter/CallFrameClosure.h b/Source/JavaScriptCore/interpreter/CallFrameClosure.h index e3326626d..2c03b7452 100644 --- a/Source/JavaScriptCore/interpreter/CallFrameClosure.h +++ b/Source/JavaScriptCore/interpreter/CallFrameClosure.h @@ -49,11 +49,6 @@ struct CallFrameClosure { { protoCallFrame->setArgument(argument, value); } - - void resetCallFrame() - { - protoCallFrame->setScope(scope); - } }; } diff --git a/Source/JavaScriptCore/interpreter/CallFrameInlines.h b/Source/JavaScriptCore/interpreter/CallFrameInlines.h deleted file mode 100644 index 51d751a51..000000000 --- a/Source/JavaScriptCore/interpreter/CallFrameInlines.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2013 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR - * 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 CallFrameInlines_h -#define CallFrameInlines_h - -#include "CallFrame.h" - -namespace JSC { - -inline uint32_t CallFrame::Location::encode(CallFrame::Location::TypeTag tag, uint32_t bits) -{ -#if USE(JSVALUE64) - ASSERT(!(bits & s_shiftedMask)); - ASSERT(!(tag & ~s_mask)); - return bits | (tag << s_shift); -#else - ASSERT(!(tag & ~s_mask)); - if (tag & CodeOriginIndexTag) - bits = (bits << s_shift); - ASSERT(!(bits & s_mask)); - bits |= tag; - return bits; -#endif -} - -inline uint32_t CallFrame::Location::decode(uint32_t bits) -{ -#if USE(JSVALUE64) - return bits & ~s_shiftedMask; -#else - if (isCodeOriginIndex(bits)) - return bits >> s_shift; - return bits & ~s_mask; -#endif -} - -#if USE(JSVALUE64) -inline uint32_t CallFrame::Location::encodeAsBytecodeOffset(uint32_t bits) -{ - uint32_t encodedBits = encode(BytecodeLocationTag, bits); - ASSERT(isBytecodeLocation(encodedBits)); - return encodedBits; -} -#else -inline uint32_t CallFrame::Location::encodeAsBytecodeInstruction(Instruction* instruction) -{ - uint32_t encodedBits = encode(BytecodeLocationTag, reinterpret_cast<uint32_t>(instruction)); - ASSERT(isBytecodeLocation(encodedBits)); - return encodedBits; -} -#endif - -inline uint32_t CallFrame::Location::encodeAsCodeOriginIndex(uint32_t bits) -{ - uint32_t encodedBits = encode(CodeOriginIndexTag, bits); - ASSERT(isCodeOriginIndex(encodedBits)); - return encodedBits; -} - -inline bool CallFrame::Location::isBytecodeLocation(uint32_t bits) -{ - return !isCodeOriginIndex(bits); -} - -inline bool CallFrame::Location::isCodeOriginIndex(uint32_t bits) -{ -#if USE(JSVALUE64) - TypeTag tag = static_cast<TypeTag>(bits >> s_shift); - return !!(tag & CodeOriginIndexTag); -#else - return !!(bits & CodeOriginIndexTag); -#endif -} - -inline bool CallFrame::hasLocationAsBytecodeOffset() const -{ - return Location::isBytecodeLocation(locationAsRawBits()); -} - -inline bool CallFrame::hasLocationAsCodeOriginIndex() const -{ - return Location::isCodeOriginIndex(locationAsRawBits()); -} - -inline unsigned CallFrame::locationAsRawBits() const -{ - return this[JSStack::ArgumentCount].tag(); -} - -inline void CallFrame::setLocationAsRawBits(unsigned bits) -{ - this[JSStack::ArgumentCount].tag() = static_cast<int32_t>(bits); -} - -#if USE(JSVALUE64) -inline unsigned CallFrame::locationAsBytecodeOffset() const -{ - ASSERT(hasLocationAsBytecodeOffset()); - ASSERT(codeBlock()); - return Location::decode(locationAsRawBits()); -} - -inline void CallFrame::setLocationAsBytecodeOffset(unsigned offset) -{ - ASSERT(codeBlock()); - setLocationAsRawBits(Location::encodeAsBytecodeOffset(offset)); - ASSERT(hasLocationAsBytecodeOffset()); -} -#endif // USE(JSVALUE64) - -inline unsigned CallFrame::locationAsCodeOriginIndex() const -{ - ASSERT(hasLocationAsCodeOriginIndex()); - ASSERT(codeBlock()); - return Location::decode(locationAsRawBits()); -} - -} // namespace JSC - -#endif // CallFrameInlines_h diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp index 4fbc8229a..18a90760d 100644 --- a/Source/JavaScriptCore/interpreter/Interpreter.cpp +++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2010, 2012-2015 Apple Inc. All rights reserved. * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca> * * Redistribution and use in source and binary forms, with or without @@ -11,7 +11,7 @@ * 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 + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -30,25 +30,25 @@ #include "config.h" #include "Interpreter.h" -#include "Arguments.h" #include "BatchedTransitionOptimizer.h" -#include "CallFrame.h" #include "CallFrameClosure.h" -#include "CallFrameInlines.h" +#include "ClonedArguments.h" #include "CodeBlock.h" +#include "DirectArguments.h" #include "Heap.h" #include "Debugger.h" #include "DebuggerCallFrame.h" #include "ErrorInstance.h" #include "EvalCodeCache.h" +#include "Exception.h" #include "ExceptionHelpers.h" #include "GetterSetter.h" -#include "JSActivation.h" #include "JSArray.h" #include "JSBoundFunction.h" -#include "JSNameScope.h" +#include "JSCInlines.h" +#include "JSLexicalEnvironment.h" +#include "JSModuleEnvironment.h" #include "JSNotAnObject.h" -#include "JSPropertyNameIterator.h" #include "JSStackInlines.h" #include "JSString.h" #include "JSWithScope.h" @@ -56,24 +56,27 @@ #include "LLIntThunks.h" #include "LegacyProfiler.h" #include "LiteralParser.h" -#include "NameInstance.h" #include "ObjectPrototype.h" -#include "Operations.h" #include "Parser.h" #include "ProtoCallFrame.h" #include "RegExpObject.h" #include "RegExpPrototype.h" #include "Register.h" #include "SamplingTool.h" +#include "ScopedArguments.h" +#include "StackAlignment.h" #include "StackVisitor.h" #include "StrictEvalActivation.h" #include "StrongInlines.h" +#include "Symbol.h" #include "VMEntryScope.h" +#include "VMInlines.h" #include "VirtualRegister.h" #include <limits.h> #include <stdio.h> #include <wtf/StackStats.h> +#include <wtf/StdLibExtras.h> #include <wtf/StringPrintStream.h> #include <wtf/Threading.h> #include <wtf/WTFThreadData.h> @@ -83,26 +86,53 @@ #include "JIT.h" #endif -#define WTF_USE_GCC_COMPUTED_GOTO_WORKAROUND (ENABLE(LLINT) && !defined(__llvm__)) - using namespace std; namespace JSC { -Interpreter::ErrorHandlingMode::ErrorHandlingMode(ExecState *exec) - : m_interpreter(*exec->interpreter()) +String StackFrame::friendlySourceURL() const { - if (!m_interpreter.m_errorHandlingModeReentry) - m_interpreter.stack().enableErrorStackReserve(); - m_interpreter.m_errorHandlingModeReentry++; + String traceLine; + + switch (codeType) { + case StackFrameEvalCode: + case StackFrameModuleCode: + case StackFrameFunctionCode: + case StackFrameGlobalCode: + if (!sourceURL.isEmpty()) + traceLine = sourceURL.impl(); + break; + case StackFrameNativeCode: + traceLine = "[native code]"; + break; + } + return traceLine.isNull() ? emptyString() : traceLine; } -Interpreter::ErrorHandlingMode::~ErrorHandlingMode() +String StackFrame::friendlyFunctionName(CallFrame* callFrame) const { - m_interpreter.m_errorHandlingModeReentry--; - ASSERT(m_interpreter.m_errorHandlingModeReentry >= 0); - if (!m_interpreter.m_errorHandlingModeReentry) - m_interpreter.stack().disableErrorStackReserve(); + String traceLine; + JSObject* stackFrameCallee = callee.get(); + + switch (codeType) { + case StackFrameEvalCode: + traceLine = "eval code"; + break; + case StackFrameModuleCode: + traceLine = "module code"; + break; + case StackFrameNativeCode: + if (callee) + traceLine = getCalculatedDisplayName(callFrame, stackFrameCallee).impl(); + break; + case StackFrameFunctionCode: + traceLine = getCalculatedDisplayName(callFrame, stackFrameCallee).impl(); + break; + case StackFrameGlobalCode: + traceLine = "global code"; + break; + } + return traceLine.isNull() ? emptyString() : traceLine; } JSValue eval(CallFrame* callFrame) @@ -113,21 +143,27 @@ JSValue eval(CallFrame* callFrame) JSValue program = callFrame->argument(0); if (!program.isString()) return program; - + TopCallFrameSetter topCallFrame(callFrame->vm(), callFrame); + JSGlobalObject* globalObject = callFrame->lexicalGlobalObject(); + if (!globalObject->evalEnabled()) { + callFrame->vm().throwException(callFrame, createEvalError(callFrame, globalObject->evalDisabledErrorMessage())); + return jsUndefined(); + } String programSource = asString(program)->value(callFrame); if (callFrame->hadException()) return JSValue(); CallFrame* callerFrame = callFrame->callerFrame(); CodeBlock* callerCodeBlock = callerFrame->codeBlock(); - JSScope* callerScopeChain = callerFrame->scope(); - EvalExecutable* eval = callerCodeBlock->evalCodeCache().tryGet(callerCodeBlock->isStrictMode(), programSource, callerScopeChain); + JSScope* callerScopeChain = callerFrame->uncheckedR(callerCodeBlock->scopeRegister().offset()).Register::scope(); + UnlinkedCodeBlock* callerUnlinkedCodeBlock = callerCodeBlock->unlinkedCodeBlock(); + + bool isArrowFunctionContext = callerUnlinkedCodeBlock->isArrowFunction() || callerUnlinkedCodeBlock->isArrowFunctionContext(); + EvalExecutable* eval = callerCodeBlock->evalCodeCache().tryGet(callerCodeBlock->isStrictMode(), programSource, isArrowFunctionContext, callerScopeChain); if (!eval) { if (!callerCodeBlock->isStrictMode()) { - // FIXME: We can use the preparser in strict mode, we just need additional logic - // to prevent duplicates. if (programSource.is8Bit()) { LiteralParser<LChar> preparser(callFrame, programSource.characters8(), programSource.length(), NonStrictJSON); if (JSValue parsedObject = preparser.tryLiteralParse()) @@ -142,7 +178,11 @@ JSValue eval(CallFrame* callFrame) // If the literal parser bailed, it should not have thrown exceptions. ASSERT(!callFrame->vm().exception()); - eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock->ownerExecutable(), callerCodeBlock->isStrictMode(), programSource, callerScopeChain); + ThisTDZMode thisTDZMode = ThisTDZMode::CheckIfNeeded; + if (callerUnlinkedCodeBlock->constructorKind() == ConstructorKind::Derived) + thisTDZMode = ThisTDZMode::AlwaysCheck; + + eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock, callerCodeBlock->isStrictMode(), thisTDZMode, callerCodeBlock->unlinkedCodeBlock()->derivedContextType(), callerCodeBlock->unlinkedCodeBlock()->isArrowFunction(), programSource, callerScopeChain); if (!eval) return jsUndefined(); } @@ -152,109 +192,103 @@ JSValue eval(CallFrame* callFrame) return interpreter->execute(eval, callFrame, thisValue, callerScopeChain); } -CallFrame* sizeAndAllocFrameForVarargs(CallFrame* callFrame, JSStack* stack, JSValue arguments, int firstFreeRegister) +unsigned sizeOfVarargs(CallFrame* callFrame, JSValue arguments, uint32_t firstVarArgOffset) { - if (!arguments) { // f.apply(x, arguments), with arguments unmodified. - unsigned argumentCountIncludingThis = callFrame->argumentCountIncludingThis(); - CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister - argumentCountIncludingThis - JSStack::CallFrameHeaderSize - 1); - if (argumentCountIncludingThis > Arguments::MaxArguments + 1 || !stack->grow(newCallFrame->registers())) { - callFrame->vm().throwException(callFrame, createStackOverflowError(callFrame)); + if (UNLIKELY(!arguments.isCell())) { + if (arguments.isUndefinedOrNull()) return 0; - } - return newCallFrame; - } - - if (arguments.isUndefinedOrNull()) { - CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister - 1 - JSStack::CallFrameHeaderSize - 1); - if (!stack->grow(newCallFrame->registers())) { - callFrame->vm().throwException(callFrame, createStackOverflowError(callFrame)); - return 0; - } - return newCallFrame; - } - - if (!arguments.isObject()) { - callFrame->vm().throwException(callFrame, createInvalidParameterError(callFrame, "Function.prototype.apply", arguments)); + + callFrame->vm().throwException(callFrame, createInvalidFunctionApplyParameterError(callFrame, arguments)); return 0; } - - if (asObject(arguments)->classInfo() == Arguments::info()) { - Arguments* argsObject = asArguments(arguments); - unsigned argCount = argsObject->length(callFrame); - CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister - CallFrame::offsetFor(argCount + 1)); - if (argCount > Arguments::MaxArguments || !stack->grow(newCallFrame->registers())) { - callFrame->vm().throwException(callFrame, createStackOverflowError(callFrame)); - return 0; - } - return newCallFrame; - } - - if (isJSArray(arguments)) { - JSArray* array = asArray(arguments); - unsigned argCount = array->length(); - CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister - CallFrame::offsetFor(argCount + 1)); - if (argCount > Arguments::MaxArguments || !stack->grow(newCallFrame->registers())) { - callFrame->vm().throwException(callFrame, createStackOverflowError(callFrame)); - return 0; - } - return newCallFrame; + + JSCell* cell = arguments.asCell(); + unsigned length; + switch (cell->type()) { + case DirectArgumentsType: + length = jsCast<DirectArguments*>(cell)->length(callFrame); + break; + case ScopedArgumentsType: + length =jsCast<ScopedArguments*>(cell)->length(callFrame); + break; + case StringType: + callFrame->vm().throwException(callFrame, createInvalidFunctionApplyParameterError(callFrame, arguments)); + return 0; + default: + ASSERT(arguments.isObject()); + if (isJSArray(cell)) + length = jsCast<JSArray*>(cell)->length(); + else + length = jsCast<JSObject*>(cell)->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame); + break; } + + if (length >= firstVarArgOffset) + length -= firstVarArgOffset; + else + length = 0; + + return length; +} - JSObject* argObject = asObject(arguments); - unsigned argCount = argObject->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame); - CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister - CallFrame::offsetFor(argCount + 1)); - if (argCount > Arguments::MaxArguments || !stack->grow(newCallFrame->registers())) { - callFrame->vm().throwException(callFrame, createStackOverflowError(callFrame)); +unsigned sizeFrameForVarargs(CallFrame* callFrame, JSStack* stack, JSValue arguments, unsigned numUsedStackSlots, uint32_t firstVarArgOffset) +{ + unsigned length = sizeOfVarargs(callFrame, arguments, firstVarArgOffset); + + CallFrame* calleeFrame = calleeFrameForVarargs(callFrame, numUsedStackSlots, length + 1); + if (length > maxArguments || !stack->ensureCapacityFor(calleeFrame->registers())) { + throwStackOverflowError(callFrame); return 0; } - return newCallFrame; + + return length; } -void loadVarargs(CallFrame* callFrame, CallFrame* newCallFrame, JSValue thisValue, JSValue arguments) +void loadVarargs(CallFrame* callFrame, VirtualRegister firstElementDest, JSValue arguments, uint32_t offset, uint32_t length) { - if (!arguments) { // f.apply(x, arguments), with arguments unmodified. - unsigned argumentCountIncludingThis = callFrame->argumentCountIncludingThis(); - - newCallFrame->setArgumentCountIncludingThis(argumentCountIncludingThis); - newCallFrame->setThisValue(thisValue); - for (size_t i = 0; i < callFrame->argumentCount(); ++i) - newCallFrame->setArgument(i, callFrame->argumentAfterCapture(i)); + if (UNLIKELY(!arguments.isCell()) || !length) return; - } - if (arguments.isUndefinedOrNull()) { - newCallFrame->setArgumentCountIncludingThis(1); - newCallFrame->setThisValue(thisValue); + JSCell* cell = arguments.asCell(); + switch (cell->type()) { + case DirectArgumentsType: + jsCast<DirectArguments*>(cell)->copyToArguments(callFrame, firstElementDest, offset, length); return; - } - - if (asObject(arguments)->classInfo() == Arguments::info()) { - Arguments* argsObject = asArguments(arguments); - unsigned argCount = argsObject->length(callFrame); - newCallFrame->setArgumentCountIncludingThis(argCount + 1); - newCallFrame->setThisValue(thisValue); - argsObject->copyToArguments(callFrame, newCallFrame, argCount); + case ScopedArgumentsType: + jsCast<ScopedArguments*>(cell)->copyToArguments(callFrame, firstElementDest, offset, length); return; - } - - if (isJSArray(arguments)) { - JSArray* array = asArray(arguments); - unsigned argCount = array->length(); - newCallFrame->setArgumentCountIncludingThis(argCount + 1); - newCallFrame->setThisValue(thisValue); - array->copyToArguments(callFrame, newCallFrame, argCount); + default: { + ASSERT(arguments.isObject()); + JSObject* object = jsCast<JSObject*>(cell); + if (isJSArray(object)) { + jsCast<JSArray*>(object)->copyToArguments(callFrame, firstElementDest, offset, length); + return; + } + unsigned i; + for (i = 0; i < length && object->canGetIndexQuickly(i + offset); ++i) + callFrame->r(firstElementDest + i) = object->getIndexQuickly(i + offset); + for (; i < length; ++i) + callFrame->r(firstElementDest + i) = object->get(callFrame, i + offset); return; - } + } } +} + +void setupVarargsFrame(CallFrame* callFrame, CallFrame* newCallFrame, JSValue arguments, uint32_t offset, uint32_t length) +{ + VirtualRegister calleeFrameOffset(newCallFrame - callFrame); - JSObject* argObject = asObject(arguments); - unsigned argCount = argObject->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame); - newCallFrame->setArgumentCountIncludingThis(argCount + 1); + loadVarargs( + callFrame, + calleeFrameOffset + CallFrame::argumentOffset(0), + arguments, offset, length); + + newCallFrame->setArgumentCountIncludingThis(length + 1); +} + +void setupVarargsFrameAndSetThis(CallFrame* callFrame, CallFrame* newCallFrame, JSValue thisValue, JSValue arguments, uint32_t firstVarArgOffset, uint32_t length) +{ + setupVarargsFrame(callFrame, newCallFrame, arguments, firstVarArgOffset, length); newCallFrame->setThisValue(thisValue); - for (size_t i = 0; i < argCount; ++i) { - newCallFrame->setArgument(i, asObject(arguments)->get(callFrame, i)); - if (UNLIKELY(callFrame->vm().exception())) - return; - } } Interpreter::Interpreter(VM& vm) @@ -272,11 +306,9 @@ Interpreter::~Interpreter() { } -void Interpreter::initialize(bool canUseJIT) +void Interpreter::initialize() { - UNUSED_PARAM(canUseJIT); - -#if ENABLE(COMPUTED_GOTO_OPCODES) && ENABLE(LLINT) +#if ENABLE(COMPUTED_GOTO_OPCODES) m_opcodeTable = LLInt::opcodeMap(); for (int i = 0; i < numOpcodeIDs; ++i) m_opcodeIDTable.add(m_opcodeTable[i], static_cast<OpcodeID>(i)); @@ -361,7 +393,7 @@ void Interpreter::dumpRegisters(CallFrame* callFrame) --it; dataLogF("[Callee] | %10p | %p \n", it, callFrame->callee()); --it; - dataLogF("[ScopeChain] | %10p | %p \n", it, callFrame->scope()); + // FIXME: Remove the next decrement when the ScopeChain slot is removed from the call header --it; #if ENABLE(JIT) AbstractPC pc = callFrame->abstractReturnPC(callFrame->vm()); @@ -388,7 +420,7 @@ void Interpreter::dumpRegisters(CallFrame* callFrame) } dataLogF("-----------------------------------------------------------------------------\n"); - end = it - codeBlock->m_numCalleeRegisters + codeBlock->m_numVars; + end = it - codeBlock->m_numCalleeLocals + codeBlock->m_numVars; if (it != end) { do { JSValue v = (*it).jsValue(); @@ -405,68 +437,21 @@ void Interpreter::dumpRegisters(CallFrame* callFrame) bool Interpreter::isOpcode(Opcode opcode) { #if ENABLE(COMPUTED_GOTO_OPCODES) -#if !ENABLE(LLINT) - return static_cast<OpcodeID>(bitwise_cast<uintptr_t>(opcode)) <= op_end; -#else return opcode != HashTraits<Opcode>::emptyValue() && !HashTraits<Opcode>::isDeletedValue(opcode) && m_opcodeIDTable.contains(opcode); -#endif #else return opcode >= 0 && opcode <= op_end; #endif } -static bool unwindCallFrame(StackVisitor& visitor) -{ - CallFrame* callFrame = visitor->callFrame(); - CodeBlock* codeBlock = visitor->codeBlock(); - CodeBlock* oldCodeBlock = codeBlock; - JSScope* scope = callFrame->scope(); - - if (Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger()) { - if (callFrame->callee()) - debugger->returnEvent(callFrame); - else - debugger->didExecuteProgram(callFrame); - } - - JSValue activation; - if (oldCodeBlock->codeType() == FunctionCode && oldCodeBlock->needsActivation()) { -#if ENABLE(DFG_JIT) - RELEASE_ASSERT(!visitor->isInlinedFrame()); -#endif - activation = callFrame->uncheckedR(oldCodeBlock->activationRegister().offset()).jsValue(); - if (activation) - jsCast<JSActivation*>(activation)->tearOff(*scope->vm()); - } - - if (oldCodeBlock->codeType() == FunctionCode && oldCodeBlock->usesArguments()) { - if (Arguments* arguments = visitor->existingArguments()) { - if (activation) - arguments->didTearOffActivation(callFrame, jsCast<JSActivation*>(activation)); -#if ENABLE(DFG_JIT) - else if (visitor->isInlinedFrame()) - arguments->tearOff(callFrame, visitor->inlineCallFrame()); -#endif - else - arguments->tearOff(callFrame); - } - } - - CallFrame* callerFrame = callFrame->callerFrame(); - if (callerFrame->isVMEntrySentinel()) { - callFrame->vm().topCallFrame = callerFrame->vmEntrySentinelCallerFrame(); - return false; - } - return true; -} - static StackFrameCodeType getStackFrameCodeType(StackVisitor& visitor) { switch (visitor->codeType()) { case StackVisitor::Frame::Eval: return StackFrameEvalCode; + case StackVisitor::Frame::Module: + return StackFrameModuleCode; case StackVisitor::Frame::Function: return StackFrameFunctionCode; case StackVisitor::Frame::Global: @@ -496,6 +481,9 @@ void StackFrame::computeLineAndColumn(unsigned& line, unsigned& column) line = divotLine + lineOffset; column = divotColumn + (divotLine ? 1 : firstLineColumnOffset); + + if (executable->hasOverrideLineNumber()) + line = executable->overrideLineNumber(); } void StackFrame::expressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column) @@ -528,6 +516,16 @@ String StackFrame::toString(CallFrame* callFrame) return traceBuild.toString().impl(); } +static inline bool isWebAssemblyExecutable(ExecutableBase* executable) +{ +#if !ENABLE(WEBASSEMBLY) + UNUSED_PARAM(executable); + return false; +#else + return executable->isWebAssemblyExecutable(); +#endif +} + class GetStackTraceFunctor { public: GetStackTraceFunctor(VM& vm, Vector<StackFrame>& results, size_t remainingCapacity) @@ -541,15 +539,17 @@ public: { VM& vm = m_vm; if (m_remainingCapacityForFrameCapture) { - if (visitor->isJSFrame()) { + if (visitor->isJSFrame() + && !isWebAssemblyExecutable(visitor->codeBlock()->ownerExecutable()) + && !visitor->codeBlock()->unlinkedCodeBlock()->isBuiltinFunction()) { CodeBlock* codeBlock = visitor->codeBlock(); StackFrame s = { Strong<JSObject>(vm, visitor->callee()), getStackFrameCodeType(visitor), - Strong<ExecutableBase>(vm, codeBlock->ownerExecutable()), + Strong<ScriptExecutable>(vm, codeBlock->ownerScriptExecutable()), Strong<UnlinkedCodeBlock>(vm, codeBlock->unlinkedCodeBlock()), codeBlock->source(), - codeBlock->ownerExecutable()->lineNo(), + codeBlock->ownerScriptExecutable()->firstLine(), codeBlock->firstLineColumnOffset(), codeBlock->sourceOffset(), visitor->bytecodeOffset(), @@ -557,7 +557,7 @@ public: }; m_results.append(s); } else { - StackFrame s = { Strong<JSObject>(vm, visitor->callee()), StackFrameNativeCode, Strong<ExecutableBase>(), Strong<UnlinkedCodeBlock>(), 0, 0, 0, 0, 0, String()}; + StackFrame s = { Strong<JSObject>(vm, visitor->callee()), StackFrameNativeCode, Strong<ScriptExecutable>(), Strong<UnlinkedCodeBlock>(), 0, 0, 0, 0, 0, String()}; m_results.append(s); } @@ -576,7 +576,6 @@ private: void Interpreter::getStackTrace(Vector<StackFrame>& results, size_t maxStackSize) { VM& vm = m_vm; - ASSERT(!vm.topCallFrame->isVMEntrySentinel()); CallFrame* callFrame = vm.topCallFrame; if (!callFrame) return; @@ -597,9 +596,26 @@ JSString* Interpreter::stackTraceAsString(ExecState* exec, Vector<StackFrame> st return jsString(&exec->vm(), builder.toString()); } -class GetExceptionHandlerFunctor { +ALWAYS_INLINE static HandlerInfo* findExceptionHandler(StackVisitor& visitor, CodeBlock* codeBlock, CodeBlock::RequiredHandler requiredHandler) +{ + ASSERT(codeBlock); +#if ENABLE(DFG_JIT) + ASSERT(!visitor->isInlinedFrame()); +#endif + + CallFrame* callFrame = visitor->callFrame(); + unsigned exceptionHandlerIndex; + if (JITCode::isOptimizingJIT(codeBlock->jitType())) + exceptionHandlerIndex = callFrame->callSiteIndex().bits(); + else + exceptionHandlerIndex = callFrame->bytecodeOffset(); + + return codeBlock->handlerForIndex(exceptionHandlerIndex, requiredHandler); +} + +class GetCatchHandlerFunctor { public: - GetExceptionHandlerFunctor() + GetCatchHandlerFunctor() : m_handler(0) { } @@ -608,12 +624,13 @@ public: StackVisitor::Status operator()(StackVisitor& visitor) { + visitor.unwindToMachineCodeBlockFrame(); + CodeBlock* codeBlock = visitor->codeBlock(); if (!codeBlock) return StackVisitor::Continue; - unsigned bytecodeOffset = visitor->bytecodeOffset(); - m_handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset); + m_handler = findExceptionHandler(visitor, codeBlock, CodeBlock::RequiredHandler::CatchHandler); if (m_handler) return StackVisitor::Done; @@ -624,6 +641,18 @@ private: HandlerInfo* m_handler; }; +ALWAYS_INLINE static void notifyDebuggerOfUnwinding(CallFrame* callFrame) +{ + if (Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger()) { + SuspendExceptionScope scope(&callFrame->vm()); + if (jsDynamicCast<JSFunction*>(callFrame->callee())) + debugger->returnEvent(callFrame); + else + debugger->didExecuteProgram(callFrame); + ASSERT(!callFrame->hadException()); + } +} + class UnwindFunctor { public: UnwindFunctor(CallFrame*& callFrame, bool isTermination, CodeBlock*& codeBlock, HandlerInfo*& handler) @@ -636,35 +665,92 @@ public: StackVisitor::Status operator()(StackVisitor& visitor) { + visitor.unwindToMachineCodeBlockFrame(); VM& vm = m_callFrame->vm(); m_callFrame = visitor->callFrame(); m_codeBlock = visitor->codeBlock(); - unsigned bytecodeOffset = visitor->bytecodeOffset(); - if (m_isTermination || !(m_handler = m_codeBlock->handlerForBytecodeOffset(bytecodeOffset))) { - if (!unwindCallFrame(visitor)) { - if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->exceptionUnwind(m_callFrame); - return StackVisitor::Done; - } - } else + m_handler = nullptr; + if (!m_isTermination) { + if (m_codeBlock && !isWebAssemblyExecutable(m_codeBlock->ownerExecutable())) + m_handler = findExceptionHandler(visitor, m_codeBlock, CodeBlock::RequiredHandler::AnyHandler); + } + + if (m_handler) return StackVisitor::Done; + notifyDebuggerOfUnwinding(m_callFrame); + + bool shouldStopUnwinding = visitor->callerIsVMEntryFrame(); + if (shouldStopUnwinding) { + if (LegacyProfiler* profiler = vm.enabledProfiler()) + profiler->exceptionUnwind(m_callFrame); + + copyCalleeSavesToVMCalleeSavesBuffer(visitor); + + return StackVisitor::Done; + } + + copyCalleeSavesToVMCalleeSavesBuffer(visitor); + return StackVisitor::Continue; } private: + void copyCalleeSavesToVMCalleeSavesBuffer(StackVisitor& visitor) + { +#if ENABLE(JIT) && NUMBER_OF_CALLEE_SAVES_REGISTERS > 0 + + if (!visitor->isJSFrame()) + return; + +#if ENABLE(DFG_JIT) + if (visitor->inlineCallFrame()) + return; +#endif + RegisterAtOffsetList* currentCalleeSaves = m_codeBlock ? m_codeBlock->calleeSaveRegisters() : nullptr; + + if (!currentCalleeSaves) + return; + + VM& vm = m_callFrame->vm(); + RegisterAtOffsetList* allCalleeSaves = vm.getAllCalleeSaveRegisterOffsets(); + RegisterSet dontCopyRegisters = RegisterSet::stackRegisters(); + intptr_t* frame = reinterpret_cast<intptr_t*>(m_callFrame->registers()); + + unsigned registerCount = currentCalleeSaves->size(); + for (unsigned i = 0; i < registerCount; i++) { + RegisterAtOffset currentEntry = currentCalleeSaves->at(i); + if (dontCopyRegisters.get(currentEntry.reg())) + continue; + RegisterAtOffset* vmCalleeSavesEntry = allCalleeSaves->find(currentEntry.reg()); + + vm.calleeSaveRegistersBuffer[vmCalleeSavesEntry->offsetAsIndex()] = *(frame + currentEntry.offsetAsIndex()); + } +#else + UNUSED_PARAM(visitor); +#endif + } + CallFrame*& m_callFrame; bool m_isTermination; CodeBlock*& m_codeBlock; HandlerInfo*& m_handler; }; -NEVER_INLINE HandlerInfo* Interpreter::unwind(CallFrame*& callFrame, JSValue& exceptionValue) +NEVER_INLINE HandlerInfo* Interpreter::unwind(VM& vm, CallFrame*& callFrame, Exception* exception, UnwindStart unwindStart) { + if (unwindStart == UnwindFromCallerFrame) { + if (callFrame->callerFrameOrVMEntryFrame() == vm.topVMEntryFrame) + return nullptr; + + callFrame = callFrame->callerFrame(); + vm.topCallFrame = callFrame; + } + CodeBlock* codeBlock = callFrame->codeBlock(); - bool isTermination = false; + JSValue exceptionValue = exception->value(); ASSERT(!exceptionValue.isEmpty()); ASSERT(!exceptionValue.isCell() || exceptionValue.asCell()); // This shouldn't be possible (hence the assertions), but we're already in the slowest of @@ -672,60 +758,42 @@ NEVER_INLINE HandlerInfo* Interpreter::unwind(CallFrame*& callFrame, JSValue& ex if (exceptionValue.isEmpty() || (exceptionValue.isCell() && !exceptionValue.asCell())) exceptionValue = jsNull(); - if (exceptionValue.isObject()) { - isTermination = isTerminatedExecutionException(asObject(exceptionValue)); - } + ASSERT(vm.exception() && vm.exception()->stack().size()); - ASSERT(callFrame->vm().exceptionStack().size()); + // Calculate an exception handler vPC, unwinding call frames as necessary. + HandlerInfo* handler = nullptr; + UnwindFunctor functor(callFrame, isTerminatedExecutionException(exception), codeBlock, handler); + callFrame->iterate(functor); + if (!handler) + return nullptr; + + return handler; +} +void Interpreter::notifyDebuggerOfExceptionToBeThrown(CallFrame* callFrame, Exception* exception) +{ Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger(); - if (debugger && debugger->needsExceptionCallbacks()) { - // We need to clear the exception and the exception stack here in order to see if a new exception happens. - // Afterwards, the values are put back to continue processing this error. - ClearExceptionScope scope(&callFrame->vm()); + if (debugger && debugger->needsExceptionCallbacks() && !exception->didNotifyInspectorOfThrow()) { // This code assumes that if the debugger is enabled then there is no inlining. // If that assumption turns out to be false then we'll ignore the inlined call // frames. // https://bugs.webkit.org/show_bug.cgi?id=121754 - bool hasHandler; + bool hasCatchHandler; + bool isTermination = isTerminatedExecutionException(exception); if (isTermination) - hasHandler = false; + hasCatchHandler = false; else { - GetExceptionHandlerFunctor functor; + GetCatchHandlerFunctor functor; callFrame->iterate(functor); - hasHandler = !!functor.handler(); + HandlerInfo* handler = functor.handler(); + ASSERT(!handler || handler->isCatchHandler()); + hasCatchHandler = !!handler; } - debugger->exception(callFrame, exceptionValue, hasHandler); + debugger->exception(callFrame, exception->value(), hasCatchHandler); } - - // Calculate an exception handler vPC, unwinding call frames as necessary. - HandlerInfo* handler = 0; - VM& vm = callFrame->vm(); - ASSERT(callFrame == vm.topCallFrame); - UnwindFunctor functor(callFrame, isTermination, codeBlock, handler); - callFrame->iterate(functor); - if (!handler) - return 0; - - if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->exceptionUnwind(callFrame); - - // Unwind the scope chain within the exception handler's call frame. - int targetScopeDepth = handler->scopeDepth; - if (codeBlock->needsActivation() && callFrame->uncheckedR(codeBlock->activationRegister().offset()).jsValue()) - ++targetScopeDepth; - - JSScope* scope = callFrame->scope(); - int scopeDelta = scope->depth() - targetScopeDepth; - RELEASE_ASSERT(scopeDelta >= 0); - - while (scopeDelta--) - scope = scope->next(); - callFrame->setScope(scope); - - return handler; + exception->setDidNotifyInspectorOfThrow(); } static inline JSValue checkedReturn(JSValue returnValue) @@ -758,16 +826,16 @@ private: JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, JSObject* thisObj) { SamplingScope samplingScope(this); - - JSScope* scope = callFrame->scope(); + + JSScope* scope = thisObj->globalObject()->globalScope(); VM& vm = *scope->vm(); ASSERT(!vm.exception()); ASSERT(!vm.isCollectorBusy()); + RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); if (vm.isCollectorBusy()) return jsNull(); - VMEntryScope entryScope(vm, scope->globalObject()); if (!vm.isSafeToRecurse()) return checkedReturn(throwStackOverflowError(callFrame)); @@ -777,7 +845,7 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J Vector<JSONPData> JSONPData; bool parseResult; - const String programSource = program->source().toString(); + StringView programSource = program->source().view(); if (programSource.isNull()) return jsUndefined(); if (programSource.is8Bit()) { @@ -808,10 +876,10 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J switch (JSONPPath[i].m_type) { case JSONPPathEntryTypeDot: { if (i == 0) { - PropertySlot slot(globalObject); + PropertySlot slot(globalObject, PropertySlot::InternalMethodType::Get); if (!globalObject->getPropertySlot(callFrame, JSONPPath[i].m_pathEntryName, slot)) { if (entry) - return callFrame->vm().throwException(callFrame, createUndefinedVariableError(globalObject->globalExec(), JSONPPath[i].m_pathEntryName)); + return callFrame->vm().throwException(callFrame, createUndefinedVariableError(callFrame, JSONPPath[i].m_pathEntryName)); goto failedJSONP; } baseObject = slot.getValue(callFrame, JSONPPath[i].m_pathEntryName); @@ -874,40 +942,37 @@ failedJSONP: // If we get here, then we have already proven that the script is not a JSON // object. + VMEntryScope entryScope(vm, scope->globalObject()); + // Compile source to bytecode if necessary: if (JSObject* error = program->initializeGlobalProperties(vm, callFrame, scope)) return checkedReturn(callFrame->vm().throwException(callFrame, error)); - if (JSObject* error = program->prepareForExecution(callFrame, scope, CodeForCall)) + if (JSObject* error = program->prepareForExecution(callFrame, nullptr, scope, CodeForCall)) return checkedReturn(callFrame->vm().throwException(callFrame, error)); ProgramCodeBlock* codeBlock = program->codeBlock(); - if (UNLIKELY(vm.watchdog.didFire(callFrame))) + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) return throwTerminatedExecutionException(callFrame); ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. - if (UNLIKELY(!m_stack.entryCheck(codeBlock, 1))) - return checkedReturn(throwStackOverflowError(callFrame)); - ProtoCallFrame protoCallFrame; - protoCallFrame.init(codeBlock, scope, 0, thisObj, 1); + protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), thisObj, 1); if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->willExecute(callFrame, program->sourceURL(), program->lineNo()); + profiler->willExecute(callFrame, program->sourceURL(), program->firstLine(), program->startColumn()); // Execute the code: JSValue result; { SamplingTool::CallRecord callRecord(m_sampler.get()); - Watchdog::Scope watchdogScope(vm.watchdog); - - result = program->generatedJITCode()->execute(&vm, &protoCallFrame, m_stack.getTopOfStack()); + result = program->generatedJITCode()->execute(&vm, &protoCallFrame); } if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->didExecute(callFrame, program->sourceURL(), program->lineNo()); + profiler->didExecute(callFrame, program->sourceURL(), program->firstLine(), program->startColumn()); return checkedReturn(result); } @@ -921,24 +986,27 @@ JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallT return jsNull(); bool isJSCall = (callType == CallTypeJS); - JSScope* scope; + JSScope* scope = nullptr; CodeBlock* newCodeBlock; size_t argsCount = 1 + args.size(); // implicit "this" parameter - if (isJSCall) + JSGlobalObject* globalObject; + + if (isJSCall) { scope = callData.js.scope; - else { + globalObject = scope->globalObject(); + } else { ASSERT(callType == CallTypeHost); - scope = callFrame->scope(); + globalObject = function->globalObject(); } - VMEntryScope entryScope(vm, scope->globalObject()); + VMEntryScope entryScope(vm, globalObject); if (!vm.isSafeToRecurse()) return checkedReturn(throwStackOverflowError(callFrame)); if (isJSCall) { // Compile the callee: - JSObject* compileError = callData.js.functionExecutable->prepareForExecution(callFrame, scope, CodeForCall); + JSObject* compileError = callData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(function), scope, CodeForCall); if (UNLIKELY(!!compileError)) { return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); } @@ -948,14 +1016,11 @@ JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallT } else newCodeBlock = 0; - if (UNLIKELY(vm.watchdog.didFire(callFrame))) + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) return throwTerminatedExecutionException(callFrame); - if (UNLIKELY(!m_stack.entryCheck(newCodeBlock, argsCount))) - return checkedReturn(throwStackOverflowError(callFrame)); - ProtoCallFrame protoCallFrame; - protoCallFrame.init(newCodeBlock, scope, function, thisValue, argsCount, args.data()); + protoCallFrame.init(newCodeBlock, function, thisValue, argsCount, args.data()); if (LegacyProfiler* profiler = vm.enabledProfiler()) profiler->willExecute(callFrame, function); @@ -963,13 +1028,15 @@ JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallT JSValue result; { SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSCall); - Watchdog::Scope watchdogScope(vm.watchdog); // Execute the code: if (isJSCall) - result = callData.js.functionExecutable->generatedJITCodeForCall()->execute(&vm, &protoCallFrame, m_stack.getTopOfStack()); - else - result = JSValue::decode(callToNativeFunction(reinterpret_cast<void*>(callData.native.function), &vm.topCallFrame, &protoCallFrame, m_stack.getTopOfStack())); + result = callData.js.functionExecutable->generatedJITCodeForCall()->execute(&vm, &protoCallFrame); + else { + result = JSValue::decode(vmEntryToNative(reinterpret_cast<void*>(callData.native.function), &vm, &protoCallFrame)); + if (callFrame->hadException()) + result = jsNull(); + } } if (LegacyProfiler* profiler = vm.enabledProfiler()) @@ -978,7 +1045,7 @@ JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallT return checkedReturn(result); } -JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* constructor, ConstructType constructType, const ConstructData& constructData, const ArgList& args) +JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* constructor, ConstructType constructType, const ConstructData& constructData, const ArgList& args, JSValue newTarget) { VM& vm = callFrame->vm(); ASSERT(!callFrame->hadException()); @@ -989,24 +1056,27 @@ JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* construc return checkedReturn(throwStackOverflowError(callFrame)); bool isJSConstruct = (constructType == ConstructTypeJS); - JSScope* scope; + JSScope* scope = nullptr; CodeBlock* newCodeBlock; size_t argsCount = 1 + args.size(); // implicit "this" parameter - if (isJSConstruct) + JSGlobalObject* globalObject; + + if (isJSConstruct) { scope = constructData.js.scope; - else { + globalObject = scope->globalObject(); + } else { ASSERT(constructType == ConstructTypeHost); - scope = callFrame->scope(); + globalObject = constructor->globalObject(); } - VMEntryScope entryScope(vm, scope->globalObject()); + VMEntryScope entryScope(vm, globalObject); if (!vm.isSafeToRecurse()) return checkedReturn(throwStackOverflowError(callFrame)); if (isJSConstruct) { // Compile the callee: - JSObject* compileError = constructData.js.functionExecutable->prepareForExecution(callFrame, scope, CodeForConstruct); + JSObject* compileError = constructData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(constructor), scope, CodeForConstruct); if (UNLIKELY(!!compileError)) { return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); } @@ -1016,14 +1086,11 @@ JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* construc } else newCodeBlock = 0; - if (UNLIKELY(vm.watchdog.didFire(callFrame))) + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) return throwTerminatedExecutionException(callFrame); - if (UNLIKELY(!m_stack.entryCheck(newCodeBlock, argsCount))) - return checkedReturn(throwStackOverflowError(callFrame)); - ProtoCallFrame protoCallFrame; - protoCallFrame.init(newCodeBlock, scope, constructor, jsUndefined(), argsCount, args.data()); + protoCallFrame.init(newCodeBlock, constructor, newTarget, argsCount, args.data()); if (LegacyProfiler* profiler = vm.enabledProfiler()) profiler->willExecute(callFrame, constructor); @@ -1031,13 +1098,12 @@ JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* construc JSValue result; { SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSConstruct); - Watchdog::Scope watchdogScope(vm.watchdog); // Execute the code. if (isJSConstruct) - result = constructData.js.functionExecutable->generatedJITCodeForConstruct()->execute(&vm, &protoCallFrame, m_stack.getTopOfStack()); + result = constructData.js.functionExecutable->generatedJITCodeForConstruct()->execute(&vm, &protoCallFrame); else { - result = JSValue::decode(callToNativeFunction(reinterpret_cast<void*>(constructData.native.function), &vm.topCallFrame, &protoCallFrame, m_stack.getTopOfStack())); + result = JSValue::decode(vmEntryToNative(reinterpret_cast<void*>(constructData.native.function), &vm, &protoCallFrame)); if (!callFrame->hadException()) RELEASE_ASSERT(result.isObject()); @@ -1062,7 +1128,7 @@ CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionE return CallFrameClosure(); // Compile the callee: - JSObject* error = functionExecutable->prepareForExecution(callFrame, scope, CodeForCall); + JSObject* error = functionExecutable->prepareForExecution(callFrame, function, scope, CodeForCall); if (error) { callFrame->vm().throwException(callFrame, error); return CallFrameClosure(); @@ -1072,12 +1138,7 @@ CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionE size_t argsCount = argumentCountIncludingThis; - if (UNLIKELY(!m_stack.entryCheck(newCodeBlock, argsCount))) { - throwStackOverflowError(callFrame); - return CallFrameClosure(); - } - - protoCallFrame->init(newCodeBlock, scope, function, jsUndefined(), argsCount, args); + protoCallFrame->init(newCodeBlock, function, jsUndefined(), argsCount, args); // Return the successful closure: CallFrameClosure result = { callFrame, protoCallFrame, function, functionExecutable, &vm, scope, newCodeBlock->numParameters(), argumentCountIncludingThis }; return result; @@ -1089,25 +1150,23 @@ JSValue Interpreter::execute(CallFrameClosure& closure) SamplingScope samplingScope(this); ASSERT(!vm.isCollectorBusy()); + RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); if (vm.isCollectorBusy()) return jsNull(); StackStats::CheckPoint stackCheckPoint; - closure.resetCallFrame(); if (LegacyProfiler* profiler = vm.enabledProfiler()) profiler->willExecute(closure.oldCallFrame, closure.function); - if (UNLIKELY(vm.watchdog.didFire(closure.oldCallFrame))) + if (UNLIKELY(vm.shouldTriggerTermination(closure.oldCallFrame))) return throwTerminatedExecutionException(closure.oldCallFrame); // Execute the code: JSValue result; { SamplingTool::CallRecord callRecord(m_sampler.get()); - Watchdog::Scope watchdogScope(vm.watchdog); - - result = closure.functionExecutable->generatedJITCodeForCall()->execute(&vm, closure.protoCallFrame, m_stack.getTopOfStack()); + result = closure.functionExecutable->generatedJITCodeForCall()->execute(&vm, closure.protoCallFrame); } if (LegacyProfiler* profiler = vm.enabledProfiler()) @@ -1124,6 +1183,7 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue ASSERT(scope->vm() == &callFrame->vm()); ASSERT(!vm.exception()); ASSERT(!vm.isCollectorBusy()); + RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); if (vm.isCollectorBusy()) return jsNull(); @@ -1136,27 +1196,55 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue JSScope* variableObject; if ((numVariables || numFunctions) && eval->isStrictMode()) { - scope = StrictEvalActivation::create(callFrame); + scope = StrictEvalActivation::create(callFrame, scope); variableObject = scope; } else { for (JSScope* node = scope; ; node = node->next()) { RELEASE_ASSERT(node); - if (node->isVariableObject() && !node->isNameScopeObject()) { + if (node->isGlobalObject()) { variableObject = node; break; + } + if (JSLexicalEnvironment* lexicalEnvironment = jsDynamicCast<JSLexicalEnvironment*>(node)) { + if (lexicalEnvironment->symbolTable()->scopeType() == SymbolTable::ScopeType::VarScope) { + variableObject = node; + break; + } } } } - JSObject* compileError = eval->prepareForExecution(callFrame, scope, CodeForCall); + JSObject* compileError = eval->prepareForExecution(callFrame, nullptr, scope, CodeForCall); if (UNLIKELY(!!compileError)) return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); EvalCodeBlock* codeBlock = eval->codeBlock(); + // We can't declare a "var"/"function" that overwrites a global "let"/"const"/"class" in a sloppy-mode eval. + if (variableObject->isGlobalObject() && !eval->isStrictMode() && (numVariables || numFunctions)) { + JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast<JSGlobalObject*>(variableObject)->globalLexicalEnvironment(); + for (unsigned i = 0; i < numVariables; ++i) { + const Identifier& ident = codeBlock->variable(i); + PropertySlot slot(globalLexicalEnvironment, PropertySlot::InternalMethodType::VMInquiry); + if (JSGlobalLexicalEnvironment::getOwnPropertySlot(globalLexicalEnvironment, callFrame, ident, slot)) { + return checkedReturn(callFrame->vm().throwException(callFrame, + createTypeError(callFrame, makeString("Can't create duplicate global variable in eval: '", String(ident.impl()), "'")))); + } + } + + for (int i = 0; i < numFunctions; ++i) { + FunctionExecutable* function = codeBlock->functionDecl(i); + PropertySlot slot(globalLexicalEnvironment, PropertySlot::InternalMethodType::VMInquiry); + if (JSGlobalLexicalEnvironment::getOwnPropertySlot(globalLexicalEnvironment, callFrame, function->name(), slot)) { + return checkedReturn(callFrame->vm().throwException(callFrame, + createTypeError(callFrame, makeString("Can't create duplicate global variable in eval: '", String(function->name().impl()), "'")))); + } + } + } + if (numVariables || numFunctions) { BatchedTransitionOptimizer optimizer(vm, variableObject); if (variableObject->next()) - variableObject->globalObject()->varInjectionWatchpoint()->fireAll(); + variableObject->globalObject()->varInjectionWatchpoint()->fireAll("Executed eval, fired VarInjection watchpoint"); for (unsigned i = 0; i < numVariables; ++i) { const Identifier& ident = codeBlock->variable(i); @@ -1173,31 +1261,74 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue } } - if (UNLIKELY(vm.watchdog.didFire(callFrame))) + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) return throwTerminatedExecutionException(callFrame); ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. - if (UNLIKELY(!m_stack.entryCheck(codeBlock, 1))) + ProtoCallFrame protoCallFrame; + protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), thisValue, 1); + + if (LegacyProfiler* profiler = vm.enabledProfiler()) + profiler->willExecute(callFrame, eval->sourceURL(), eval->firstLine(), eval->startColumn()); + + // Execute the code: + JSValue result; + { + SamplingTool::CallRecord callRecord(m_sampler.get()); + result = eval->generatedJITCode()->execute(&vm, &protoCallFrame); + } + + if (LegacyProfiler* profiler = vm.enabledProfiler()) + profiler->didExecute(callFrame, eval->sourceURL(), eval->firstLine(), eval->startColumn()); + + return checkedReturn(result); +} + +JSValue Interpreter::execute(ModuleProgramExecutable* executable, CallFrame* callFrame, JSModuleEnvironment* scope) +{ + VM& vm = *scope->vm(); + SamplingScope samplingScope(this); + + ASSERT(scope->vm() == &callFrame->vm()); + ASSERT(!vm.exception()); + ASSERT(!vm.isCollectorBusy()); + RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); + if (vm.isCollectorBusy()) + return jsNull(); + + VMEntryScope entryScope(vm, scope->globalObject()); + if (!vm.isSafeToRecurse()) return checkedReturn(throwStackOverflowError(callFrame)); + JSObject* compileError = executable->prepareForExecution(callFrame, nullptr, scope, CodeForCall); + if (UNLIKELY(!!compileError)) + return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); + ModuleProgramCodeBlock* codeBlock = executable->codeBlock(); + + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) + return throwTerminatedExecutionException(callFrame); + + ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. + + // The |this| of the module is always `undefined`. + // http://www.ecma-international.org/ecma-262/6.0/#sec-module-environment-records-hasthisbinding + // http://www.ecma-international.org/ecma-262/6.0/#sec-module-environment-records-getthisbinding ProtoCallFrame protoCallFrame; - protoCallFrame.init(codeBlock, scope, 0, thisValue, 1); + protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), jsUndefined(), 1); if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->willExecute(callFrame, eval->sourceURL(), eval->lineNo()); + profiler->willExecute(callFrame, executable->sourceURL(), executable->firstLine(), executable->startColumn()); // Execute the code: JSValue result; { SamplingTool::CallRecord callRecord(m_sampler.get()); - Watchdog::Scope watchdogScope(vm.watchdog); - - result = eval->generatedJITCode()->execute(&vm, &protoCallFrame, m_stack.getTopOfStack()); + result = executable->generatedJITCode()->execute(&vm, &protoCallFrame); } if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->didExecute(callFrame, eval->sourceURL(), eval->lineNo()); + profiler->didExecute(callFrame, executable->sourceURL(), executable->firstLine(), executable->startColumn()); return checkedReturn(result); } @@ -1207,35 +1338,38 @@ NEVER_INLINE void Interpreter::debug(CallFrame* callFrame, DebugHookID debugHook Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger(); if (!debugger) return; - ASSERT(callFrame->codeBlock()->hasDebuggerRequests() || callFrame->hadException()); + + ASSERT(callFrame->codeBlock()->hasDebuggerRequests()); + ASSERT(!callFrame->hadException()); switch (debugHookID) { case DidEnterCallFrame: debugger->callEvent(callFrame); - return; + break; case WillLeaveCallFrame: debugger->returnEvent(callFrame); - return; + break; case WillExecuteStatement: debugger->atStatement(callFrame); - return; + break; case WillExecuteProgram: debugger->willExecuteProgram(callFrame); - return; + break; case DidExecuteProgram: debugger->didExecuteProgram(callFrame); - return; + break; case DidReachBreakpoint: debugger->didReachBreakpoint(callFrame); - return; + break; } + ASSERT(!callFrame->hadException()); } void Interpreter::enableSampler() { #if ENABLE(OPCODE_SAMPLING) if (!m_sampler) { - m_sampler = adoptPtr(new SamplingTool(this)); + m_sampler = std::make_unique<SamplingTool>(this); m_sampler->setup(); } #endif diff --git a/Source/JavaScriptCore/interpreter/Interpreter.h b/Source/JavaScriptCore/interpreter/Interpreter.h index cfc721b38..aba745f17 100644 --- a/Source/JavaScriptCore/interpreter/Interpreter.h +++ b/Source/JavaScriptCore/interpreter/Interpreter.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2013, 2015 Apple Inc. All rights reserved. * Copyright (C) 2012 Research In Motion Limited. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,7 +11,7 @@ * 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 + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -33,12 +33,12 @@ #include "ArgList.h" #include "JSCJSValue.h" #include "JSCell.h" -#include "JSFunction.h" #include "JSObject.h" #include "JSStack.h" #include "LLIntData.h" #include "Opcode.h" #include "SourceProvider.h" +#include "StackAlignment.h" #include <wtf/HashMap.h> #include <wtf/text/StringBuilder.h> @@ -50,9 +50,13 @@ namespace JSC { class ExecutableBase; class FunctionExecutable; class VM; + class JSFunction; class JSGlobalObject; + class JSModuleEnvironment; + class JSModuleRecord; class LLIntOffsetsExtractor; class ProgramExecutable; + class ModuleProgramExecutable; class Register; class JSScope; class SamplingTool; @@ -61,6 +65,8 @@ namespace JSC { struct Instruction; struct ProtoCallFrame; + enum UnwindStart { UnwindFromCurrentFrame, UnwindFromCallerFrame }; + enum DebugHookID { WillExecuteProgram, DidExecuteProgram, @@ -73,6 +79,7 @@ namespace JSC { enum StackFrameCodeType { StackFrameGlobalCode, StackFrameEvalCode, + StackFrameModuleCode, StackFrameFunctionCode, StackFrameNativeCode }; @@ -80,7 +87,7 @@ namespace JSC { struct StackFrame { Strong<JSObject> callee; StackFrameCodeType codeType; - Strong<ExecutableBase> executable; + Strong<ScriptExecutable> executable; Strong<UnlinkedCodeBlock> codeBlock; RefPtr<SourceProvider> code; int lineOffset; @@ -89,65 +96,28 @@ namespace JSC { unsigned bytecodeOffset; String sourceURL; JS_EXPORT_PRIVATE String toString(CallFrame*); - String friendlySourceURL() const - { - String traceLine; - - switch (codeType) { - case StackFrameEvalCode: - case StackFrameFunctionCode: - case StackFrameGlobalCode: - if (!sourceURL.isEmpty()) - traceLine = sourceURL.impl(); - break; - case StackFrameNativeCode: - traceLine = "[native code]"; - break; - } - return traceLine.isNull() ? emptyString() : traceLine; - } - String friendlyFunctionName(CallFrame* callFrame) const - { - String traceLine; - JSObject* stackFrameCallee = callee.get(); - - switch (codeType) { - case StackFrameEvalCode: - traceLine = "eval code"; - break; - case StackFrameNativeCode: - if (callee) - traceLine = getCalculatedDisplayName(callFrame, stackFrameCallee).impl(); - break; - case StackFrameFunctionCode: - traceLine = getCalculatedDisplayName(callFrame, stackFrameCallee).impl(); - break; - case StackFrameGlobalCode: - traceLine = "global code"; - break; - } - return traceLine.isNull() ? emptyString() : traceLine; - } + String friendlySourceURL() const; + String friendlyFunctionName(CallFrame*) const; JS_EXPORT_PRIVATE void computeLineAndColumn(unsigned& line, unsigned& column); private: void expressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column); }; - class ClearExceptionScope { + class SuspendExceptionScope { public: - ClearExceptionScope(VM* vm): m_vm(vm) + SuspendExceptionScope(VM* vm) + : m_vm(vm) { - vm->getExceptionInfo(oldException, oldExceptionStack); + oldException = vm->exception(); vm->clearException(); } - ~ClearExceptionScope() + ~SuspendExceptionScope() { - m_vm->setExceptionInfo(oldException, oldExceptionStack); + m_vm->restorePreviousException(oldException); } private: - JSC::JSValue oldException; - RefCountedArray<JSC::StackFrame> oldExceptionStack; + Exception* oldException; VM* m_vm; }; @@ -157,13 +127,11 @@ namespace JSC { : vm(currentVM) , oldCallFrame(currentVM.topCallFrame) { - ASSERT(!callFrame->isVMEntrySentinel()); currentVM.topCallFrame = callFrame; } ~TopCallFrameSetter() { - ASSERT(!oldCallFrame->isVMEntrySentinel()); vm.topCallFrame = oldCallFrame; } private: @@ -177,11 +145,36 @@ namespace JSC { { ASSERT(vm); ASSERT(callFrame); - ASSERT(!callFrame->isVMEntrySentinel()); + ASSERT(callFrame < vm->topVMEntryFrame); vm->topCallFrame = callFrame; } }; + class NativeCallFrameTracerWithRestore { + public: + ALWAYS_INLINE NativeCallFrameTracerWithRestore(VM* vm, VMEntryFrame* vmEntryFrame, CallFrame* callFrame) + : m_vm(vm) + { + ASSERT(vm); + ASSERT(callFrame); + m_savedTopVMEntryFrame = vm->topVMEntryFrame; + m_savedTopCallFrame = vm->topCallFrame; + vm->topVMEntryFrame = vmEntryFrame; + vm->topCallFrame = callFrame; + } + + ALWAYS_INLINE ~NativeCallFrameTracerWithRestore() + { + m_vm->topVMEntryFrame = m_savedTopVMEntryFrame; + m_vm->topCallFrame = m_savedTopCallFrame; + } + + private: + VM* m_vm; + VMEntryFrame* m_savedTopVMEntryFrame; + CallFrame* m_savedTopCallFrame; + }; + class Interpreter { WTF_MAKE_FAST_ALLOCATED; friend class CachedCall; @@ -190,18 +183,10 @@ namespace JSC { friend class VM; public: - class ErrorHandlingMode { - public: - JS_EXPORT_PRIVATE ErrorHandlingMode(ExecState*); - JS_EXPORT_PRIVATE ~ErrorHandlingMode(); - private: - Interpreter& m_interpreter; - }; - Interpreter(VM &); ~Interpreter(); - void initialize(bool canUseJIT); + void initialize(); JSStack& stack() { return m_stack; } @@ -218,7 +203,7 @@ namespace JSC { OpcodeID getOpcodeID(Opcode opcode) { ASSERT(m_initialized); -#if ENABLE(COMPUTED_GOTO_OPCODES) && ENABLE(LLINT) +#if ENABLE(COMPUTED_GOTO_OPCODES) ASSERT(isOpcode(opcode)); return m_opcodeIDTable.get(opcode); #else @@ -230,16 +215,16 @@ namespace JSC { JSValue execute(ProgramExecutable*, CallFrame*, JSObject* thisObj); JSValue executeCall(CallFrame*, JSObject* function, CallType, const CallData&, JSValue thisValue, const ArgList&); - JSObject* executeConstruct(CallFrame*, JSObject* function, ConstructType, const ConstructData&, const ArgList&); + JSObject* executeConstruct(CallFrame*, JSObject* function, ConstructType, const ConstructData&, const ArgList&, JSValue newTarget); JSValue execute(EvalExecutable*, CallFrame*, JSValue thisValue, JSScope*); + JSValue execute(ModuleProgramExecutable*, CallFrame*, JSModuleEnvironment*); void getArgumentsData(CallFrame*, JSFunction*&, ptrdiff_t& firstParameterIndex, Register*& argv, int& argc); SamplingTool* sampler() { return m_sampler.get(); } - bool isInErrorHandlingMode() { return m_errorHandlingModeReentry; } - - NEVER_INLINE HandlerInfo* unwind(CallFrame*&, JSValue&); + NEVER_INLINE HandlerInfo* unwind(VM&, CallFrame*&, Exception*, UnwindStart); + void notifyDebuggerOfExceptionToBeThrown(CallFrame*, Exception*); NEVER_INLINE void debug(CallFrame*, DebugHookID); JSString* stackTraceAsString(ExecState*, Vector<StackFrame>); @@ -254,6 +239,8 @@ namespace JSC { JS_EXPORT_PRIVATE void dumpCallFrame(CallFrame*); + void getStackTrace(Vector<StackFrame>& results, size_t maxStackSize = std::numeric_limits<size_t>::max()); + private: enum ExecutionFlag { Normal, InitializeAndReturn }; @@ -261,21 +248,21 @@ namespace JSC { JSValue execute(CallFrameClosure&); - void getStackTrace(Vector<StackFrame>& results, size_t maxStackSize = std::numeric_limits<size_t>::max()); + void dumpRegisters(CallFrame*); - bool isCallBytecode(Opcode opcode) { return opcode == getOpcode(op_call) || opcode == getOpcode(op_construct) || opcode == getOpcode(op_call_eval); } + bool isCallBytecode(Opcode opcode) { return opcode == getOpcode(op_call) || opcode == getOpcode(op_construct) || opcode == getOpcode(op_call_eval) || opcode == getOpcode(op_tail_call); } void enableSampler(); int m_sampleEntryDepth; - OwnPtr<SamplingTool> m_sampler; + std::unique_ptr<SamplingTool> m_sampler; VM& m_vm; JSStack m_stack; int m_errorHandlingModeReentry; -#if ENABLE(COMPUTED_GOTO_OPCODES) && ENABLE(LLINT) +#if ENABLE(COMPUTED_GOTO_OPCODES) Opcode* m_opcodeTable; // Maps OpcodeID => Opcode for compiling HashMap<Opcode, OpcodeID> m_opcodeIDTable; // Maps Opcode => OpcodeID for decompiling #endif @@ -286,8 +273,29 @@ namespace JSC { }; JSValue eval(CallFrame*); - CallFrame* sizeAndAllocFrameForVarargs(CallFrame*, JSStack*, JSValue, int); - void loadVarargs(CallFrame*, CallFrame*, JSValue, JSValue); + + inline CallFrame* calleeFrameForVarargs(CallFrame* callFrame, unsigned numUsedStackSlots, unsigned argumentCountIncludingThis) + { + // We want the new frame to be allocated on a stack aligned offset with a stack + // aligned size. Align the size here. + argumentCountIncludingThis = WTF::roundUpToMultipleOf( + stackAlignmentRegisters(), + argumentCountIncludingThis + JSStack::CallFrameHeaderSize) - JSStack::CallFrameHeaderSize; + + // Align the frame offset here. + unsigned paddedCalleeFrameOffset = WTF::roundUpToMultipleOf( + stackAlignmentRegisters(), + numUsedStackSlots + argumentCountIncludingThis + JSStack::CallFrameHeaderSize); + return CallFrame::create(callFrame->registers() - paddedCalleeFrameOffset); + } + + unsigned sizeOfVarargs(CallFrame* exec, JSValue arguments, uint32_t firstVarArgOffset); + static const unsigned maxArguments = 0x10000; + unsigned sizeFrameForVarargs(CallFrame* exec, JSStack*, JSValue arguments, unsigned numUsedStackSlots, uint32_t firstVarArgOffset); + void loadVarargs(CallFrame* execCaller, VirtualRegister firstElementDest, JSValue source, uint32_t offset, uint32_t length); + void setupVarargsFrame(CallFrame* execCaller, CallFrame* execCallee, JSValue arguments, uint32_t firstVarArgOffset, uint32_t length); + void setupVarargsFrameAndSetThis(CallFrame* execCaller, CallFrame* execCallee, JSValue thisValue, JSValue arguments, uint32_t firstVarArgOffset, uint32_t length); + } // namespace JSC #endif // Interpreter_h diff --git a/Source/JavaScriptCore/interpreter/JSStack.cpp b/Source/JavaScriptCore/interpreter/JSStack.cpp index 722e1bc23..6e441dfd5 100644 --- a/Source/JavaScriptCore/interpreter/JSStack.cpp +++ b/Source/JavaScriptCore/interpreter/JSStack.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2013, 2014, 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,7 @@ * 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 + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -31,121 +31,154 @@ #include "ConservativeRoots.h" #include "Interpreter.h" +#include "JSCInlines.h" +#include "Options.h" +#include <wtf/Lock.h> namespace JSC { +#if !ENABLE(JIT) static size_t committedBytesCount = 0; -static Mutex& stackStatisticsMutex() +static size_t commitSize() { - DEFINE_STATIC_LOCAL(Mutex, staticMutex, ()); - return staticMutex; -} + static size_t size = std::max<size_t>(16 * 1024, pageSize()); + return size; +} + +static StaticLock stackStatisticsMutex; +#endif // !ENABLE(JIT) -JSStack::JSStack(VM& vm, size_t capacity) +JSStack::JSStack(VM& vm) : m_vm(vm) - , m_end(0) , m_topCallFrame(vm.topCallFrame) +#if !ENABLE(JIT) + , m_end(0) + , m_reservedZoneSizeInRegisters(0) +#endif { +#if !ENABLE(JIT) + size_t capacity = Options::maxPerThreadStackUsage(); ASSERT(capacity && isPageAligned(capacity)); - m_reservation = PageReservation::reserve(roundUpAllocationSize(capacity * sizeof(Register), commitSize), OSAllocator::JSVMStackPages); - updateStackLimit(highAddress()); - m_commitEnd = highAddress(); - - disableErrorStackReserve(); + m_reservation = PageReservation::reserve(WTF::roundUpToMultipleOf(commitSize(), capacity), OSAllocator::JSVMStackPages); + setStackLimit(highAddress()); + m_commitTop = highAddress(); + + m_lastStackTop = baseOfStack(); +#endif // !ENABLE(JIT) m_topCallFrame = 0; } +#if !ENABLE(JIT) JSStack::~JSStack() { - void* highAddress = reinterpret_cast<void*>(static_cast<char*>(m_reservation.base()) + m_reservation.size()); - m_reservation.decommit(reinterpret_cast<void*>(m_commitEnd), reinterpret_cast<intptr_t>(highAddress) - reinterpret_cast<intptr_t>(m_commitEnd)); - addToCommittedByteCount(-(reinterpret_cast<intptr_t>(highAddress) - reinterpret_cast<intptr_t>(m_commitEnd))); + ptrdiff_t sizeToDecommit = reinterpret_cast<char*>(highAddress()) - reinterpret_cast<char*>(m_commitTop); + m_reservation.decommit(reinterpret_cast<void*>(m_commitTop), sizeToDecommit); + addToCommittedByteCount(-sizeToDecommit); m_reservation.deallocate(); } -bool JSStack::growSlowCase(Register* newEnd) +bool JSStack::growSlowCase(Register* newTopOfStack) { + Register* newTopOfStackWithReservedZone = newTopOfStack - m_reservedZoneSizeInRegisters; + // If we have already committed enough memory to satisfy this request, // just update the end pointer and return. - if (newEnd >= m_commitEnd) { - updateStackLimit(newEnd); + if (newTopOfStackWithReservedZone >= m_commitTop) { + setStackLimit(newTopOfStack); return true; } // Compute the chunk size of additional memory to commit, and see if we // have it is still within our budget. If not, we'll fail to grow and // return false. - long delta = roundUpAllocationSize(reinterpret_cast<char*>(m_commitEnd) - reinterpret_cast<char*>(newEnd), commitSize); - if (reinterpret_cast<char*>(m_commitEnd) - delta <= reinterpret_cast<char*>(m_useableEnd)) + ptrdiff_t delta = reinterpret_cast<char*>(m_commitTop) - reinterpret_cast<char*>(newTopOfStackWithReservedZone); + delta = WTF::roundUpToMultipleOf(commitSize(), delta); + Register* newCommitTop = m_commitTop - (delta / sizeof(Register)); + if (newCommitTop < reservationTop()) return false; - // Otherwise, the growth is still within our budget. Go ahead and commit - // it and return true. - m_reservation.commit(reinterpret_cast<char*>(m_commitEnd) - delta, delta); + // Otherwise, the growth is still within our budget. Commit it and return true. + m_reservation.commit(newCommitTop, delta); addToCommittedByteCount(delta); - m_commitEnd = reinterpret_cast_ptr<Register*>(reinterpret_cast<char*>(m_commitEnd) - delta); - updateStackLimit(newEnd); + m_commitTop = newCommitTop; + setStackLimit(newTopOfStack); return true; } -void JSStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots) +void JSStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks) { - conservativeRoots.add(getBaseOfStack(), getTopOfStack()); + conservativeRoots.add(topOfStack() + 1, highAddress(), jitStubRoutines, codeBlocks); } -void JSStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks) +void JSStack::sanitizeStack() { - conservativeRoots.add(getBaseOfStack(), getTopOfStack(), jitStubRoutines, codeBlocks); +#if !ASAN_ENABLED + ASSERT(topOfStack() <= baseOfStack()); + + if (m_lastStackTop < topOfStack()) { + char* begin = reinterpret_cast<char*>(m_lastStackTop + 1); + char* end = reinterpret_cast<char*>(topOfStack() + 1); + memset(begin, 0, end - begin); + } + + m_lastStackTop = topOfStack(); +#endif } void JSStack::releaseExcessCapacity() { - ptrdiff_t delta = reinterpret_cast<uintptr_t>(highAddress()) - reinterpret_cast<uintptr_t>(m_commitEnd); - m_reservation.decommit(m_commitEnd, delta); + Register* highAddressWithReservedZone = highAddress() - m_reservedZoneSizeInRegisters; + ptrdiff_t delta = reinterpret_cast<char*>(highAddressWithReservedZone) - reinterpret_cast<char*>(m_commitTop); + m_reservation.decommit(m_commitTop, delta); addToCommittedByteCount(-delta); - m_commitEnd = highAddress(); + m_commitTop = highAddressWithReservedZone; } -void JSStack::initializeThreading() +void JSStack::addToCommittedByteCount(long byteCount) { - stackStatisticsMutex(); + LockHolder locker(stackStatisticsMutex); + ASSERT(static_cast<long>(committedBytesCount) + byteCount > -1); + committedBytesCount += byteCount; } -size_t JSStack::committedByteCount() +void JSStack::setReservedZoneSize(size_t reservedZoneSize) { - MutexLocker locker(stackStatisticsMutex()); - return committedBytesCount; + m_reservedZoneSizeInRegisters = reservedZoneSize / sizeof(Register); + if (m_commitTop >= (m_end + 1) - m_reservedZoneSizeInRegisters) + growSlowCase(m_end + 1); } +#endif // !ENABLE(JIT) -void JSStack::addToCommittedByteCount(long byteCount) +#if ENABLE(JIT) +Register* JSStack::lowAddress() const { - MutexLocker locker(stackStatisticsMutex()); - ASSERT(static_cast<long>(committedBytesCount) + byteCount > -1); - committedBytesCount += byteCount; + ASSERT(wtfThreadData().stack().isGrowingDownward()); + return reinterpret_cast<Register*>(m_vm.stackLimit()); } -void JSStack::enableErrorStackReserve() +Register* JSStack::highAddress() const { - m_useableEnd = reservationEnd(); + ASSERT(wtfThreadData().stack().isGrowingDownward()); + return reinterpret_cast<Register*>(wtfThreadData().stack().origin()); } +#endif // ENABLE(JIT) -void JSStack::disableErrorStackReserve() +size_t JSStack::committedByteCount() { - char* useableEnd = reinterpret_cast<char*>(reservationEnd()) + commitSize; - m_useableEnd = reinterpret_cast_ptr<Register*>(useableEnd); - - // By the time we get here, we are guaranteed to be destructing the last - // Interpreter::ErrorHandlingMode that enabled this reserve in the first - // place. That means the stack space beyond m_useableEnd before we - // enabled the reserve was not previously in use. Hence, it is safe to - // shrink back to that m_useableEnd. - if (m_end < m_useableEnd) { - ASSERT(m_topCallFrame->frameExtent() >= m_useableEnd); - shrink(m_useableEnd); - } +#if !ENABLE(JIT) + LockHolder locker(stackStatisticsMutex); + return committedBytesCount; +#else + // When using the C stack, we don't know how many stack pages are actually + // committed. So, we use the current stack usage as an estimate. + ASSERT(wtfThreadData().stack().isGrowingDownward()); + int8_t* current = reinterpret_cast<int8_t*>(¤t); + int8_t* high = reinterpret_cast<int8_t*>(wtfThreadData().stack().origin()); + return high - current; +#endif } } // namespace JSC diff --git a/Source/JavaScriptCore/interpreter/JSStack.h b/Source/JavaScriptCore/interpreter/JSStack.h index adf825d98..770db0920 100644 --- a/Source/JavaScriptCore/interpreter/JSStack.h +++ b/Source/JavaScriptCore/interpreter/JSStack.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008, 2009, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2013, 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,7 +10,7 @@ * 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 + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -35,11 +35,6 @@ #include <wtf/PageReservation.h> #include <wtf/VMTags.h> -#define ENABLE_DEBUG_JSSTACK 0 -#if !defined(NDEBUG) && !defined(ENABLE_DEBUG_JSSTACK) -#define ENABLE_DEBUG_JSSTACK 1 -#endif - namespace JSC { class CodeBlockSet; @@ -61,8 +56,8 @@ namespace JSC { WTF_MAKE_NONCOPYABLE(JSStack); public: enum CallFrameHeaderEntry { - CodeBlock = sizeof(CallerFrameAndPC) / sizeof(Register), - ScopeChain, + CallerFrameAndPCSize = sizeof(CallerFrameAndPC) / sizeof(Register), + CodeBlock = CallerFrameAndPCSize, Callee, ArgumentCount, CallFrameHeaderSize, @@ -72,90 +67,81 @@ namespace JSC { FirstArgument, }; - static const size_t defaultCapacity = 512 * 1024; - static const size_t commitSize = 16 * 1024; // Allow 8k of excess registers before we start trying to reap the stack static const ptrdiff_t maxExcessCapacity = 8 * 1024; - JSStack(VM&, size_t capacity = defaultCapacity); - ~JSStack(); + JSStack(VM&); - void gatherConservativeRoots(ConservativeRoots&); + bool ensureCapacityFor(Register* newTopOfStack); + + bool containsAddress(Register* address) { return (lowAddress() <= address && address < highAddress()); } + static size_t committedByteCount(); + +#if ENABLE(JIT) + void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&) { } + void sanitizeStack() { } +#else + ~JSStack(); + void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&); + void sanitizeStack(); - Register* getBaseOfStack() const + Register* baseOfStack() const { return highAddress() - 1; } size_t size() const { return highAddress() - lowAddress(); } - bool grow(Register*); - - static size_t committedByteCount(); - static void initializeThreading(); - - Register* getTopOfFrame(CallFrame*); - Register* getStartOfFrame(CallFrame*); - Register* getTopOfStack(); - - bool entryCheck(class CodeBlock*, int); + void setReservedZoneSize(size_t); - CallFrame* pushFrame(class CodeBlock*, JSScope*, int argsCount, JSObject* callee); - - void popFrame(CallFrame*); - - bool containsAddress(Register* address) { return (lowAddress() <= address && address <= highAddress()); } - - void enableErrorStackReserve(); - void disableErrorStackReserve(); - -#if ENABLE(DEBUG_JSSTACK) - void installFence(CallFrame*, const char *function = "", int lineNo = 0); - void validateFence(CallFrame*, const char *function = "", int lineNo = 0); - static const int FenceSize = 4; -#else // !ENABLE(DEBUG_JSSTACK) - void installFence(CallFrame*, const char* = "", int = 0) { } - void validateFence(CallFrame*, const char* = "", int = 0) { } -#endif // !ENABLE(DEBUG_JSSTACK) + inline Register* topOfStack(); +#endif // ENABLE(JIT) private: + +#if !ENABLE(JIT) Register* lowAddress() const { - return m_end; + return m_end + 1; } Register* highAddress() const { return reinterpret_cast_ptr<Register*>(static_cast<char*>(m_reservation.base()) + m_reservation.size()); } +#else + Register* lowAddress() const; + Register* highAddress() const; +#endif // !ENABLE(JIT) - Register* reservationEnd() const +#if !ENABLE(JIT) + inline Register* topOfFrameFor(CallFrame*); + + Register* reservationTop() const { - char* reservationEnd = static_cast<char*>(m_reservation.base()); - return reinterpret_cast_ptr<Register*>(reservationEnd); + char* reservationTop = static_cast<char*>(m_reservation.base()); + return reinterpret_cast_ptr<Register*>(reservationTop); } -#if ENABLE(DEBUG_JSSTACK) - static JSValue generateFenceValue(size_t argIndex); - void installTrapsAfterFrame(CallFrame*); -#else - void installTrapsAfterFrame(CallFrame*) { } -#endif - - bool growSlowCase(Register*); - void shrink(Register*); + bool grow(Register* newTopOfStack); + bool growSlowCase(Register* newTopOfStack); + void shrink(Register* newTopOfStack); void releaseExcessCapacity(); void addToCommittedByteCount(long); - void updateStackLimit(Register* newEnd); + void setStackLimit(Register* newTopOfStack); +#endif // !ENABLE(JIT) VM& m_vm; + CallFrame*& m_topCallFrame; +#if !ENABLE(JIT) Register* m_end; - Register* m_commitEnd; - Register* m_useableEnd; + Register* m_commitTop; PageReservation m_reservation; - CallFrame*& m_topCallFrame; + Register* m_lastStackTop; + ptrdiff_t m_reservedZoneSizeInRegisters; +#endif // !ENABLE(JIT) friend class LLIntOffsetsExtractor; }; diff --git a/Source/JavaScriptCore/interpreter/JSStackInlines.h b/Source/JavaScriptCore/interpreter/JSStackInlines.h index 5a2aff117..69508ab5d 100644 --- a/Source/JavaScriptCore/interpreter/JSStackInlines.h +++ b/Source/JavaScriptCore/interpreter/JSStackInlines.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,262 +33,61 @@ namespace JSC { -inline Register* JSStack::getTopOfFrame(CallFrame* frame) +inline bool JSStack::ensureCapacityFor(Register* newTopOfStack) { - if (UNLIKELY(!frame)) - return getBaseOfStack(); - return frame->frameExtent(); -} - -inline Register* JSStack::getTopOfStack() -{ - return getTopOfFrame(m_topCallFrame); -} - -inline Register* JSStack::getStartOfFrame(CallFrame* frame) -{ - CallFrame* callerFrame = frame->callerFrameSkippingVMEntrySentinel(); - return getTopOfFrame(callerFrame); -} - -inline bool JSStack::entryCheck(class CodeBlock* codeBlock, int argsCount) -{ - Register* oldEnd = getTopOfStack(); - - // Ensure that we have enough space for the parameters: - size_t paddedArgsCount = argsCount; - if (codeBlock) { - size_t numParameters = codeBlock->numParameters(); - if (paddedArgsCount < numParameters) - paddedArgsCount = numParameters; - } - - Register* newCallFrameSlot = oldEnd - paddedArgsCount - (2 * JSStack::CallFrameHeaderSize) + 1; - -#if ENABLE(DEBUG_JSSTACK) - newCallFrameSlot -= JSStack::FenceSize; +#if !ENABLE(JIT) + return grow(newTopOfStack); +#else + ASSERT(wtfThreadData().stack().isGrowingDownward()); + return newTopOfStack >= m_vm.stackLimit(); #endif - - Register* newEnd = newCallFrameSlot; - if (!!codeBlock) - newEnd += virtualRegisterForLocal(codeBlock->frameRegisterCount()).offset(); - - // Ensure that we have the needed stack capacity to push the new frame: - if (!grow(newEnd)) - return false; - - return true; } -inline CallFrame* JSStack::pushFrame(class CodeBlock* codeBlock, JSScope* scope, int argsCount, JSObject* callee) -{ - ASSERT(!!scope); - Register* oldEnd = getTopOfStack(); - - // Ensure that we have enough space for the parameters: - size_t paddedArgsCount = argsCount; - if (codeBlock) { - size_t numParameters = codeBlock->numParameters(); - if (paddedArgsCount < numParameters) - paddedArgsCount = numParameters; - } - - Register* newCallFrameSlot = oldEnd - paddedArgsCount - (2 * JSStack::CallFrameHeaderSize) + 1; - -#if ENABLE(DEBUG_JSSTACK) - newCallFrameSlot -= JSStack::FenceSize; -#endif - - Register* newEnd = newCallFrameSlot; - if (!!codeBlock) - newEnd += virtualRegisterForLocal(codeBlock->frameRegisterCount()).offset(); - - // Ensure that we have the needed stack capacity to push the new frame: - if (!grow(newEnd)) - return 0; - - // Compute the address of the new VM sentinel frame for this invocation: - CallFrame* newVMEntrySentinelFrame = CallFrame::create(newCallFrameSlot + paddedArgsCount + JSStack::CallFrameHeaderSize); - ASSERT(!!newVMEntrySentinelFrame); - - // Compute the address of the new frame for this invocation: - CallFrame* newCallFrame = CallFrame::create(newCallFrameSlot); - ASSERT(!!newCallFrame); - - // The caller frame should always be the real previous frame on the stack, - // and not a potential GlobalExec that was passed in. Point callerFrame to - // the top frame on the stack. - CallFrame* callerFrame = m_topCallFrame; - - // Initialize the VM sentinel frame header: - newVMEntrySentinelFrame->initializeVMEntrySentinelFrame(callerFrame); - - // Initialize the callee frame header: - newCallFrame->init(codeBlock, 0, scope, newVMEntrySentinelFrame, argsCount, callee); +#if !ENABLE(JIT) - ASSERT(!!newCallFrame->scope()); - - // Pad additional args if needed: - // Note: we need to subtract 1 from argsCount and paddedArgsCount to - // exclude the this pointer. - for (size_t i = argsCount-1; i < paddedArgsCount-1; ++i) - newCallFrame->setArgument(i, jsUndefined()); - - installFence(newCallFrame, __FUNCTION__, __LINE__); - validateFence(newCallFrame, __FUNCTION__, __LINE__); - installTrapsAfterFrame(newCallFrame); - - // Push the new frame: - m_topCallFrame = newCallFrame; - - return newCallFrame; +inline Register* JSStack::topOfFrameFor(CallFrame* frame) +{ + if (UNLIKELY(!frame)) + return baseOfStack(); + return frame->topOfFrame() - 1; } -inline void JSStack::popFrame(CallFrame* frame) +inline Register* JSStack::topOfStack() { - validateFence(frame, __FUNCTION__, __LINE__); - - // Pop off the callee frame and the sentinel frame. - CallFrame* callerFrame = frame->callerFrame()->vmEntrySentinelCallerFrame(); - - // Pop to the caller: - m_topCallFrame = callerFrame; - - // If we are popping the very first frame from the stack i.e. no more - // frames before this, then we can now safely shrink the stack. In - // this case, we're shrinking all the way to the beginning since there - // are no more frames on the stack. - if (!callerFrame) - shrink(getBaseOfStack()); - - installTrapsAfterFrame(callerFrame); + return topOfFrameFor(m_topCallFrame); } -inline void JSStack::shrink(Register* newEnd) +inline void JSStack::shrink(Register* newTopOfStack) { + Register* newEnd = newTopOfStack - 1; if (newEnd >= m_end) return; - updateStackLimit(newEnd); - if (m_end == getBaseOfStack() && (m_commitEnd - getBaseOfStack()) >= maxExcessCapacity) + setStackLimit(newTopOfStack); + // Note: Clang complains of an unresolved linkage to maxExcessCapacity if + // invoke std::max() with it as an argument. To work around this, we first + // assign the constant to a local variable, and use the local instead. + ptrdiff_t maxExcessCapacity = JSStack::maxExcessCapacity; + ptrdiff_t maxExcessInRegisters = std::max(maxExcessCapacity, m_reservedZoneSizeInRegisters); + if (m_end == baseOfStack() && (highAddress() - m_commitTop) >= maxExcessInRegisters) releaseExcessCapacity(); } -inline bool JSStack::grow(Register* newEnd) +inline bool JSStack::grow(Register* newTopOfStack) { + Register* newEnd = newTopOfStack - 1; if (newEnd >= m_end) return true; - return growSlowCase(newEnd); + return growSlowCase(newTopOfStack); } -inline void JSStack::updateStackLimit(Register* newEnd) +inline void JSStack::setStackLimit(Register* newTopOfStack) { + Register* newEnd = newTopOfStack - 1; m_end = newEnd; -#if USE(SEPARATE_C_AND_JS_STACK) - m_vm.setJSStackLimit(newEnd); -#endif -} - -#if ENABLE(DEBUG_JSSTACK) -inline JSValue JSStack::generateFenceValue(size_t argIndex) -{ - unsigned fenceBits = 0xfacebad0 | ((argIndex+1) & 0xf); - JSValue fenceValue = JSValue(fenceBits); - return fenceValue; + m_vm.setJSStackLimit(newTopOfStack); } -// The JSStack fences mechanism works as follows: -// 1. A fence is a number (JSStack::FenceSize) of JSValues that are initialized -// with values generated by JSStack::generateFenceValue(). -// 2. When pushFrame() is called, the fence is installed after the max extent -// of the previous topCallFrame and the last arg of the new frame: -// -// | ... | -// |--------------------------------------| -// | Frame Header of previous frame | -// |--------------------------------------| -// topCallFrame --> | | -// | Locals of previous frame | -// |--------------------------------------| -// | *** the Fence *** | -// |--------------------------------------| -// | VM entry sentinel frame header | -// |--------------------------------------| -// | Args of new frame | -// |--------------------------------------| -// | Frame Header of new frame | -// |--------------------------------------| -// frame --> | Locals of new frame | -// | | -// -// 3. In popFrame() and elsewhere, we can call JSStack::validateFence() to -// assert that the fence contains the values we expect. - -inline void JSStack::installFence(CallFrame* frame, const char *function, int lineNo) -{ - UNUSED_PARAM(function); - UNUSED_PARAM(lineNo); - Register* startOfFrame = getStartOfFrame(frame); - - // The last argIndex is at: - size_t maxIndex = frame->argIndexForRegister(startOfFrame) + 1; - size_t startIndex = maxIndex - FenceSize; - for (size_t i = startIndex; i < maxIndex; ++i) { - JSValue fenceValue = generateFenceValue(i); - frame->setArgument(i, fenceValue); - } -} - -inline void JSStack::validateFence(CallFrame* frame, const char *function, int lineNo) -{ - UNUSED_PARAM(function); - UNUSED_PARAM(lineNo); - ASSERT(!!frame->scope()); - Register* startOfFrame = getStartOfFrame(frame); - size_t maxIndex = frame->argIndexForRegister(startOfFrame) + 1; - size_t startIndex = maxIndex - FenceSize; - for (size_t i = startIndex; i < maxIndex; ++i) { - JSValue fenceValue = generateFenceValue(i); - JSValue actualValue = frame->getArgumentUnsafe(i); - ASSERT(fenceValue == actualValue); - } -} - -// When debugging the JSStack, we install bad values after the extent of the -// topCallFrame at the end of pushFrame() and popFrame(). The intention is -// to trigger crashes in the event that memory in this supposedly unused -// region is read and consumed without proper initialization. After the trap -// words are installed, the stack looks like this: -// -// | ... | -// |-----------------------------| -// | Frame Header of frame | -// |-----------------------------| -// topCallFrame --> | | -// | Locals of frame | -// |-----------------------------| -// | *** Trap words *** | -// |-----------------------------| -// | Unused space ... | -// | ... | - -inline void JSStack::installTrapsAfterFrame(CallFrame* frame) -{ - Register* topOfFrame = getTopOfFrame(frame); - const int sizeOfTrap = 64; - int32_t* startOfTrap = reinterpret_cast<int32_t*>(topOfFrame); - int32_t* endOfTrap = startOfTrap - sizeOfTrap; - int32_t* endOfCommitedMemory = reinterpret_cast<int32_t*>(m_commitEnd); - - // Make sure we're not exceeding the amount of available memory to write to: - if (endOfTrap < endOfCommitedMemory) - endOfTrap = endOfCommitedMemory; - - // Lay the traps: - int32_t* p = startOfTrap; - while (p > endOfTrap) - *p-- = 0xabadcafe; // A bad word to trigger a crash if deref'ed. -} -#endif // ENABLE(DEBUG_JSSTACK) +#endif // !ENABLE(JIT) } // namespace JSC diff --git a/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp b/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp index a36001437..eb80b2c23 100644 --- a/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp +++ b/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp @@ -27,23 +27,25 @@ #include "ProtoCallFrame.h" #include "CodeBlock.h" +#include "JSCInlines.h" +#include "StackAlignment.h" namespace JSC { -void ProtoCallFrame::init(CodeBlock* codeBlock, JSScope* scope, JSObject* callee, JSValue thisValue, int argCountIncludingThis, JSValue* otherArgs) +void ProtoCallFrame::init(CodeBlock* codeBlock, JSObject* callee, JSValue thisValue, int argCountIncludingThis, JSValue* otherArgs) { this->args = otherArgs; this->setCodeBlock(codeBlock); - this->setScope(scope); this->setCallee(callee); this->setArgumentCountIncludingThis(argCountIncludingThis); - size_t paddedArgsCount = argCountIncludingThis; - if (codeBlock) { - size_t numParameters = codeBlock->numParameters(); - if (paddedArgsCount < numParameters) - paddedArgsCount = numParameters; - } - this->setPaddedArgsCount(paddedArgsCount); + if (codeBlock && argCountIncludingThis < codeBlock->numParameters()) + this->arityMissMatch = true; + else + this->arityMissMatch = false; + + // Round up argCountIncludingThis to keep the stack frame size aligned. + size_t paddedArgsCount = roundArgumentCountToAlignFrame(argCountIncludingThis); + this->setPaddedArgCount(paddedArgsCount); this->clearCurrentVPC(); this->setThisValue(thisValue); } diff --git a/Source/JavaScriptCore/interpreter/ProtoCallFrame.h b/Source/JavaScriptCore/interpreter/ProtoCallFrame.h index b7151eeff..af33a3072 100644 --- a/Source/JavaScriptCore/interpreter/ProtoCallFrame.h +++ b/Source/JavaScriptCore/interpreter/ProtoCallFrame.h @@ -32,34 +32,33 @@ namespace JSC { struct ProtoCallFrame { Register codeBlockValue; - Register scopeChainValue; Register calleeValue; Register argCountAndCodeOriginValue; Register thisArg; - size_t paddedArgCount; + uint32_t paddedArgCount; + bool arityMissMatch; JSValue *args; - void init(CodeBlock*, JSScope*, JSObject*, JSValue, int, JSValue* otherArgs = 0); + void init(CodeBlock*, JSObject*, JSValue, int, JSValue* otherArgs = 0); CodeBlock* codeBlock() const { return codeBlockValue.Register::codeBlock(); } void setCodeBlock(CodeBlock* codeBlock) { codeBlockValue = codeBlock; } - JSScope* scope() const { return scopeChainValue.Register::scope(); } - void setScope(JSScope* scope) { scopeChainValue = scope; } - - JSObject* callee() const { return calleeValue.Register::function(); } - void setCallee(JSObject* callee) { calleeValue = Register::withCallee(callee); } + JSObject* callee() const { return calleeValue.Register::object(); } + void setCallee(JSObject* callee) { calleeValue = callee; } int argumentCountIncludingThis() const { return argCountAndCodeOriginValue.payload(); } int argumentCount() const { return argumentCountIncludingThis() - 1; } void setArgumentCountIncludingThis(int count) { argCountAndCodeOriginValue.payload() = count; } - void setPaddedArgsCount(size_t argCount) { paddedArgCount = argCount; } + void setPaddedArgCount(uint32_t argCount) { paddedArgCount = argCount; } void clearCurrentVPC() { argCountAndCodeOriginValue.tag() = 0; } JSValue thisValue() const { return thisArg.Register::jsValue(); } void setThisValue(JSValue value) { thisArg = value; } + bool needArityCheck() { return arityMissMatch; } + JSValue argument(size_t argumentIndex) { ASSERT(static_cast<int>(argumentIndex) < argumentCount()); diff --git a/Source/JavaScriptCore/interpreter/Register.h b/Source/JavaScriptCore/interpreter/Register.h index 13a7e5866..c0396c966 100644 --- a/Source/JavaScriptCore/interpreter/Register.h +++ b/Source/JavaScriptCore/interpreter/Register.h @@ -10,7 +10,7 @@ * 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 + * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * @@ -37,9 +37,8 @@ namespace JSC { class CodeBlock; class ExecState; - class JSActivation; + class JSLexicalEnvironment; class JSObject; - class JSPropertyNameIterator; class JSScope; typedef ExecState CallFrame; @@ -52,18 +51,19 @@ namespace JSC { Register(const JSValue&); Register& operator=(const JSValue&); JSValue jsValue() const; + JSValue asanUnsafeJSValue() const; EncodedJSValue encodedJSValue() const; Register& operator=(CallFrame*); Register& operator=(CodeBlock*); Register& operator=(JSScope*); + Register& operator=(JSObject*); int32_t i() const; - JSActivation* activation() const; CallFrame* callFrame() const; CodeBlock* codeBlock() const; - JSObject* function() const; - JSPropertyNameIterator* propertyNameIterator() const; + CodeBlock* asanUnsafeCodeBlock() const; + JSObject* object() const; JSScope* scope() const; int32_t unboxedInt32() const; int64_t unboxedInt52() const; @@ -73,6 +73,7 @@ namespace JSC { JSCell* unboxedCell() const; int32_t payload() const; int32_t tag() const; + int32_t unsafeTag() const; int32_t& payload(); int32_t& tag(); @@ -82,8 +83,6 @@ namespace JSC { return r; } - static Register withCallee(JSObject* callee); - private: union { EncodedJSValue value; @@ -113,6 +112,12 @@ namespace JSC { return *this; } + // FIXME (rdar://problem/19379214): ASan only needs to be suppressed for Register::jsValue() when called from prepareOSREntry(), but there is currently no way to express this short of adding a separate copy of the function. + SUPPRESS_ASAN ALWAYS_INLINE JSValue Register::asanUnsafeJSValue() const + { + return JSValue::decode(u.value); + } + ALWAYS_INLINE JSValue Register::jsValue() const { return JSValue::decode(u.value); @@ -152,6 +157,11 @@ namespace JSC { return u.codeBlock; } + SUPPRESS_ASAN ALWAYS_INLINE CodeBlock* Register::asanUnsafeCodeBlock() const + { + return u.codeBlock; + } + ALWAYS_INLINE int32_t Register::unboxedInt32() const { return payload(); @@ -196,6 +206,11 @@ namespace JSC { return u.encodedValue.asBits.tag; } + SUPPRESS_ASAN ALWAYS_INLINE int32_t Register::unsafeTag() const + { + return u.encodedValue.asBits.tag; + } + ALWAYS_INLINE int32_t& Register::payload() { return u.encodedValue.asBits.payload; diff --git a/Source/JavaScriptCore/interpreter/StackVisitor.cpp b/Source/JavaScriptCore/interpreter/StackVisitor.cpp index d922e7f8f..c0c03d280 100644 --- a/Source/JavaScriptCore/interpreter/StackVisitor.cpp +++ b/Source/JavaScriptCore/interpreter/StackVisitor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,11 +26,11 @@ #include "config.h" #include "StackVisitor.h" -#include "Arguments.h" -#include "CallFrameInlines.h" +#include "ClonedArguments.h" #include "Executable.h" +#include "InlineCallFrame.h" #include "Interpreter.h" -#include "Operations.h" +#include "JSCInlines.h" #include <wtf/DataLog.h> namespace JSC { @@ -38,7 +38,20 @@ namespace JSC { StackVisitor::StackVisitor(CallFrame* startFrame) { m_frame.m_index = 0; - readFrame(startFrame); + CallFrame* topFrame; + if (startFrame) { + m_frame.m_VMEntryFrame = startFrame->vm().topVMEntryFrame; + topFrame = startFrame->vm().topCallFrame; + } else { + m_frame.m_VMEntryFrame = 0; + topFrame = 0; + } + m_frame.m_callerIsVMEntryFrame = false; + readFrame(topFrame); + + // Find the frame the caller wants to start unwinding from. + while (m_frame.callFrame() && m_frame.callFrame() != startFrame) + gotoNextFrame(); } void StackVisitor::gotoNextFrame() @@ -46,17 +59,33 @@ void StackVisitor::gotoNextFrame() #if ENABLE(DFG_JIT) if (m_frame.isInlinedFrame()) { InlineCallFrame* inlineCallFrame = m_frame.inlineCallFrame(); - CodeOrigin* callerCodeOrigin = &inlineCallFrame->caller; - readInlinedFrame(m_frame.callFrame(), callerCodeOrigin); - - } else + CodeOrigin* callerCodeOrigin = inlineCallFrame->getCallerSkippingTailCalls(); + if (!callerCodeOrigin) { + while (inlineCallFrame) { + readInlinedFrame(m_frame.callFrame(), &inlineCallFrame->directCaller); + inlineCallFrame = m_frame.inlineCallFrame(); + } + m_frame.m_VMEntryFrame = m_frame.m_CallerVMEntryFrame; + readFrame(m_frame.callerFrame()); + } else + readInlinedFrame(m_frame.callFrame(), callerCodeOrigin); + return; + } #endif // ENABLE(DFG_JIT) - readFrame(m_frame.callerFrame()); + m_frame.m_VMEntryFrame = m_frame.m_CallerVMEntryFrame; + readFrame(m_frame.callerFrame()); +} + +void StackVisitor::unwindToMachineCodeBlockFrame() +{ +#if ENABLE(DFG_JIT) + while (m_frame.isInlinedFrame()) + gotoNextFrame(); +#endif } void StackVisitor::readFrame(CallFrame* callFrame) { - ASSERT(!callFrame->isVMEntrySentinel()); if (!callFrame) { m_frame.setToEnd(); return; @@ -81,7 +110,7 @@ void StackVisitor::readFrame(CallFrame* callFrame) return; } - unsigned index = callFrame->locationAsCodeOriginIndex(); + CallSiteIndex index = callFrame->callSiteIndex(); ASSERT(codeBlock->canGetCodeOrigin(index)); if (!codeBlock->canGetCodeOrigin(index)) { // See assertion above. In release builds, we try to protect ourselves @@ -104,13 +133,14 @@ void StackVisitor::readNonInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOri { m_frame.m_callFrame = callFrame; m_frame.m_argumentCountIncludingThis = callFrame->argumentCountIncludingThis(); - m_frame.m_callerFrame = callFrame->callerFrameSkippingVMEntrySentinel(); + m_frame.m_CallerVMEntryFrame = m_frame.m_VMEntryFrame; + m_frame.m_callerFrame = callFrame->callerFrame(m_frame.m_CallerVMEntryFrame); + m_frame.m_callerIsVMEntryFrame = m_frame.m_CallerVMEntryFrame != m_frame.m_VMEntryFrame; m_frame.m_callee = callFrame->callee(); - m_frame.m_scope = callFrame->scope(); m_frame.m_codeBlock = callFrame->codeBlock(); m_frame.m_bytecodeOffset = !m_frame.codeBlock() ? 0 : codeOrigin ? codeOrigin->bytecodeIndex - : callFrame->locationAsBytecodeOffset(); + : callFrame->bytecodeOffset(); #if ENABLE(DFG_JIT) m_frame.m_inlineCallFrame = 0; #endif @@ -127,7 +157,6 @@ static int inlinedFrameOffset(CodeOrigin* codeOrigin) void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin) { ASSERT(codeOrigin); - ASSERT(!callFrame->isVMEntrySentinel()); int frameOffset = inlinedFrameOffset(codeOrigin); bool isInlined = !!frameOffset; @@ -136,14 +165,15 @@ void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin m_frame.m_callFrame = callFrame; m_frame.m_inlineCallFrame = inlineCallFrame; - m_frame.m_argumentCountIncludingThis = inlineCallFrame->arguments.size(); - m_frame.m_codeBlock = inlineCallFrame->baselineCodeBlock(); + if (inlineCallFrame->argumentCountRegister.isValid()) + m_frame.m_argumentCountIncludingThis = callFrame->r(inlineCallFrame->argumentCountRegister.offset()).unboxedInt32(); + else + m_frame.m_argumentCountIncludingThis = inlineCallFrame->arguments.size(); + m_frame.m_codeBlock = inlineCallFrame->baselineCodeBlock.get(); m_frame.m_bytecodeOffset = codeOrigin->bytecodeIndex; JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame); - m_frame.m_scope = callee->scope(); m_frame.m_callee = callee; - ASSERT(m_frame.scope()); ASSERT(m_frame.callee()); // The callerFrame just needs to be non-null to indicate that we @@ -166,6 +196,8 @@ StackVisitor::Frame::CodeType StackVisitor::Frame::codeType() const switch (codeBlock()->codeType()) { case EvalCode: return CodeType::Eval; + case ModuleCode: + return CodeType::Module; case FunctionCode: return CodeType::Function; case GlobalCode: @@ -182,7 +214,10 @@ String StackVisitor::Frame::functionName() switch (codeType()) { case CodeType::Eval: - traceLine = "eval code"; + traceLine = ASCIILiteral("eval code"); + break; + case CodeType::Module: + traceLine = ASCIILiteral("module code"); break; case CodeType::Native: if (callee) @@ -192,7 +227,7 @@ String StackVisitor::Frame::functionName() traceLine = getCalculatedDisplayName(callFrame(), callee).impl(); break; case CodeType::Global: - traceLine = "global code"; + traceLine = ASCIILiteral("global code"); break; } return traceLine.isNull() ? emptyString() : traceLine; @@ -204,15 +239,16 @@ String StackVisitor::Frame::sourceURL() switch (codeType()) { case CodeType::Eval: + case CodeType::Module: case CodeType::Function: case CodeType::Global: { - String sourceURL = codeBlock()->ownerExecutable()->sourceURL(); + String sourceURL = codeBlock()->ownerScriptExecutable()->sourceURL(); if (!sourceURL.isEmpty()) traceLine = sourceURL.impl(); break; } case CodeType::Native: - traceLine = "[native code]"; + traceLine = ASCIILiteral("[native code]"); break; } return traceLine.isNull() ? emptyString() : traceLine; @@ -241,48 +277,26 @@ String StackVisitor::Frame::toString() return traceBuild.toString().impl(); } -Arguments* StackVisitor::Frame::createArguments() +ClonedArguments* StackVisitor::Frame::createArguments() { ASSERT(m_callFrame); CallFrame* physicalFrame = m_callFrame; - VM& vm = physicalFrame->vm(); - Arguments* arguments; + ClonedArguments* arguments; + ArgumentsMode mode; + if (Options::useFunctionDotArguments()) + mode = ArgumentsMode::Cloned; + else + mode = ArgumentsMode::FakeValues; #if ENABLE(DFG_JIT) if (isInlinedFrame()) { ASSERT(m_inlineCallFrame); - arguments = Arguments::create(vm, physicalFrame, m_inlineCallFrame); - arguments->tearOff(physicalFrame, m_inlineCallFrame); + arguments = ClonedArguments::createWithInlineFrame(physicalFrame, physicalFrame, m_inlineCallFrame, mode); } else #endif - { - arguments = Arguments::create(vm, physicalFrame); - arguments->tearOff(physicalFrame); - } + arguments = ClonedArguments::createWithMachineFrame(physicalFrame, physicalFrame, mode); return arguments; } -Arguments* StackVisitor::Frame::existingArguments() -{ - if (codeBlock()->codeType() != FunctionCode) - return 0; - if (!codeBlock()->usesArguments()) - return 0; - - VirtualRegister reg; - -#if ENABLE(DFG_JIT) - if (isInlinedFrame()) - reg = inlineCallFrame()->argumentsRegister; - else -#endif // ENABLE(DFG_JIT) - reg = codeBlock()->argumentsRegister(); - - JSValue result = callFrame()->r(unmodifiedArgumentsRegister(reg).offset()).jsValue(); - if (!result) - return 0; - return jsCast<Arguments*>(result); -} - void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column) { CodeBlock* codeBlock = this->codeBlock(); @@ -299,8 +313,11 @@ void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column) unsigned divotColumn = 0; retrieveExpressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn); - line = divotLine + codeBlock->ownerExecutable()->lineNo(); + line = divotLine + codeBlock->ownerScriptExecutable()->firstLine(); column = divotColumn + (divotLine ? 1 : codeBlock->firstLineColumnOffset()); + + if (codeBlock->ownerScriptExecutable()->hasOverrideLineNumber()) + line = codeBlock->ownerScriptExecutable()->overrideLineNumber(); } void StackVisitor::Frame::retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column) @@ -318,157 +335,106 @@ void StackVisitor::Frame::setToEnd() #endif } -#ifndef NDEBUG - -static const char* jitTypeName(JITCode::JITType jitType) -{ - switch (jitType) { - case JITCode::None: return "None"; - case JITCode::HostCallThunk: return "HostCallThunk"; - case JITCode::InterpreterThunk: return "InterpreterThunk"; - case JITCode::BaselineJIT: return "BaselineJIT"; - case JITCode::DFGJIT: return "DFGJIT"; - case JITCode::FTLJIT: return "FTLJIT"; - } - return "<unknown>"; -} - static void printIndents(int levels) { while (levels--) dataLogFString(" "); } -static void printif(int indentLevels, const char* format, ...) +template<typename... Types> +void log(unsigned indent, const Types&... values) { - va_list argList; - va_start(argList, format); + printIndents(indent); + dataLog(values...); +} - if (indentLevels) - printIndents(indentLevels); +template<typename... Types> +void logF(unsigned indent, const char* format, const Types&... values) +{ + printIndents(indent); -#if COMPILER(CLANG) || (COMPILER(GCC) && GCC_VERSION_AT_LEAST(4, 6, 0)) +#if COMPILER(GCC_OR_CLANG) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" #pragma GCC diagnostic ignored "-Wmissing-format-attribute" #endif - WTF::dataLogFV(format, argList); + dataLogF(format, values...); -#if COMPILER(CLANG) || (COMPILER(GCC) && GCC_VERSION_AT_LEAST(4, 6, 0)) +#if COMPILER(GCC_OR_CLANG) #pragma GCC diagnostic pop #endif - - va_end(argList); } -void StackVisitor::Frame::print(int indentLevel) +void StackVisitor::Frame::print(int indent) { - int i = indentLevel; - if (!this->callFrame()) { - printif(i, "frame 0x0\n"); + log(indent, "frame 0x0\n"); return; } CodeBlock* codeBlock = this->codeBlock(); - printif(i, "frame %p {\n", this->callFrame()); + logF(indent, "frame %p {\n", this->callFrame()); - CallFrame* callFrame = m_callFrame; - CallFrame* callerFrame = this->callerFrame(); - void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr; + { + indent++; + + CallFrame* callFrame = m_callFrame; + CallFrame* callerFrame = this->callerFrame(); + void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr; - printif(i, " name '%s'\n", functionName().utf8().data()); - printif(i, " sourceURL '%s'\n", sourceURL().utf8().data()); - printif(i, " isVMEntrySentinel %d\n", callerFrame->isVMEntrySentinel()); + log(indent, "name: ", functionName(), "\n"); + log(indent, "sourceURL: ", sourceURL(), "\n"); + bool isInlined = false; #if ENABLE(DFG_JIT) - printif(i, " isInlinedFrame %d\n", isInlinedFrame()); - if (isInlinedFrame()) - printif(i, " InlineCallFrame %p\n", m_inlineCallFrame); + isInlined = isInlinedFrame(); + log(indent, "isInlinedFrame: ", isInlinedFrame(), "\n"); + if (isInlinedFrame()) + logF(indent, "InlineCallFrame: %p\n", m_inlineCallFrame); #endif - printif(i, " callee %p\n", callee()); - printif(i, " returnPC %p\n", returnPC); - printif(i, " callerFrame %p\n", callerFrame); - unsigned locationRawBits = callFrame->locationAsRawBits(); - printif(i, " rawLocationBits %u 0x%x\n", locationRawBits, locationRawBits); - printif(i, " codeBlock %p\n", codeBlock); - if (codeBlock) { - JITCode::JITType jitType = codeBlock->jitType(); - if (callFrame->hasLocationAsBytecodeOffset()) { - unsigned bytecodeOffset = callFrame->locationAsBytecodeOffset(); - printif(i, " bytecodeOffset %u %p / %zu\n", bytecodeOffset, reinterpret_cast<void*>(bytecodeOffset), codeBlock->instructions().size()); + logF(indent, "callee: %p\n", callee()); + logF(indent, "returnPC: %p\n", returnPC); + logF(indent, "callerFrame: %p\n", callerFrame); + unsigned locationRawBits = callFrame->callSiteAsRawBits(); + logF(indent, "rawLocationBits: %u 0x%x\n", locationRawBits, locationRawBits); + logF(indent, "codeBlock: %p ", codeBlock); + if (codeBlock) + dataLog(*codeBlock); + dataLog("\n"); + if (codeBlock && !isInlined) { + indent++; + + if (callFrame->callSiteBitsAreBytecodeOffset()) { + unsigned bytecodeOffset = callFrame->bytecodeOffset(); + log(indent, "bytecodeOffset: ", bytecodeOffset, " of ", codeBlock->instructions().size(), "\n"); #if ENABLE(DFG_JIT) - } else { - unsigned codeOriginIndex = callFrame->locationAsCodeOriginIndex(); - printif(i, " codeOriginIdex %u %p / %zu\n", codeOriginIndex, reinterpret_cast<void*>(codeOriginIndex), codeBlock->codeOrigins().size()); + } else { + log(indent, "hasCodeOrigins: ", codeBlock->hasCodeOrigins(), "\n"); + if (codeBlock->hasCodeOrigins()) { + CallSiteIndex callSiteIndex = callFrame->callSiteIndex(); + log(indent, "callSiteIndex: ", callSiteIndex.bits(), " of ", codeBlock->codeOrigins().size(), "\n"); + + JITCode::JITType jitType = codeBlock->jitType(); + if (jitType != JITCode::FTLJIT) { + JITCode* jitCode = codeBlock->jitCode().get(); + logF(indent, "jitCode: %p start %p end %p\n", jitCode, jitCode->start(), jitCode->end()); + } + } #endif + } + unsigned line = 0; + unsigned column = 0; + computeLineAndColumn(line, column); + log(indent, "line: ", line, "\n"); + log(indent, "column: ", column, "\n"); + + indent--; } - unsigned line = 0; - unsigned column = 0; - computeLineAndColumn(line, column); - printif(i, " line %d\n", line); - printif(i, " column %d\n", column); - printif(i, " jitType %d <%s> isOptimizingJIT %d\n", jitType, jitTypeName(jitType), JITCode::isOptimizingJIT(jitType)); -#if ENABLE(DFG_JIT) - printif(i, " hasCodeOrigins %d\n", codeBlock->hasCodeOrigins()); - if (codeBlock->hasCodeOrigins()) { - JITCode* jitCode = codeBlock->jitCode().get(); - printif(i, " jitCode %p start %p end %p\n", jitCode, jitCode->start(), jitCode->end()); - } -#endif + indent--; } - printif(i, "}\n"); + log(indent, "}\n"); } -#endif // NDEBUG - } // namespace JSC - -#ifndef NDEBUG -using JSC::StackVisitor; - -// For debugging use -JS_EXPORT_PRIVATE void debugPrintCallFrame(JSC::CallFrame*); -JS_EXPORT_PRIVATE void debugPrintStack(JSC::CallFrame* topCallFrame); - -class DebugPrintFrameFunctor { -public: - enum Action { - PrintOne, - PrintAll - }; - - DebugPrintFrameFunctor(Action action) - : m_action(action) - { - } - - StackVisitor::Status operator()(StackVisitor& visitor) - { - visitor->print(2); - return m_action == PrintAll ? StackVisitor::Continue : StackVisitor::Done; - } - -private: - Action m_action; -}; - -void debugPrintCallFrame(JSC::CallFrame* callFrame) -{ - if (!callFrame) - return; - DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintOne); - callFrame->iterate(functor); -} - -void debugPrintStack(JSC::CallFrame* topCallFrame) -{ - if (!topCallFrame) - return; - DebugPrintFrameFunctor functor(DebugPrintFrameFunctor::PrintAll); - topCallFrame->iterate(functor); -} - -#endif // !NDEBUG diff --git a/Source/JavaScriptCore/interpreter/StackVisitor.h b/Source/JavaScriptCore/interpreter/StackVisitor.h index 990a226b3..bbf37fe9c 100644 --- a/Source/JavaScriptCore/interpreter/StackVisitor.h +++ b/Source/JavaScriptCore/interpreter/StackVisitor.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Apple Inc. All rights reserved. + * Copyright (C) 2013, 2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,6 +26,7 @@ #ifndef StackVisitor_h #define StackVisitor_h +#include "VMEntryRecord.h" #include <wtf/text/WTFString.h> namespace JSC { @@ -33,12 +34,12 @@ namespace JSC { struct CodeOrigin; struct InlineCallFrame; -class Arguments; class CodeBlock; class ExecState; class JSFunction; class JSObject; class JSScope; +class ClonedArguments; class Register; typedef ExecState CallFrame; @@ -51,14 +52,15 @@ public: Global, Eval, Function, + Module, Native }; size_t index() const { return m_index; } size_t argumentCountIncludingThis() const { return m_argumentCountIncludingThis; } + bool callerIsVMEntryFrame() const { return m_callerIsVMEntryFrame; } CallFrame* callerFrame() const { return m_callerFrame; } JSObject* callee() const { return m_callee; } - JSScope* scope() const { return m_scope; } CodeBlock* codeBlock() const { return m_codeBlock; } unsigned bytecodeOffset() const { return m_bytecodeOffset; } #if ENABLE(DFG_JIT) @@ -77,13 +79,11 @@ public: CodeType codeType() const; JS_EXPORT_PRIVATE void computeLineAndColumn(unsigned& line, unsigned& column); - Arguments* createArguments(); - Arguments* existingArguments(); + ClonedArguments* createArguments(); + VMEntryFrame* vmEntryFrame() const { return m_VMEntryFrame; } CallFrame* callFrame() const { return m_callFrame; } -#ifndef NDEBUG JS_EXPORT_PRIVATE void print(int indentLevel); -#endif private: Frame() { } @@ -94,11 +94,13 @@ public: size_t m_index; size_t m_argumentCountIncludingThis; + VMEntryFrame* m_VMEntryFrame; + VMEntryFrame* m_CallerVMEntryFrame; CallFrame* m_callerFrame; JSObject* m_callee; - JSScope* m_scope; CodeBlock* m_codeBlock; unsigned m_bytecodeOffset; + bool m_callerIsVMEntryFrame; #if ENABLE(DFG_JIT) InlineCallFrame* m_inlineCallFrame; #endif @@ -129,6 +131,7 @@ public: Frame& operator*() { return m_frame; } ALWAYS_INLINE Frame* operator->() { return &m_frame; } + void unwindToMachineCodeBlockFrame(); private: JS_EXPORT_PRIVATE StackVisitor(CallFrame* startFrame); @@ -144,6 +147,32 @@ private: Frame m_frame; }; +class CallerFunctor { +public: + CallerFunctor() + : m_hasSkippedFirstFrame(false) + , m_callerFrame(0) + { + } + + CallFrame* callerFrame() const { return m_callerFrame; } + + StackVisitor::Status operator()(StackVisitor& visitor) + { + if (!m_hasSkippedFirstFrame) { + m_hasSkippedFirstFrame = true; + return StackVisitor::Continue; + } + + m_callerFrame = visitor->callFrame(); + return StackVisitor::Done; + } + +private: + bool m_hasSkippedFirstFrame; + CallFrame* m_callerFrame; +}; + } // namespace JSC #endif // StackVisitor_h diff --git a/Source/JavaScriptCore/interpreter/VMEntryRecord.h b/Source/JavaScriptCore/interpreter/VMEntryRecord.h new file mode 100644 index 000000000..9726538fb --- /dev/null +++ b/Source/JavaScriptCore/interpreter/VMEntryRecord.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR + * 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 VMEntryRecord_h +#define VMEntryRecord_h + +namespace JSC { + +typedef void VMEntryFrame; + +class ExecState; +class VM; + +struct VMEntryRecord { + /* + * This record stored in a vmEntryTo{JavaScript,Host} allocated frame. It is allocated on the stack + * after callee save registers where local variables would go. + */ + VM* m_vm; + ExecState* m_prevTopCallFrame; + VMEntryFrame* m_prevTopVMEntryFrame; + + ExecState* prevTopCallFrame() { return m_prevTopCallFrame; } + SUPPRESS_ASAN ExecState* unsafePrevTopCallFrame() { return m_prevTopCallFrame; } + + VMEntryFrame* prevTopVMEntryFrame() { return m_prevTopVMEntryFrame; } + SUPPRESS_ASAN VMEntryFrame* unsafePrevTopVMEntryFrame() { return m_prevTopVMEntryFrame; } +}; + +extern "C" VMEntryRecord* vmEntryRecord(VMEntryFrame*); + +} // namespace JSC + +#endif // VMEntryRecord_h diff --git a/Source/JavaScriptCore/interpreter/VMInspector.cpp b/Source/JavaScriptCore/interpreter/VMInspector.cpp deleted file mode 100644 index fbb49413d..000000000 --- a/Source/JavaScriptCore/interpreter/VMInspector.cpp +++ /dev/null @@ -1,572 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR - * 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 "VMInspector.h" - -#if ENABLE(VMINSPECTOR) - -#include <wtf/ASCIICType.h> -#include <wtf/text/WTFString.h> - -namespace JSC { - -const char* VMInspector::getTypeName(JSValue value) -{ - if (value.isInt32()) - return "<Int32>"; - if (value.isBoolean()) - return "<Boolean>"; - if (value.isNull()) - return "<Empty>"; - if (value.isUndefined()) - return "<Undefined>"; - if (value.isCell()) - return "<Cell>"; - if (value.isEmpty()) - return "<Empty>"; - return ""; -} - -void VMInspector::dumpFrame0(CallFrame* frame) -{ - dumpFrame(frame, 0, 0, 0, 0); -} - -void VMInspector::dumpFrame(CallFrame* frame, const char* prefix, - const char* funcName, const char* file, int line) -{ - int frameCount = VMInspector::countFrames(frame); - if (frameCount < 0) - return; - - Instruction* vPC = 0; - if (frame->codeBlock()) - vPC = frame->currentVPC(); - - #define CAST reinterpret_cast - - if (prefix) - printf("%s ", prefix); - - printf("frame [%d] %p { cb %p:%s, retPC %p:%s, scope %p:%s, callee %p:%s, callerFrame %p:%s, argc %d, vPC %p }", - frameCount, frame, - CAST<void*>(frame[JSStack::CodeBlock].payload()), - getTypeName(frame[JSStack::CodeBlock].jsValue()), - CAST<void*>(frame[JSStack::ReturnPC].payload()), - getTypeName(frame[JSStack::ReturnPC].jsValue()), - CAST<void*>(frame[JSStack::ScopeChain].payload()), - getTypeName(frame[JSStack::ScopeChain].jsValue()), - CAST<void*>(frame[JSStack::Callee].payload()), - getTypeName(frame[JSStack::Callee].jsValue()), - CAST<void*>(frame[JSStack::CallerFrame].callFrame()), - getTypeName(frame[JSStack::CallerFrame].jsValue()), - frame[JSStack::ArgumentCount].payload(), - vPC); - - if (funcName || file || (line >= 0)) { - printf(" @"); - if (funcName) - printf(" %s", funcName); - if (file) - printf(" %s", file); - if (line >= 0) - printf(":%d", line); - } - printf("\n"); -} - -int VMInspector::countFrames(CallFrame* frame) -{ - int count = -1; - while (frame && !frame->isVMEntrySentinel()) { - count++; - frame = frame->callerFrame(); - } - return count; -} - - -//============================================================================ -// class FormatPrinter -// - implements functionality to support fprintf. -// -// The FormatPrinter classes do the real formatting and printing. -// By default, the superclass FormatPrinter will print to stdout (printf). -// Each of the subclass will implement the other ...printf() options. -// The subclasses are: -// -// FileFormatPrinter - fprintf -// StringFormatPrinter - sprintf -// StringNFormatPrinter - snprintf - -class FormatPrinter { -public: - virtual ~FormatPrinter() { } - - void print(const char* format, va_list args); - -protected: - // Low level printers: - bool printArg(const char* format, ...); - virtual bool printArg(const char* format, va_list args); - - // JS type specific printers: - void printWTFString(va_list args, bool verbose); -}; - - -// The public print() function is the real workhorse behind the printf -// family of functions. print() deciphers the % formatting, translate them -// to primitive formats, and dispatches to underlying printArg() functions -// to do the printing. -// -// The non-public internal printArg() function is virtual and is responsible -// for handling the variations between printf, fprintf, sprintf, and snprintf. - -void FormatPrinter::print(const char* format, va_list args) -{ - const char* p = format; - const char* errorStr; - - // buffer is only used for 2 purposes: - // 1. To temporarily hold a copy of normal chars (not needing formatting) - // to be passed to printArg() and printed. - // - // The incoming format string may contain a string of normal chars much - // longer than 128, but we handle this by breaking them out to 128 chars - // fragments and printing each fragment before re-using the buffer to - // load up the next fragment. - // - // 2. To hold a single "%..." format to be passed to printArg() to process - // a single va_arg. - - char buffer[129]; // 128 chars + null terminator. - char* end = &buffer[sizeof(buffer) - 1]; - const char* startOfFormatSpecifier = 0; - - while (true) { - char c = *p++; - char* curr = buffer; - - // Print leading normal chars: - while (c != '\0' && c != '%') { - *curr++ = c; - if (curr == end) { - // Out of buffer space. Flush the fragment, and start over. - *curr = '\0'; - bool success = printArg("%s", buffer); - if (!success) { - errorStr = buffer; - goto handleError; - } - curr = buffer; - } - c = *p++; - } - // If we have stuff in the buffer, flush the fragment: - if (curr != buffer) { - ASSERT(curr < end + 1); - *curr = '\0'; - bool success = printArg("%s", buffer); - if (!success) { - errorStr = buffer; - goto handleError; - } - } - - // End if there are not more chars to print: - if (c == '\0') - break; - - // If we get here, we've must have seen a '%': - startOfFormatSpecifier = p - 1; - ASSERT(*startOfFormatSpecifier == '%'); - c = *p++; - - // Check for "%%" case: - if (c == '%') { - bool success = printArg("%c", '%'); - if (!success) { - errorStr = p - 2; - goto handleError; - } - continue; - } - - // Check for JS (%J<x>) formatting extensions: - if (c == 'J') { - bool verbose = false; - - c = *p++; - if (UNLIKELY(c == '\0')) { - errorStr = p - 2; // Rewind to % in "%J\0" - goto handleError; - } - - if (c == '+') { - verbose = true; - c= *p++; - if (UNLIKELY(c == '\0')) { - errorStr = p - 3; // Rewind to % in "%J+\0" - goto handleError; - } - } - - switch (c) { - // %Js - WTF::String* - case 's': { - printWTFString(args, verbose); - continue; - } - } // END switch. - - // Check for non-JS extensions: - } else if (c == 'b') { - int value = va_arg(args, int); - printArg("%s", value ? "TRUE" : "FALSE"); - continue; - } - - // If we didn't handle the format in one of the above cases, - // rewind p and let the standard formatting check handle it - // if possible: - p = startOfFormatSpecifier; - ASSERT(*p == '%'); - - // Check for standard formatting: - // A format specifier always starts with a % and ends with some - // alphabet. We'll do the simple thing and scan until the next - // alphabet, or the end of string. - - // In the following, we're going to use buffer as storage for a copy - // of a single format specifier. Hence, conceptually, we can think of - // 'buffer' as synonymous with 'argFormat' here: - -#define ABORT_IF_FORMAT_TOO_LONG(curr) \ - do { \ - if (UNLIKELY(curr >= end)) \ - goto formatTooLong; \ - } while (false) - - curr = buffer; - *curr++ = *p++; // Output the first % in the format specifier. - c = *p++; // Grab the next char in the format specifier. - - // Checks for leading modifiers e.g. "%-d": - // 0, -, ' ', +, '\'' - if (c == '0' || c == '-' || c == ' ' || c == '+' || c == '\'' || c == '#') { - ABORT_IF_FORMAT_TOO_LONG(curr); - *curr++ = c; - c = *p++; - } - - // Checks for decimal digit field width modifiers e.g. "%2f": - while (c >= '0' && c <= '9') { - ABORT_IF_FORMAT_TOO_LONG(curr); - *curr++ = c; - c = *p++; - } - - // Checks for '.' e.g. "%2.f": - if (c == '.') { - ABORT_IF_FORMAT_TOO_LONG(curr); - *curr++ = c; - c = *p++; - - // Checks for decimal digit precision modifiers e.g. "%.2f": - while (c >= '0' && c <= '9') { - ABORT_IF_FORMAT_TOO_LONG(curr); - *curr++ = c; - c = *p++; - } - } - - // Checks for the modifier <m> where <m> can be: - // l, h, j, t, z - // e.g. "%ld" - if (c == 'l' || c == 'h' || c == 'j' || c == 't' || c == 'z' || c == 'L') { - ABORT_IF_FORMAT_TOO_LONG(curr); - *curr++ = c; - char prevChar = c; - c = *p++; - - // Checks for the modifier ll or hh in %<x><m>: - if ((prevChar == 'l' || prevChar == 'h') && c == prevChar) { - ABORT_IF_FORMAT_TOO_LONG(curr); - *curr++ = c; - c = *p++; - } - } - - // Checks for %<x> where <x> can be: - // d, i, n, o, u, x, X - // But hey, we're just going to do the simple thing and allow any - // alphabet. The user is expected to pass correct format specifiers. - // We won't do any format checking here. We'll just pass it on, and the - // underlying ...printf() implementation may do the needed checking - // at its discretion. - while (c != '\0' && !isASCIIAlpha(c)) { - ABORT_IF_FORMAT_TOO_LONG(curr); - *curr++ = c; - c = *p++; - } - - ABORT_IF_FORMAT_TOO_LONG(curr); - *curr++ = c; - if (c == '\0') { - // Uh oh. Bad format. We should have gotten an alphabet instead. - // Print the supposed format as a string instead: - errorStr = buffer; - goto handleError; - } - - // Otherwise, we have the alpha that terminates the format. - // Terminate the buffer (i.e. argFormat) string: - ASSERT(isASCIIAlpha(c)); - ABORT_IF_FORMAT_TOO_LONG(curr); - *curr = '\0'; - - bool success = printArg(buffer, args); - if (!success) { - errorStr = buffer; - goto handleError; - } - } -#undef ABORT_IF_FORMAT_TOO_LONG - - return; - -formatTooLong: - // Print the error string: - ASSERT(!!startOfFormatSpecifier); - p = startOfFormatSpecifier; - ASSERT(p >= format); - printArg("ERROR @ Format too long at \"%s\"\n", p); - return; - -handleError: - // We've got an error. Can't do any more work. Print an error message if - // possible and then just return. - - // The errorStr may be pointing into the middle of buffer, or the original - // format string. Move the string to buffer for consistency, and also so - // that we can strip it of newlines below. - if (errorStr != buffer) { - size_t length = strlen(errorStr); - if (length > sizeof(buffer) - 1) - length = sizeof(buffer) - 1; - memmove(buffer, errorStr, length); - buffer[length] = '\0'; // Terminate the moved error string. - } - // Strip the newlines: - char* cp = buffer; - while (*cp) { - if (*cp == '\n' || *cp == '\r') - *cp = ' '; - cp++; - } - // Print the error string: - printArg("ERROR @ \"%s\"\n", buffer); -} - - -bool FormatPrinter::printArg(const char* format, ...) -{ - va_list args; - va_start(args, format); - bool success = printArg(format, args); - va_end(args); - return success; -} - -bool FormatPrinter::printArg(const char* format, va_list args) -{ - int count = ::vprintf(format, args); - return (count >= 0); // Fail if less than 0 chars printed. -} - - -// %Js - WTF::String* -// verbose mode prints: WTF::String "<your string>" -void FormatPrinter::printWTFString(va_list args, bool verbose) -{ - const String* str = va_arg(args, const String*); - - // Print verbose header if appropriate: - if (verbose) - printArg("WTF::String \""); - - // Print the string itself: - if (!str->isEmpty()) { - if (str->is8Bit()) { - const LChar* chars = str->characters8(); - printArg("%s", reinterpret_cast<const char*>(chars)); - } else { - const UChar* chars = str->characters16(); - printArg("%S", reinterpret_cast<const wchar_t*>(chars)); - } - } - - // Print verbose footer if appropriate: - if (verbose) - printArg("\""); -} - - -//============================================================================ -// class FileFormatPrinter -// - implements functionality to support fprintf. - -class FileFormatPrinter: public FormatPrinter { -public: - FileFormatPrinter(FILE*); -private: - virtual bool printArg(const char* format, va_list args); - - FILE* m_file; -}; - -FileFormatPrinter::FileFormatPrinter(FILE* file) - : m_file(file) -{ -} - -bool FileFormatPrinter::printArg(const char* format, va_list args) -{ - int count = ::vfprintf(m_file, format, args); - return (count >= 0); // Fail if less than 0 chars printed. -} - - -//============================================================================ -// class StringFormatPrinter -// - implements functionality to support sprintf. - -class StringFormatPrinter: public FormatPrinter { -public: - StringFormatPrinter(char* buffer); -private: - virtual bool printArg(const char* format, va_list args); - - char* m_buffer; -}; - -StringFormatPrinter::StringFormatPrinter(char* buffer) - : m_buffer(buffer) -{ -} - -bool StringFormatPrinter::printArg(const char* format, va_list args) -{ - int count = ::vsprintf(m_buffer, format, args); - m_buffer += count; - return (count >= 0); // Fail if less than 0 chars printed. -} - - -//============================================================================ -// class StringNFormatPrinter -// - implements functionality to support snprintf. - -class StringNFormatPrinter: public FormatPrinter { -public: - StringNFormatPrinter(char* buffer, size_t); -private: - virtual bool printArg(const char* format, va_list args); - - char* m_buffer; - size_t m_size; -}; - - -StringNFormatPrinter::StringNFormatPrinter(char* buffer, size_t size) - : m_buffer(buffer) - , m_size(size) -{ -} - -bool StringNFormatPrinter::printArg(const char* format, va_list args) -{ - if (m_size > 0) { - int count = ::vsnprintf(m_buffer, m_size, format, args); - - // According to vsnprintf specs, ... - bool success = (count >= 0); - if (static_cast<size_t>(count) >= m_size) { - // If count > size, then we didn't have enough buffer space. - count = m_size; - } - - // Adjust the buffer to what's left if appropriate: - if (success) { - m_buffer += count; - m_size -= count; - } - return success; - } - // No more room to print. Declare it a fail: - return false; -} - - -//============================================================================ -// VMInspector printf family of methods: - -void VMInspector::fprintf(FILE* file, const char* format, ...) -{ - va_list args; - va_start(args, format); - FileFormatPrinter(file).print(format, args); - va_end(args); -} - -void VMInspector::printf(const char* format, ...) -{ - va_list args; - va_start(args, format); - FormatPrinter().print(format, args); - va_end(args); -} - -void VMInspector::sprintf(char* buffer, const char* format, ...) -{ - va_list args; - va_start(args, format); - StringFormatPrinter(buffer).print(format, args); - va_end(args); -} - -void VMInspector::snprintf(char* buffer, size_t size, const char* format, ...) -{ - va_list args; - va_start(args, format); - StringNFormatPrinter(buffer, size).print(format, args); - va_end(args); -} - -} // namespace JSC - -#endif // ENABLE(VMINSPECTOR) diff --git a/Source/JavaScriptCore/interpreter/VMInspector.h b/Source/JavaScriptCore/interpreter/VMInspector.h deleted file mode 100644 index 6623068dc..000000000 --- a/Source/JavaScriptCore/interpreter/VMInspector.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2012 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. OR - * 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 VMInspector_h -#define VMInspector_h - -#define ENABLE_VMINSPECTOR 0 - -#include "CallFrame.h" -#include "JSCJSValue.h" -#include <stdarg.h> -#include <stdio.h> -#include <wtf/text/WTFString.h> - -namespace JSC { - -#if ENABLE(VMINSPECTOR) - -class VMInspector { -public: - static JS_EXPORT_PRIVATE const char* getTypeName(JSValue); - static JS_EXPORT_PRIVATE void dumpFrame0(CallFrame*); - static JS_EXPORT_PRIVATE void dumpFrame(CallFrame*, const char* prefix = 0, const char* funcName = 0, const char* file = 0, int line = -1); - static JS_EXPORT_PRIVATE int countFrames(CallFrame*); - - // Special family of ...printf() functions that support, in addition to the - // standard % formats (e.g. %d, %s, etc), the following extra JSC formatting - // options, %J<x>, where <x> consists of: - // - // + - verbose mode modifier. - // Used in combination with other options. Must come after the %J. - // s - WTF::String* - // - // Examples of usage: - // - // WTF::String str("My WTF String"); - // - // // Printing the string. Will print: - // // The wtf string says: "My WTF String" and is NOT EMPTY. - // - // VMInspector::printf("The wtf string says: \"%Js\" and is %s\n", - // &str, str.isEmpty()?"EMPTY":"NOT EMPTY"); - // - // // Printing the string with verbose mode. Will print: - // // <WTF::String "My WTF String"> - // - // VMInspector::printf("<%J+s>\n", &str); - // - // Also added some convenience non-JS formats: - // - // %b - boolean (va_args will look for an int). - // Prints TRUE if non-zero, else prints FALSE. - // - // Caution: the user is expected to pass the correctly matched arguments - // to pair with the corresponding % fomatting. - - static JS_EXPORT_PRIVATE void fprintf(FILE*, const char* format, ...); - static JS_EXPORT_PRIVATE void printf(const char* format, ...); - static JS_EXPORT_PRIVATE void sprintf(char*, const char* format, ...); - static JS_EXPORT_PRIVATE void snprintf(char*, size_t, const char* format, ...); -}; - -#endif // ENABLE(VMINSPECTOR) - -} // namespace JSC - -#endif // VMInspector.h |
