summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/interpreter
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2016-05-24 08:28:08 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2016-05-24 08:28:08 +0000
commita4e969f4965059196ca948db781e52f7cfebf19e (patch)
tree6ca352808c8fdc52006a0f33f6ae3c593b23867d /Source/JavaScriptCore/interpreter
parent41386e9cb918eed93b3f13648cbef387e371e451 (diff)
downloadWebKitGtk-tarball-a4e969f4965059196ca948db781e52f7cfebf19e.tar.gz
webkitgtk-2.12.3webkitgtk-2.12.3
Diffstat (limited to 'Source/JavaScriptCore/interpreter')
-rw-r--r--Source/JavaScriptCore/interpreter/AbstractPC.cpp4
-rw-r--r--Source/JavaScriptCore/interpreter/AbstractPC.h1
-rw-r--r--Source/JavaScriptCore/interpreter/CachedCall.h2
-rw-r--r--Source/JavaScriptCore/interpreter/CallFrame.cpp228
-rw-r--r--Source/JavaScriptCore/interpreter/CallFrame.h258
-rw-r--r--Source/JavaScriptCore/interpreter/CallFrameClosure.h5
-rw-r--r--Source/JavaScriptCore/interpreter/CallFrameInlines.h143
-rw-r--r--Source/JavaScriptCore/interpreter/Interpreter.cpp746
-rw-r--r--Source/JavaScriptCore/interpreter/Interpreter.h154
-rw-r--r--Source/JavaScriptCore/interpreter/JSStack.cpp147
-rw-r--r--Source/JavaScriptCore/interpreter/JSStack.h104
-rw-r--r--Source/JavaScriptCore/interpreter/JSStackInlines.h263
-rw-r--r--Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp20
-rw-r--r--Source/JavaScriptCore/interpreter/ProtoCallFrame.h17
-rw-r--r--Source/JavaScriptCore/interpreter/Register.h31
-rw-r--r--Source/JavaScriptCore/interpreter/StackVisitor.cpp310
-rw-r--r--Source/JavaScriptCore/interpreter/StackVisitor.h45
-rw-r--r--Source/JavaScriptCore/interpreter/VMEntryRecord.h56
-rw-r--r--Source/JavaScriptCore/interpreter/VMInspector.cpp572
-rw-r--r--Source/JavaScriptCore/interpreter/VMInspector.h89
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*>(&current);
+ 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