summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/interpreter
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2015-05-20 09:56:07 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2015-05-20 09:56:07 +0000
commit41386e9cb918eed93b3f13648cbef387e371e451 (patch)
treea97f9d7bd1d9d091833286085f72da9d83fd0606 /Source/JavaScriptCore/interpreter
parente15dd966d523731101f70ccf768bba12435a0208 (diff)
downloadWebKitGtk-tarball-41386e9cb918eed93b3f13648cbef387e371e451.tar.gz
webkitgtk-2.4.9webkitgtk-2.4.9
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.cpp79
-rw-r--r--Source/JavaScriptCore/interpreter/CallFrame.h169
-rw-r--r--Source/JavaScriptCore/interpreter/CallFrameClosure.h5
-rw-r--r--Source/JavaScriptCore/interpreter/CallFrameInlines.h15
-rw-r--r--Source/JavaScriptCore/interpreter/Interpreter.cpp512
-rw-r--r--Source/JavaScriptCore/interpreter/Interpreter.h134
-rw-r--r--Source/JavaScriptCore/interpreter/JSStack.cpp144
-rw-r--r--Source/JavaScriptCore/interpreter/JSStack.h103
-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.h20
-rw-r--r--Source/JavaScriptCore/interpreter/StackVisitor.cpp274
-rw-r--r--Source/JavaScriptCore/interpreter/StackVisitor.h43
-rw-r--r--Source/JavaScriptCore/interpreter/VMEntryRecord.h54
-rw-r--r--Source/JavaScriptCore/interpreter/VMInspector.cpp572
-rw-r--r--Source/JavaScriptCore/interpreter/VMInspector.h89
20 files changed, 1661 insertions, 859 deletions
diff --git a/Source/JavaScriptCore/interpreter/AbstractPC.cpp b/Source/JavaScriptCore/interpreter/AbstractPC.cpp
index 09b24d5dc..8600b7228 100644
--- a/Source/JavaScriptCore/interpreter/AbstractPC.cpp
+++ b/Source/JavaScriptCore/interpreter/AbstractPC.cpp
@@ -27,9 +27,9 @@
#include "AbstractPC.h"
#include "CallFrame.h"
-#include "JSObject.h"
-#include "JSCInlines.h"
#include "VM.h"
+#include "JSObject.h"
+
namespace JSC {
diff --git a/Source/JavaScriptCore/interpreter/AbstractPC.h b/Source/JavaScriptCore/interpreter/AbstractPC.h
index ac2aaa7ad..c30027d9e 100644
--- a/Source/JavaScriptCore/interpreter/AbstractPC.h
+++ b/Source/JavaScriptCore/interpreter/AbstractPC.h
@@ -27,6 +27,7 @@
#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 30cad2a14..2ca3e9794 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->isHostFunctionNonInline());
+ ASSERT(!function->isHostFunction());
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 259061df4..a226e9848 100644
--- a/Source/JavaScriptCore/interpreter/CallFrame.cpp
+++ b/Source/JavaScriptCore/interpreter/CallFrame.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2013, 2014 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008, 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
@@ -29,10 +29,8 @@
#include "CallFrameInlines.h"
#include "CodeBlock.h"
#include "Interpreter.h"
-#include "JSLexicalEnvironment.h"
-#include "JSCInlines.h"
+#include "Operations.h"
#include "VMEntryScope.h"
-#include <wtf/StringPrintStream.h>
namespace JSC {
@@ -118,11 +116,11 @@ CodeOrigin CallFrame::codeOrigin()
return CodeOrigin(locationAsBytecodeOffset());
}
-Register* CallFrame::topOfFrameInternal()
+Register* CallFrame::frameExtentInternal()
{
CodeBlock* codeBlock = this->codeBlock();
ASSERT(codeBlock);
- return registers() + codeBlock->stackPointerOffset();
+ return registers() + virtualRegisterForLocal(codeBlock->frameRegisterCount()).offset();
}
JSGlobalObject* CallFrame::vmEntryGlobalObject()
@@ -136,73 +134,4 @@ 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());
-}
-
-JSLexicalEnvironment* CallFrame::lexicalEnvironment() const
-{
- CodeBlock* codeBlock = this->codeBlock();
- RELEASE_ASSERT(codeBlock->needsActivation());
- VirtualRegister activationRegister = codeBlock->activationRegister();
- return registers()[activationRegister.offset()].Register::lexicalEnvironment();
-}
-
-JSLexicalEnvironment* CallFrame::lexicalEnvironmentOrNullptr() const
-{
- CodeBlock* codeBlock = this->codeBlock();
- return codeBlock->needsActivation() ? lexicalEnvironment() : nullptr;
-}
-
-void CallFrame::setActivation(JSLexicalEnvironment* lexicalEnvironment)
-{
- CodeBlock* codeBlock = this->codeBlock();
- RELEASE_ASSERT(codeBlock->needsActivation());
- VirtualRegister activationRegister = codeBlock->activationRegister();
- registers()[activationRegister.offset()] = lexicalEnvironment;
-}
-
-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 afa8d3ca7..48fbcd779 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, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2003, 2007, 2008, 2011, 2013 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,17 +24,16 @@
#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 JSLexicalEnvironment;
+ class JSActivation;
class Interpreter;
class JSScope;
@@ -43,19 +42,14 @@ namespace JSC {
class ExecState : private Register {
public:
JSValue calleeAsValue() const { return this[JSStack::Callee].jsValue(); }
- JSObject* callee() const { return this[JSStack::Callee].object(); }
+ JSObject* callee() const { return this[JSStack::Callee].function(); }
CodeBlock* codeBlock() const { return this[JSStack::CodeBlock].Register::codeBlock(); }
- JSScope* scope(int scopeRegisterOffset) const
+ JSScope* scope() const
{
- ASSERT(this[scopeRegisterOffset].Register::scope());
- return this[scopeRegisterOffset].Register::scope();
+ ASSERT(this[JSStack::ScopeChain].Register::scope());
+ return this[JSStack::ScopeChain].Register::scope();
}
- bool hasActivation() const;
- JSLexicalEnvironment* lexicalEnvironment() const;
- JSLexicalEnvironment* lexicalEnvironmentOrNullptr() const;
- JSValue uncheckedActivation() const;
-
// Global object in which execution began.
JS_EXPORT_PRIVATE JSGlobalObject* vmEntryGlobalObject();
@@ -75,30 +69,49 @@ 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();
+ }
- Exception* exception() const { return vm().exception(); }
- bool hadException() const { return !!vm().exception(); }
-
- Exception* lastException() const { return vm().lastException(); }
- void clearLastException() { vm().clearLastException(); }
+ JSValue exception() const { return vm().exception(); }
+ bool hadException() const { return !vm().exception().isEmpty(); }
- 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 static_cast<CallFrame*>(callerFrameOrVMEntryFrame()); }
-
- JS_EXPORT_PRIVATE CallFrame* callerFrame(VMEntryFrame*&);
-
+ CallFrame* callerFrame() const { return callerFrameAndPC().callerFrame; }
static ptrdiff_t callerFrameOffset() { return OBJECT_OFFSETOF(CallerFrameAndPC, callerFrame); }
ReturnAddressPtr returnPC() const { return ReturnAddressPtr(callerFrameAndPC().pc); }
@@ -163,20 +176,24 @@ namespace JSC {
// CodeOrigin(0) if we're in native code.
CodeOrigin codeOrigin();
- Register* topOfFrame()
+ Register* frameExtent()
{
- if (!codeBlock())
- return registers();
- return topOfFrameInternal();
+ if (isVMEntrySentinel() || !codeBlock())
+ return registers() - 1;
+ return frameExtentInternal();
}
+ 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
@@ -185,27 +202,25 @@ namespace JSC {
#endif
void setCallerFrame(CallFrame* frame) { callerFrameAndPC().callerFrame = frame; }
- void setScope(int scopeRegisterOffset, JSScope* scope) { static_cast<Register*>(this)[scopeRegisterOffset] = scope; }
- void setActivation(JSLexicalEnvironment*);
-
- 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);
+ 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);
}
// 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; }
@@ -238,31 +253,51 @@ 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] = callee; }
+ void setCallee(JSObject* callee) { static_cast<Register*>(this)[JSStack::Callee] = Register::withCallee(callee); }
void setCodeBlock(CodeBlock* codeBlock) { static_cast<Register*>(this)[JSStack::CodeBlock] = codeBlock; }
void setReturnPC(void* value) { callerFrameAndPC().pc = reinterpret_cast<Instruction*>(value); }
@@ -274,10 +309,8 @@ 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();
@@ -285,8 +318,6 @@ 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:
@@ -307,12 +338,22 @@ namespace JSC {
return argIndex;
}
- void* callerFrameOrVMEntryFrame() const { return callerFrameAndPC().callerFrame; }
+ 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); }
+
friend class JSStack;
+ friend class VMInspector;
};
} // namespace JSC
diff --git a/Source/JavaScriptCore/interpreter/CallFrameClosure.h b/Source/JavaScriptCore/interpreter/CallFrameClosure.h
index 2c03b7452..e3326626d 100644
--- a/Source/JavaScriptCore/interpreter/CallFrameClosure.h
+++ b/Source/JavaScriptCore/interpreter/CallFrameClosure.h
@@ -49,6 +49,11 @@ 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
index 3f430ec31..51d751a51 100644
--- a/Source/JavaScriptCore/interpreter/CallFrameInlines.h
+++ b/Source/JavaScriptCore/interpreter/CallFrameInlines.h
@@ -27,7 +27,6 @@
#define CallFrameInlines_h
#include "CallFrame.h"
-#include "CodeBlock.h"
namespace JSC {
@@ -139,20 +138,6 @@ inline unsigned CallFrame::locationAsCodeOriginIndex() const
return Location::decode(locationAsRawBits());
}
-inline bool CallFrame::hasActivation() const
-{
- JSValue activation = uncheckedActivation();
- return !!activation && activation.isCell();
-}
-
-inline JSValue CallFrame::uncheckedActivation() const
-{
- CodeBlock* codeBlock = this->codeBlock();
- RELEASE_ASSERT(codeBlock->needsActivation());
- VirtualRegister activationRegister = codeBlock->activationRegister();
- return registers()[activationRegister.offset()].jsValue();
-}
-
} // namespace JSC
#endif // CallFrameInlines_h
diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp
index 518fc0b76..4fbc8229a 100644
--- a/Source/JavaScriptCore/interpreter/Interpreter.cpp
+++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2009, 2010, 2012-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2010, 2012, 2013 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 Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -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 "JSCInlines.h"
-#include "JSLexicalEnvironment.h"
+#include "JSNameScope.h"
#include "JSNotAnObject.h"
+#include "JSPropertyNameIterator.h"
#include "JSStackInlines.h"
#include "JSString.h"
#include "JSWithScope.h"
@@ -56,27 +56,24 @@
#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>
@@ -86,49 +83,26 @@
#include "JIT.h"
#endif
+#define WTF_USE_GCC_COMPUTED_GOTO_WORKAROUND (ENABLE(LLINT) && !defined(__llvm__))
+
using namespace std;
namespace JSC {
-String StackFrame::friendlySourceURL() const
+Interpreter::ErrorHandlingMode::ErrorHandlingMode(ExecState *exec)
+ : m_interpreter(*exec->interpreter())
{
- 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;
+ if (!m_interpreter.m_errorHandlingModeReentry)
+ m_interpreter.stack().enableErrorStackReserve();
+ m_interpreter.m_errorHandlingModeReentry++;
}
-String StackFrame::friendlyFunctionName(CallFrame* callFrame) const
+Interpreter::ErrorHandlingMode::~ErrorHandlingMode()
{
- 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;
+ m_interpreter.m_errorHandlingModeReentry--;
+ ASSERT(m_interpreter.m_errorHandlingModeReentry >= 0);
+ if (!m_interpreter.m_errorHandlingModeReentry)
+ m_interpreter.stack().disableErrorStackReserve();
}
JSValue eval(CallFrame* callFrame)
@@ -147,11 +121,13 @@ JSValue eval(CallFrame* callFrame)
CallFrame* callerFrame = callFrame->callerFrame();
CodeBlock* callerCodeBlock = callerFrame->codeBlock();
- JSScope* callerScopeChain = callerFrame->uncheckedR(callerCodeBlock->scopeRegister().offset()).Register::scope();
+ JSScope* callerScopeChain = callerFrame->scope();
EvalExecutable* eval = callerCodeBlock->evalCodeCache().tryGet(callerCodeBlock->isStrictMode(), programSource, 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())
@@ -166,8 +142,7 @@ JSValue eval(CallFrame* callFrame)
// If the literal parser bailed, it should not have thrown exceptions.
ASSERT(!callFrame->vm().exception());
- ThisTDZMode thisTDZMode = callerCodeBlock->unlinkedCodeBlock()->constructorKind() == ConstructorKind::Derived ? ThisTDZMode::AlwaysCheck : ThisTDZMode::CheckIfNeeded;
- eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock->ownerExecutable(), callerCodeBlock->isStrictMode(), thisTDZMode, programSource, callerScopeChain);
+ eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock->ownerExecutable(), callerCodeBlock->isStrictMode(), programSource, callerScopeChain);
if (!eval)
return jsUndefined();
}
@@ -177,103 +152,109 @@ JSValue eval(CallFrame* callFrame)
return interpreter->execute(eval, callFrame, thisValue, callerScopeChain);
}
-unsigned sizeOfVarargs(CallFrame* callFrame, JSValue arguments, uint32_t firstVarArgOffset)
+CallFrame* sizeAndAllocFrameForVarargs(CallFrame* callFrame, JSStack* stack, JSValue arguments, int firstFreeRegister)
{
- if (UNLIKELY(!arguments.isCell())) {
- if (arguments.isUndefinedOrNull())
+ 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));
return 0;
-
- callFrame->vm().throwException(callFrame, createInvalidFunctionApplyParameterError(callFrame, arguments));
- 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));
+
+ 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));
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;
-}
-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);
+ 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;
+ }
+
+ 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));
return 0;
}
-
- return length;
+ return newCallFrame;
}
-void loadVarargs(CallFrame* callFrame, VirtualRegister firstElementDest, JSValue arguments, uint32_t offset, uint32_t length)
+void loadVarargs(CallFrame* callFrame, CallFrame* newCallFrame, JSValue thisValue, JSValue arguments)
{
- if (UNLIKELY(!arguments.isCell()))
+ 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));
return;
+ }
- JSCell* cell = arguments.asCell();
- switch (cell->type()) {
- case DirectArgumentsType:
- jsCast<DirectArguments*>(cell)->copyToArguments(callFrame, firstElementDest, offset, length);
+ if (arguments.isUndefinedOrNull()) {
+ newCallFrame->setArgumentCountIncludingThis(1);
+ newCallFrame->setThisValue(thisValue);
return;
- case ScopedArgumentsType:
- jsCast<ScopedArguments*>(cell)->copyToArguments(callFrame, firstElementDest, offset, length);
- return;
- 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);
+ }
+
+ 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);
return;
- } }
-}
-
-void setupVarargsFrame(CallFrame* callFrame, CallFrame* newCallFrame, JSValue arguments, uint32_t offset, uint32_t length)
-{
- VirtualRegister calleeFrameOffset(newCallFrame - callFrame);
+ }
- loadVarargs(
- callFrame,
- calleeFrameOffset + CallFrame::argumentOffset(0),
- arguments, offset, length);
+ if (isJSArray(arguments)) {
+ JSArray* array = asArray(arguments);
+ unsigned argCount = array->length();
+ newCallFrame->setArgumentCountIncludingThis(argCount + 1);
+ newCallFrame->setThisValue(thisValue);
+ array->copyToArguments(callFrame, newCallFrame, argCount);
+ return;
+ }
- 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);
+ JSObject* argObject = asObject(arguments);
+ unsigned argCount = argObject->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame);
+ newCallFrame->setArgumentCountIncludingThis(argCount + 1);
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)
@@ -295,7 +276,7 @@ void Interpreter::initialize(bool canUseJIT)
{
UNUSED_PARAM(canUseJIT);
-#if ENABLE(COMPUTED_GOTO_OPCODES)
+#if ENABLE(COMPUTED_GOTO_OPCODES) && ENABLE(LLINT)
m_opcodeTable = LLInt::opcodeMap();
for (int i = 0; i < numOpcodeIDs; ++i)
m_opcodeIDTable.add(m_opcodeTable[i], static_cast<OpcodeID>(i));
@@ -380,7 +361,7 @@ void Interpreter::dumpRegisters(CallFrame* callFrame)
--it;
dataLogF("[Callee] | %10p | %p \n", it, callFrame->callee());
--it;
- // FIXME: Remove the next decrement when the ScopeChain slot is removed from the call header
+ dataLogF("[ScopeChain] | %10p | %p \n", it, callFrame->scope());
--it;
#if ENABLE(JIT)
AbstractPC pc = callFrame->abstractReturnPC(callFrame->vm());
@@ -424,9 +405,13 @@ 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
@@ -435,16 +420,46 @@ bool Interpreter::isOpcode(Opcode opcode)
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()) {
- SuspendExceptionScope scope(&callFrame->vm());
- if (jsDynamicCast<JSFunction*>(callFrame->callee()))
+ if (callFrame->callee())
debugger->returnEvent(callFrame);
else
debugger->didExecuteProgram(callFrame);
- ASSERT(!callFrame->hadException());
}
- return !visitor->callerIsVMEntryFrame();
+ 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)
@@ -481,9 +496,6 @@ 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)
@@ -529,15 +541,15 @@ public:
{
VM& vm = m_vm;
if (m_remainingCapacityForFrameCapture) {
- if (visitor->isJSFrame() && !visitor->codeBlock()->unlinkedCodeBlock()->isBuiltinFunction()) {
+ if (visitor->isJSFrame()) {
CodeBlock* codeBlock = visitor->codeBlock();
StackFrame s = {
Strong<JSObject>(vm, visitor->callee()),
getStackFrameCodeType(visitor),
- Strong<ScriptExecutable>(vm, codeBlock->ownerExecutable()),
+ Strong<ExecutableBase>(vm, codeBlock->ownerExecutable()),
Strong<UnlinkedCodeBlock>(vm, codeBlock->unlinkedCodeBlock()),
codeBlock->source(),
- codeBlock->ownerExecutable()->firstLine(),
+ codeBlock->ownerExecutable()->lineNo(),
codeBlock->firstLineColumnOffset(),
codeBlock->sourceOffset(),
visitor->bytecodeOffset(),
@@ -545,7 +557,7 @@ public:
};
m_results.append(s);
} else {
- StackFrame s = { Strong<JSObject>(vm, visitor->callee()), StackFrameNativeCode, Strong<ScriptExecutable>(), Strong<UnlinkedCodeBlock>(), 0, 0, 0, 0, 0, String()};
+ StackFrame s = { Strong<JSObject>(vm, visitor->callee()), StackFrameNativeCode, Strong<ExecutableBase>(), Strong<UnlinkedCodeBlock>(), 0, 0, 0, 0, 0, String()};
m_results.append(s);
}
@@ -564,6 +576,7 @@ 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;
@@ -584,9 +597,9 @@ JSString* Interpreter::stackTraceAsString(ExecState* exec, Vector<StackFrame> st
return jsString(&exec->vm(), builder.toString());
}
-class GetCatchHandlerFunctor {
+class GetExceptionHandlerFunctor {
public:
- GetCatchHandlerFunctor()
+ GetExceptionHandlerFunctor()
: m_handler(0)
{
}
@@ -600,7 +613,7 @@ public:
return StackVisitor::Continue;
unsigned bytecodeOffset = visitor->bytecodeOffset();
- m_handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset, CodeBlock::RequiredHandler::CatchHandler);
+ m_handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset);
if (m_handler)
return StackVisitor::Done;
@@ -613,9 +626,8 @@ private:
class UnwindFunctor {
public:
- UnwindFunctor(VMEntryFrame*& vmEntryFrame, CallFrame*& callFrame, bool isTermination, CodeBlock*& codeBlock, HandlerInfo*& handler)
- : m_vmEntryFrame(vmEntryFrame)
- , m_callFrame(callFrame)
+ UnwindFunctor(CallFrame*& callFrame, bool isTermination, CodeBlock*& codeBlock, HandlerInfo*& handler)
+ : m_callFrame(callFrame)
, m_isTermination(isTermination)
, m_codeBlock(codeBlock)
, m_handler(handler)
@@ -625,12 +637,11 @@ public:
StackVisitor::Status operator()(StackVisitor& visitor)
{
VM& vm = m_callFrame->vm();
- m_vmEntryFrame = visitor->vmEntryFrame();
m_callFrame = visitor->callFrame();
m_codeBlock = visitor->codeBlock();
unsigned bytecodeOffset = visitor->bytecodeOffset();
- if (m_isTermination || !(m_handler = m_codeBlock ? m_codeBlock->handlerForBytecodeOffset(bytecodeOffset) : nullptr)) {
+ if (m_isTermination || !(m_handler = m_codeBlock->handlerForBytecodeOffset(bytecodeOffset))) {
if (!unwindCallFrame(visitor)) {
if (LegacyProfiler* profiler = vm.enabledProfiler())
profiler->exceptionUnwind(m_callFrame);
@@ -643,19 +654,17 @@ public:
}
private:
- VMEntryFrame*& m_vmEntryFrame;
CallFrame*& m_callFrame;
bool m_isTermination;
CodeBlock*& m_codeBlock;
HandlerInfo*& m_handler;
};
-NEVER_INLINE HandlerInfo* Interpreter::unwind(VMEntryFrame*& vmEntryFrame, CallFrame*& callFrame, Exception* exception)
+NEVER_INLINE HandlerInfo* Interpreter::unwind(CallFrame*& callFrame, JSValue& exceptionValue)
{
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
@@ -663,42 +672,39 @@ NEVER_INLINE HandlerInfo* Interpreter::unwind(VMEntryFrame*& vmEntryFrame, CallF
if (exceptionValue.isEmpty() || (exceptionValue.isCell() && !exceptionValue.asCell()))
exceptionValue = jsNull();
- if (exceptionValue.isObject())
- isTermination = isTerminatedExecutionException(exception);
+ if (exceptionValue.isObject()) {
+ isTermination = isTerminatedExecutionException(asObject(exceptionValue));
+ }
- ASSERT(callFrame->vm().exception() && callFrame->vm().exception()->stack().size());
+ ASSERT(callFrame->vm().exceptionStack().size());
Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger();
- if (debugger && debugger->needsExceptionCallbacks() && !exception->didNotifyInspectorOfThrow()) {
- // We need to clear the exception here in order to see if a new exception happens.
+ 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.
- SuspendExceptionScope scope(&callFrame->vm());
+ ClearExceptionScope scope(&callFrame->vm());
// 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 hasCatchHandler;
+ bool hasHandler;
if (isTermination)
- hasCatchHandler = false;
+ hasHandler = false;
else {
- GetCatchHandlerFunctor functor;
+ GetExceptionHandlerFunctor functor;
callFrame->iterate(functor);
- HandlerInfo* handler = functor.handler();
- ASSERT(!handler || handler->isCatchHandler());
- hasCatchHandler = !!handler;
+ hasHandler = !!functor.handler();
}
- debugger->exception(callFrame, exceptionValue, hasCatchHandler);
- ASSERT(!callFrame->hadException());
+ debugger->exception(callFrame, exceptionValue, hasHandler);
}
- exception->setDidNotifyInspectorOfThrow();
// Calculate an exception handler vPC, unwinding call frames as necessary.
HandlerInfo* handler = 0;
VM& vm = callFrame->vm();
ASSERT(callFrame == vm.topCallFrame);
- UnwindFunctor functor(vmEntryFrame, callFrame, isTermination, codeBlock, handler);
+ UnwindFunctor functor(callFrame, isTermination, codeBlock, handler);
callFrame->iterate(functor);
if (!handler)
return 0;
@@ -706,6 +712,19 @@ NEVER_INLINE HandlerInfo* Interpreter::unwind(VMEntryFrame*& vmEntryFrame, CallF
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;
}
@@ -739,16 +758,16 @@ private:
JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, JSObject* thisObj)
{
SamplingScope samplingScope(this);
-
- JSScope* scope = thisObj->globalObject();
+
+ JSScope* scope = callFrame->scope();
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));
@@ -792,7 +811,7 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J
PropertySlot slot(globalObject);
if (!globalObject->getPropertySlot(callFrame, JSONPPath[i].m_pathEntryName, slot)) {
if (entry)
- return callFrame->vm().throwException(callFrame, createUndefinedVariableError(callFrame, JSONPPath[i].m_pathEntryName));
+ return callFrame->vm().throwException(callFrame, createUndefinedVariableError(globalObject->globalExec(), JSONPPath[i].m_pathEntryName));
goto failedJSONP;
}
baseObject = slot.getValue(callFrame, JSONPPath[i].m_pathEntryName);
@@ -855,37 +874,40 @@ 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, nullptr, scope, CodeForCall))
+ if (JSObject* error = program->prepareForExecution(callFrame, scope, CodeForCall))
return checkedReturn(callFrame->vm().throwException(callFrame, error));
ProgramCodeBlock* codeBlock = program->codeBlock();
- if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
+ if (UNLIKELY(vm.watchdog.didFire(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, JSCallee::create(vm, scope->globalObject(), scope), thisObj, 1);
+ protoCallFrame.init(codeBlock, scope, 0, thisObj, 1);
if (LegacyProfiler* profiler = vm.enabledProfiler())
- profiler->willExecute(callFrame, program->sourceURL(), program->firstLine(), program->startColumn());
+ profiler->willExecute(callFrame, program->sourceURL(), program->lineNo());
// Execute the code:
JSValue result;
{
SamplingTool::CallRecord callRecord(m_sampler.get());
- result = program->generatedJITCode()->execute(&vm, &protoCallFrame);
+ Watchdog::Scope watchdogScope(vm.watchdog);
+
+ result = program->generatedJITCode()->execute(&vm, &protoCallFrame, m_stack.getTopOfStack());
}
if (LegacyProfiler* profiler = vm.enabledProfiler())
- profiler->didExecute(callFrame, program->sourceURL(), program->firstLine(), program->startColumn());
+ profiler->didExecute(callFrame, program->sourceURL(), program->lineNo());
return checkedReturn(result);
}
@@ -899,27 +921,24 @@ JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallT
return jsNull();
bool isJSCall = (callType == CallTypeJS);
- JSScope* scope = nullptr;
+ JSScope* scope;
CodeBlock* newCodeBlock;
size_t argsCount = 1 + args.size(); // implicit "this" parameter
- JSGlobalObject* globalObject;
-
- if (isJSCall) {
+ if (isJSCall)
scope = callData.js.scope;
- globalObject = scope->globalObject();
- } else {
+ else {
ASSERT(callType == CallTypeHost);
- globalObject = function->globalObject();
+ scope = callFrame->scope();
}
- VMEntryScope entryScope(vm, globalObject);
+ VMEntryScope entryScope(vm, scope->globalObject());
if (!vm.isSafeToRecurse())
return checkedReturn(throwStackOverflowError(callFrame));
if (isJSCall) {
// Compile the callee:
- JSObject* compileError = callData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(function), scope, CodeForCall);
+ JSObject* compileError = callData.js.functionExecutable->prepareForExecution(callFrame, scope, CodeForCall);
if (UNLIKELY(!!compileError)) {
return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
}
@@ -929,11 +948,14 @@ JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallT
} else
newCodeBlock = 0;
- if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
+ if (UNLIKELY(vm.watchdog.didFire(callFrame)))
return throwTerminatedExecutionException(callFrame);
+ if (UNLIKELY(!m_stack.entryCheck(newCodeBlock, argsCount)))
+ return checkedReturn(throwStackOverflowError(callFrame));
+
ProtoCallFrame protoCallFrame;
- protoCallFrame.init(newCodeBlock, function, thisValue, argsCount, args.data());
+ protoCallFrame.init(newCodeBlock, scope, function, thisValue, argsCount, args.data());
if (LegacyProfiler* profiler = vm.enabledProfiler())
profiler->willExecute(callFrame, function);
@@ -941,15 +963,13 @@ 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);
- else {
- result = JSValue::decode(vmEntryToNative(reinterpret_cast<void*>(callData.native.function), &vm, &protoCallFrame));
- if (callFrame->hadException())
- result = jsNull();
- }
+ 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()));
}
if (LegacyProfiler* profiler = vm.enabledProfiler())
@@ -958,7 +978,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, JSValue newTarget)
+JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* constructor, ConstructType constructType, const ConstructData& constructData, const ArgList& args)
{
VM& vm = callFrame->vm();
ASSERT(!callFrame->hadException());
@@ -969,27 +989,24 @@ JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* construc
return checkedReturn(throwStackOverflowError(callFrame));
bool isJSConstruct = (constructType == ConstructTypeJS);
- JSScope* scope = nullptr;
+ JSScope* scope;
CodeBlock* newCodeBlock;
size_t argsCount = 1 + args.size(); // implicit "this" parameter
- JSGlobalObject* globalObject;
-
- if (isJSConstruct) {
+ if (isJSConstruct)
scope = constructData.js.scope;
- globalObject = scope->globalObject();
- } else {
+ else {
ASSERT(constructType == ConstructTypeHost);
- globalObject = constructor->globalObject();
+ scope = callFrame->scope();
}
- VMEntryScope entryScope(vm, globalObject);
+ VMEntryScope entryScope(vm, scope->globalObject());
if (!vm.isSafeToRecurse())
return checkedReturn(throwStackOverflowError(callFrame));
if (isJSConstruct) {
// Compile the callee:
- JSObject* compileError = constructData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(constructor), scope, CodeForConstruct);
+ JSObject* compileError = constructData.js.functionExecutable->prepareForExecution(callFrame, scope, CodeForConstruct);
if (UNLIKELY(!!compileError)) {
return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
}
@@ -999,11 +1016,14 @@ JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* construc
} else
newCodeBlock = 0;
- if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
+ if (UNLIKELY(vm.watchdog.didFire(callFrame)))
return throwTerminatedExecutionException(callFrame);
+ if (UNLIKELY(!m_stack.entryCheck(newCodeBlock, argsCount)))
+ return checkedReturn(throwStackOverflowError(callFrame));
+
ProtoCallFrame protoCallFrame;
- protoCallFrame.init(newCodeBlock, constructor, newTarget, argsCount, args.data());
+ protoCallFrame.init(newCodeBlock, scope, constructor, jsUndefined(), argsCount, args.data());
if (LegacyProfiler* profiler = vm.enabledProfiler())
profiler->willExecute(callFrame, constructor);
@@ -1011,12 +1031,13 @@ 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);
+ result = constructData.js.functionExecutable->generatedJITCodeForConstruct()->execute(&vm, &protoCallFrame, m_stack.getTopOfStack());
else {
- result = JSValue::decode(vmEntryToNative(reinterpret_cast<void*>(constructData.native.function), &vm, &protoCallFrame));
+ result = JSValue::decode(callToNativeFunction(reinterpret_cast<void*>(constructData.native.function), &vm.topCallFrame, &protoCallFrame, m_stack.getTopOfStack()));
if (!callFrame->hadException())
RELEASE_ASSERT(result.isObject());
@@ -1041,7 +1062,7 @@ CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionE
return CallFrameClosure();
// Compile the callee:
- JSObject* error = functionExecutable->prepareForExecution(callFrame, function, scope, CodeForCall);
+ JSObject* error = functionExecutable->prepareForExecution(callFrame, scope, CodeForCall);
if (error) {
callFrame->vm().throwException(callFrame, error);
return CallFrameClosure();
@@ -1051,7 +1072,12 @@ CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionE
size_t argsCount = argumentCountIncludingThis;
- protoCallFrame->init(newCodeBlock, function, jsUndefined(), argsCount, args);
+ if (UNLIKELY(!m_stack.entryCheck(newCodeBlock, argsCount))) {
+ throwStackOverflowError(callFrame);
+ return CallFrameClosure();
+ }
+
+ protoCallFrame->init(newCodeBlock, scope, function, jsUndefined(), argsCount, args);
// Return the successful closure:
CallFrameClosure result = { callFrame, protoCallFrame, function, functionExecutable, &vm, scope, newCodeBlock->numParameters(), argumentCountIncludingThis };
return result;
@@ -1063,23 +1089,25 @@ 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.shouldTriggerTermination(closure.oldCallFrame)))
+ if (UNLIKELY(vm.watchdog.didFire(closure.oldCallFrame)))
return throwTerminatedExecutionException(closure.oldCallFrame);
// Execute the code:
JSValue result;
{
SamplingTool::CallRecord callRecord(m_sampler.get());
- result = closure.functionExecutable->generatedJITCodeForCall()->execute(&vm, closure.protoCallFrame);
+ Watchdog::Scope watchdogScope(vm.watchdog);
+
+ result = closure.functionExecutable->generatedJITCodeForCall()->execute(&vm, closure.protoCallFrame, m_stack.getTopOfStack());
}
if (LegacyProfiler* profiler = vm.enabledProfiler())
@@ -1096,7 +1124,6 @@ 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();
@@ -1109,26 +1136,19 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue
JSScope* variableObject;
if ((numVariables || numFunctions) && eval->isStrictMode()) {
- scope = StrictEvalActivation::create(callFrame, scope);
+ scope = StrictEvalActivation::create(callFrame);
variableObject = scope;
} else {
for (JSScope* node = scope; ; node = node->next()) {
RELEASE_ASSERT(node);
- if (node->isGlobalObject()) {
+ if (node->isVariableObject() && !node->isNameScopeObject()) {
variableObject = node;
break;
- }
- if (JSLexicalEnvironment* lexicalEnvironment = jsDynamicCast<JSLexicalEnvironment*>(node)) {
- if (lexicalEnvironment->symbolTable()->scopeType() == SymbolTable::ScopeType::VarScope) {
- variableObject = node;
- break;
- }
}
}
- ASSERT(!variableObject->isNameScopeObject());
}
- JSObject* compileError = eval->prepareForExecution(callFrame, nullptr, scope, CodeForCall);
+ JSObject* compileError = eval->prepareForExecution(callFrame, scope, CodeForCall);
if (UNLIKELY(!!compileError))
return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
EvalCodeBlock* codeBlock = eval->codeBlock();
@@ -1136,7 +1156,7 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue
if (numVariables || numFunctions) {
BatchedTransitionOptimizer optimizer(vm, variableObject);
if (variableObject->next())
- variableObject->globalObject()->varInjectionWatchpoint()->fireAll("Executed eval, fired VarInjection watchpoint");
+ variableObject->globalObject()->varInjectionWatchpoint()->fireAll();
for (unsigned i = 0; i < numVariables; ++i) {
const Identifier& ident = codeBlock->variable(i);
@@ -1153,26 +1173,31 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue
}
}
- if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
+ if (UNLIKELY(vm.watchdog.didFire(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, JSCallee::create(vm, scope->globalObject(), scope), thisValue, 1);
+ protoCallFrame.init(codeBlock, scope, 0, thisValue, 1);
if (LegacyProfiler* profiler = vm.enabledProfiler())
- profiler->willExecute(callFrame, eval->sourceURL(), eval->firstLine(), eval->startColumn());
+ profiler->willExecute(callFrame, eval->sourceURL(), eval->lineNo());
// Execute the code:
JSValue result;
{
SamplingTool::CallRecord callRecord(m_sampler.get());
- result = eval->generatedJITCode()->execute(&vm, &protoCallFrame);
+ Watchdog::Scope watchdogScope(vm.watchdog);
+
+ result = eval->generatedJITCode()->execute(&vm, &protoCallFrame, m_stack.getTopOfStack());
}
if (LegacyProfiler* profiler = vm.enabledProfiler())
- profiler->didExecute(callFrame, eval->sourceURL(), eval->firstLine(), eval->startColumn());
+ profiler->didExecute(callFrame, eval->sourceURL(), eval->lineNo());
return checkedReturn(result);
}
@@ -1182,38 +1207,35 @@ NEVER_INLINE void Interpreter::debug(CallFrame* callFrame, DebugHookID debugHook
Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger();
if (!debugger)
return;
-
- ASSERT(callFrame->codeBlock()->hasDebuggerRequests());
- ASSERT(!callFrame->hadException());
+ ASSERT(callFrame->codeBlock()->hasDebuggerRequests() || callFrame->hadException());
switch (debugHookID) {
case DidEnterCallFrame:
debugger->callEvent(callFrame);
- break;
+ return;
case WillLeaveCallFrame:
debugger->returnEvent(callFrame);
- break;
+ return;
case WillExecuteStatement:
debugger->atStatement(callFrame);
- break;
+ return;
case WillExecuteProgram:
debugger->willExecuteProgram(callFrame);
- break;
+ return;
case DidExecuteProgram:
debugger->didExecuteProgram(callFrame);
- break;
+ return;
case DidReachBreakpoint:
debugger->didReachBreakpoint(callFrame);
- break;
+ return;
}
- ASSERT(!callFrame->hadException());
}
void Interpreter::enableSampler()
{
#if ENABLE(OPCODE_SAMPLING)
if (!m_sampler) {
- m_sampler = std::make_unique<SamplingTool>(this);
+ m_sampler = adoptPtr(new SamplingTool(this));
m_sampler->setup();
}
#endif
diff --git a/Source/JavaScriptCore/interpreter/Interpreter.h b/Source/JavaScriptCore/interpreter/Interpreter.h
index ad9df6fc7..cfc721b38 100644
--- a/Source/JavaScriptCore/interpreter/Interpreter.h
+++ b/Source/JavaScriptCore/interpreter/Interpreter.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2013, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2013 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 Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -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,7 +50,6 @@ namespace JSC {
class ExecutableBase;
class FunctionExecutable;
class VM;
- class JSFunction;
class JSGlobalObject;
class LLIntOffsetsExtractor;
class ProgramExecutable;
@@ -81,7 +80,7 @@ namespace JSC {
struct StackFrame {
Strong<JSObject> callee;
StackFrameCodeType codeType;
- Strong<ScriptExecutable> executable;
+ Strong<ExecutableBase> executable;
Strong<UnlinkedCodeBlock> codeBlock;
RefPtr<SourceProvider> code;
int lineOffset;
@@ -90,28 +89,65 @@ namespace JSC {
unsigned bytecodeOffset;
String sourceURL;
JS_EXPORT_PRIVATE String toString(CallFrame*);
- String friendlySourceURL() const;
- String friendlyFunctionName(CallFrame*) const;
+ 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;
+ }
JS_EXPORT_PRIVATE void computeLineAndColumn(unsigned& line, unsigned& column);
private:
void expressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column);
};
- class SuspendExceptionScope {
+ class ClearExceptionScope {
public:
- SuspendExceptionScope(VM* vm)
- : m_vm(vm)
+ ClearExceptionScope(VM* vm): m_vm(vm)
{
- oldException = vm->exception();
+ vm->getExceptionInfo(oldException, oldExceptionStack);
vm->clearException();
}
- ~SuspendExceptionScope()
+ ~ClearExceptionScope()
{
- m_vm->setException(oldException);
+ m_vm->setExceptionInfo(oldException, oldExceptionStack);
}
private:
- Exception* oldException;
+ JSC::JSValue oldException;
+ RefCountedArray<JSC::StackFrame> oldExceptionStack;
VM* m_vm;
};
@@ -121,11 +157,13 @@ namespace JSC {
: vm(currentVM)
, oldCallFrame(currentVM.topCallFrame)
{
+ ASSERT(!callFrame->isVMEntrySentinel());
currentVM.topCallFrame = callFrame;
}
~TopCallFrameSetter()
{
+ ASSERT(!oldCallFrame->isVMEntrySentinel());
vm.topCallFrame = oldCallFrame;
}
private:
@@ -139,35 +177,11 @@ namespace JSC {
{
ASSERT(vm);
ASSERT(callFrame);
+ ASSERT(!callFrame->isVMEntrySentinel());
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;
@@ -176,6 +190,14 @@ 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();
@@ -196,7 +218,7 @@ namespace JSC {
OpcodeID getOpcodeID(Opcode opcode)
{
ASSERT(m_initialized);
-#if ENABLE(COMPUTED_GOTO_OPCODES)
+#if ENABLE(COMPUTED_GOTO_OPCODES) && ENABLE(LLINT)
ASSERT(isOpcode(opcode));
return m_opcodeIDTable.get(opcode);
#else
@@ -208,14 +230,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&, JSValue newTarget);
+ JSObject* executeConstruct(CallFrame*, JSObject* function, ConstructType, const ConstructData&, const ArgList&);
JSValue execute(EvalExecutable*, CallFrame*, JSValue thisValue, JSScope*);
void getArgumentsData(CallFrame*, JSFunction*&, ptrdiff_t& firstParameterIndex, Register*& argv, int& argc);
SamplingTool* sampler() { return m_sampler.get(); }
- NEVER_INLINE HandlerInfo* unwind(VMEntryFrame*&, CallFrame*&, Exception*);
+ bool isInErrorHandlingMode() { return m_errorHandlingModeReentry; }
+
+ NEVER_INLINE HandlerInfo* unwind(CallFrame*&, JSValue&);
NEVER_INLINE void debug(CallFrame*, DebugHookID);
JSString* stackTraceAsString(ExecState*, Vector<StackFrame>);
@@ -230,8 +254,6 @@ 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 };
@@ -239,7 +261,7 @@ namespace JSC {
JSValue execute(CallFrameClosure&);
-
+ void getStackTrace(Vector<StackFrame>& results, size_t maxStackSize = std::numeric_limits<size_t>::max());
void dumpRegisters(CallFrame*);
@@ -247,13 +269,13 @@ namespace JSC {
void enableSampler();
int m_sampleEntryDepth;
- std::unique_ptr<SamplingTool> m_sampler;
+ OwnPtr<SamplingTool> m_sampler;
VM& m_vm;
JSStack m_stack;
int m_errorHandlingModeReentry;
-#if ENABLE(COMPUTED_GOTO_OPCODES)
+#if ENABLE(COMPUTED_GOTO_OPCODES) && ENABLE(LLINT)
Opcode* m_opcodeTable; // Maps OpcodeID => Opcode for compiling
HashMap<Opcode, OpcodeID> m_opcodeIDTable; // Maps Opcode => OpcodeID for decompiling
#endif
@@ -264,22 +286,8 @@ namespace JSC {
};
JSValue eval(CallFrame*);
-
- inline CallFrame* calleeFrameForVarargs(CallFrame* callFrame, unsigned numUsedStackSlots, unsigned argumentCountIncludingThis)
- {
- 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);
-
+ CallFrame* sizeAndAllocFrameForVarargs(CallFrame*, JSStack*, JSValue, int);
+ void loadVarargs(CallFrame*, CallFrame*, JSValue, JSValue);
} // namespace JSC
#endif // Interpreter_h
diff --git a/Source/JavaScriptCore/interpreter/JSStack.cpp b/Source/JavaScriptCore/interpreter/JSStack.cpp
index d755aaa7d..722e1bc23 100644
--- a/Source/JavaScriptCore/interpreter/JSStack.cpp
+++ b/Source/JavaScriptCore/interpreter/JSStack.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2013, 2014, 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 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
@@ -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 Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -31,153 +31,121 @@
#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 StaticLock stackStatisticsMutex;
-#endif // !ENABLE(JIT)
+static Mutex& stackStatisticsMutex()
+{
+ DEFINE_STATIC_LOCAL(Mutex, staticMutex, ());
+ return staticMutex;
+}
-JSStack::JSStack(VM& vm)
+JSStack::JSStack(VM& vm, size_t capacity)
: m_vm(vm)
- , m_topCallFrame(vm.topCallFrame)
-#if !ENABLE(JIT)
, m_end(0)
- , m_reservedZoneSizeInRegisters(0)
-#endif
+ , m_topCallFrame(vm.topCallFrame)
{
-#if !ENABLE(JIT)
- size_t capacity = Options::maxPerThreadStackUsage();
ASSERT(capacity && isPageAligned(capacity));
- m_reservation = PageReservation::reserve(WTF::roundUpToMultipleOf(commitSize, capacity), OSAllocator::JSVMStackPages);
- setStackLimit(highAddress());
- m_commitTop = highAddress();
-
- m_lastStackTop = baseOfStack();
-#endif // !ENABLE(JIT)
+ m_reservation = PageReservation::reserve(roundUpAllocationSize(capacity * sizeof(Register), commitSize), OSAllocator::JSVMStackPages);
+ updateStackLimit(highAddress());
+ m_commitEnd = highAddress();
+
+ disableErrorStackReserve();
m_topCallFrame = 0;
}
-#if !ENABLE(JIT)
JSStack::~JSStack()
{
- ptrdiff_t sizeToDecommit = reinterpret_cast<char*>(highAddress()) - reinterpret_cast<char*>(m_commitTop);
- m_reservation.decommit(reinterpret_cast<void*>(m_commitTop), sizeToDecommit);
- addToCommittedByteCount(-sizeToDecommit);
+ 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)));
m_reservation.deallocate();
}
-bool JSStack::growSlowCase(Register* newTopOfStack)
+bool JSStack::growSlowCase(Register* newEnd)
{
- Register* newTopOfStackWithReservedZone = newTopOfStack - m_reservedZoneSizeInRegisters;
-
// If we have already committed enough memory to satisfy this request,
// just update the end pointer and return.
- if (newTopOfStackWithReservedZone >= m_commitTop) {
- setStackLimit(newTopOfStack);
+ if (newEnd >= m_commitEnd) {
+ updateStackLimit(newEnd);
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.
- 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())
+ 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))
return false;
- // Otherwise, the growth is still within our budget. Commit it and return true.
- m_reservation.commit(newCommitTop, delta);
+ // 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);
addToCommittedByteCount(delta);
- m_commitTop = newCommitTop;
- setStackLimit(newTopOfStack);
+ m_commitEnd = reinterpret_cast_ptr<Register*>(reinterpret_cast<char*>(m_commitEnd) - delta);
+ updateStackLimit(newEnd);
return true;
}
void JSStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots)
{
- conservativeRoots.add(topOfStack() + 1, highAddress());
+ conservativeRoots.add(getBaseOfStack(), getTopOfStack());
}
void JSStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks)
{
- conservativeRoots.add(topOfStack() + 1, highAddress(), jitStubRoutines, codeBlocks);
-}
-
-void JSStack::sanitizeStack()
-{
-#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
+ conservativeRoots.add(getBaseOfStack(), getTopOfStack(), jitStubRoutines, codeBlocks);
}
void JSStack::releaseExcessCapacity()
{
- Register* highAddressWithReservedZone = highAddress() - m_reservedZoneSizeInRegisters;
- ptrdiff_t delta = reinterpret_cast<char*>(highAddressWithReservedZone) - reinterpret_cast<char*>(m_commitTop);
- m_reservation.decommit(m_commitTop, delta);
+ ptrdiff_t delta = reinterpret_cast<uintptr_t>(highAddress()) - reinterpret_cast<uintptr_t>(m_commitEnd);
+ m_reservation.decommit(m_commitEnd, delta);
addToCommittedByteCount(-delta);
- m_commitTop = highAddressWithReservedZone;
+ m_commitEnd = highAddress();
}
-void JSStack::addToCommittedByteCount(long byteCount)
+void JSStack::initializeThreading()
{
- LockHolder locker(stackStatisticsMutex);
- ASSERT(static_cast<long>(committedBytesCount) + byteCount > -1);
- committedBytesCount += byteCount;
+ stackStatisticsMutex();
}
-void JSStack::setReservedZoneSize(size_t reservedZoneSize)
+size_t JSStack::committedByteCount()
{
- m_reservedZoneSizeInRegisters = reservedZoneSize / sizeof(Register);
- if (m_commitTop >= (m_end + 1) - m_reservedZoneSizeInRegisters)
- growSlowCase(m_end + 1);
+ MutexLocker locker(stackStatisticsMutex());
+ return committedBytesCount;
}
-#endif // !ENABLE(JIT)
-#if ENABLE(JIT)
-Register* JSStack::lowAddress() const
+void JSStack::addToCommittedByteCount(long byteCount)
{
- ASSERT(wtfThreadData().stack().isGrowingDownward());
- return reinterpret_cast<Register*>(m_vm.stackLimit());
+ MutexLocker locker(stackStatisticsMutex());
+ ASSERT(static_cast<long>(committedBytesCount) + byteCount > -1);
+ committedBytesCount += byteCount;
}
-Register* JSStack::highAddress() const
+void JSStack::enableErrorStackReserve()
{
- ASSERT(wtfThreadData().stack().isGrowingDownward());
- return reinterpret_cast<Register*>(wtfThreadData().stack().origin());
+ m_useableEnd = reservationEnd();
}
-#endif // ENABLE(JIT)
-size_t JSStack::committedByteCount()
+void JSStack::disableErrorStackReserve()
{
-#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
+ 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);
+ }
}
} // namespace JSC
diff --git a/Source/JavaScriptCore/interpreter/JSStack.h b/Source/JavaScriptCore/interpreter/JSStack.h
index 07cbbe69b..adf825d98 100644
--- a/Source/JavaScriptCore/interpreter/JSStack.h
+++ b/Source/JavaScriptCore/interpreter/JSStack.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2009, 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 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
@@ -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 Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -35,6 +35,11 @@
#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;
@@ -56,8 +61,8 @@ namespace JSC {
WTF_MAKE_NONCOPYABLE(JSStack);
public:
enum CallFrameHeaderEntry {
- CallerFrameAndPCSize = sizeof(CallerFrameAndPC) / sizeof(Register),
- CodeBlock = CallerFrameAndPCSize,
+ CodeBlock = sizeof(CallerFrameAndPC) / sizeof(Register),
+ ScopeChain,
Callee,
ArgumentCount,
CallFrameHeaderSize,
@@ -67,84 +72,90 @@ 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&);
-
- bool ensureCapacityFor(Register* newTopOfStack);
-
- bool containsAddress(Register* address) { return (lowAddress() <= address && address < highAddress()); }
- static size_t committedByteCount();
-
-#if ENABLE(JIT)
- void gatherConservativeRoots(ConservativeRoots&) { }
- void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&) { }
- void sanitizeStack() { }
-#else
+ JSStack(VM&, size_t capacity = defaultCapacity);
~JSStack();
-
+
void gatherConservativeRoots(ConservativeRoots&);
void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&);
- void sanitizeStack();
- Register* baseOfStack() const
+ Register* getBaseOfStack() const
{
return highAddress() - 1;
}
size_t size() const { return highAddress() - lowAddress(); }
- void setReservedZoneSize(size_t);
+ bool grow(Register*);
+
+ static size_t committedByteCount();
+ static void initializeThreading();
+
+ Register* getTopOfFrame(CallFrame*);
+ Register* getStartOfFrame(CallFrame*);
+ Register* getTopOfStack();
- inline Register* topOfStack();
-#endif // ENABLE(JIT)
+ bool entryCheck(class CodeBlock*, int);
- private:
+ CallFrame* pushFrame(class CodeBlock*, JSScope*, int argsCount, JSObject* callee);
-#if !ENABLE(JIT)
+ 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)
+
+ private:
Register* lowAddress() const
{
- return m_end + 1;
+ return m_end;
}
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)
-#if !ENABLE(JIT)
- inline Register* topOfFrameFor(CallFrame*);
-
- Register* reservationTop() const
+ Register* reservationEnd() const
{
- char* reservationTop = static_cast<char*>(m_reservation.base());
- return reinterpret_cast_ptr<Register*>(reservationTop);
+ char* reservationEnd = static_cast<char*>(m_reservation.base());
+ return reinterpret_cast_ptr<Register*>(reservationEnd);
}
- bool grow(Register* newTopOfStack);
- bool growSlowCase(Register* newTopOfStack);
- void shrink(Register* newTopOfStack);
+#if ENABLE(DEBUG_JSSTACK)
+ static JSValue generateFenceValue(size_t argIndex);
+ void installTrapsAfterFrame(CallFrame*);
+#else
+ void installTrapsAfterFrame(CallFrame*) { }
+#endif
+
+ bool growSlowCase(Register*);
+ void shrink(Register*);
void releaseExcessCapacity();
void addToCommittedByteCount(long);
- void setStackLimit(Register* newTopOfStack);
-#endif // !ENABLE(JIT)
+ void updateStackLimit(Register* newEnd);
VM& m_vm;
- CallFrame*& m_topCallFrame;
-#if !ENABLE(JIT)
Register* m_end;
- Register* m_commitTop;
+ Register* m_commitEnd;
+ Register* m_useableEnd;
PageReservation m_reservation;
- Register* m_lastStackTop;
- ptrdiff_t m_reservedZoneSizeInRegisters;
-#endif // !ENABLE(JIT)
+ CallFrame*& m_topCallFrame;
friend class LLIntOffsetsExtractor;
};
diff --git a/Source/JavaScriptCore/interpreter/JSStackInlines.h b/Source/JavaScriptCore/interpreter/JSStackInlines.h
index 69508ab5d..5a2aff117 100644
--- a/Source/JavaScriptCore/interpreter/JSStackInlines.h
+++ b/Source/JavaScriptCore/interpreter/JSStackInlines.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 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
@@ -33,61 +33,262 @@
namespace JSC {
-inline bool JSStack::ensureCapacityFor(Register* newTopOfStack)
+inline Register* JSStack::getTopOfFrame(CallFrame* frame)
{
-#if !ENABLE(JIT)
- return grow(newTopOfStack);
-#else
- ASSERT(wtfThreadData().stack().isGrowingDownward());
- return newTopOfStack >= m_vm.stackLimit();
-#endif
+ if (UNLIKELY(!frame))
+ return getBaseOfStack();
+ return frame->frameExtent();
}
-#if !ENABLE(JIT)
+inline Register* JSStack::getTopOfStack()
+{
+ return getTopOfFrame(m_topCallFrame);
+}
-inline Register* JSStack::topOfFrameFor(CallFrame* frame)
+inline Register* JSStack::getStartOfFrame(CallFrame* frame)
{
- if (UNLIKELY(!frame))
- return baseOfStack();
- return frame->topOfFrame() - 1;
+ CallFrame* callerFrame = frame->callerFrameSkippingVMEntrySentinel();
+ return getTopOfFrame(callerFrame);
}
-inline Register* JSStack::topOfStack()
+inline bool JSStack::entryCheck(class CodeBlock* codeBlock, int argsCount)
{
- return topOfFrameFor(m_topCallFrame);
+ 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 false;
+
+ return true;
}
-inline void JSStack::shrink(Register* newTopOfStack)
+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);
+
+ 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 void JSStack::popFrame(CallFrame* frame)
+{
+ 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);
+}
+
+inline void JSStack::shrink(Register* newEnd)
{
- Register* newEnd = newTopOfStack - 1;
if (newEnd >= m_end)
return;
- 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)
+ updateStackLimit(newEnd);
+ if (m_end == getBaseOfStack() && (m_commitEnd - getBaseOfStack()) >= maxExcessCapacity)
releaseExcessCapacity();
}
-inline bool JSStack::grow(Register* newTopOfStack)
+inline bool JSStack::grow(Register* newEnd)
{
- Register* newEnd = newTopOfStack - 1;
if (newEnd >= m_end)
return true;
- return growSlowCase(newTopOfStack);
+ return growSlowCase(newEnd);
}
-inline void JSStack::setStackLimit(Register* newTopOfStack)
+inline void JSStack::updateStackLimit(Register* newEnd)
{
- Register* newEnd = newTopOfStack - 1;
m_end = newEnd;
- m_vm.setJSStackLimit(newTopOfStack);
+#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;
}
-#endif // !ENABLE(JIT)
+// 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)
} // namespace JSC
diff --git a/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp b/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp
index eb80b2c23..a36001437 100644
--- a/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp
+++ b/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp
@@ -27,25 +27,23 @@
#include "ProtoCallFrame.h"
#include "CodeBlock.h"
-#include "JSCInlines.h"
-#include "StackAlignment.h"
namespace JSC {
-void ProtoCallFrame::init(CodeBlock* codeBlock, JSObject* callee, JSValue thisValue, int argCountIncludingThis, JSValue* otherArgs)
+void ProtoCallFrame::init(CodeBlock* codeBlock, JSScope* scope, JSObject* callee, JSValue thisValue, int argCountIncludingThis, JSValue* otherArgs)
{
this->args = otherArgs;
this->setCodeBlock(codeBlock);
+ this->setScope(scope);
this->setCallee(callee);
this->setArgumentCountIncludingThis(argCountIncludingThis);
- 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);
+ size_t paddedArgsCount = argCountIncludingThis;
+ if (codeBlock) {
+ size_t numParameters = codeBlock->numParameters();
+ if (paddedArgsCount < numParameters)
+ paddedArgsCount = numParameters;
+ }
+ this->setPaddedArgsCount(paddedArgsCount);
this->clearCurrentVPC();
this->setThisValue(thisValue);
}
diff --git a/Source/JavaScriptCore/interpreter/ProtoCallFrame.h b/Source/JavaScriptCore/interpreter/ProtoCallFrame.h
index af33a3072..b7151eeff 100644
--- a/Source/JavaScriptCore/interpreter/ProtoCallFrame.h
+++ b/Source/JavaScriptCore/interpreter/ProtoCallFrame.h
@@ -32,33 +32,34 @@ namespace JSC {
struct ProtoCallFrame {
Register codeBlockValue;
+ Register scopeChainValue;
Register calleeValue;
Register argCountAndCodeOriginValue;
Register thisArg;
- uint32_t paddedArgCount;
- bool arityMissMatch;
+ size_t paddedArgCount;
JSValue *args;
- void init(CodeBlock*, JSObject*, JSValue, int, JSValue* otherArgs = 0);
+ void init(CodeBlock*, JSScope*, JSObject*, JSValue, int, JSValue* otherArgs = 0);
CodeBlock* codeBlock() const { return codeBlockValue.Register::codeBlock(); }
void setCodeBlock(CodeBlock* codeBlock) { codeBlockValue = codeBlock; }
- JSObject* callee() const { return calleeValue.Register::object(); }
- void setCallee(JSObject* callee) { calleeValue = callee; }
+ 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); }
int argumentCountIncludingThis() const { return argCountAndCodeOriginValue.payload(); }
int argumentCount() const { return argumentCountIncludingThis() - 1; }
void setArgumentCountIncludingThis(int count) { argCountAndCodeOriginValue.payload() = count; }
- void setPaddedArgCount(uint32_t argCount) { paddedArgCount = argCount; }
+ void setPaddedArgsCount(size_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 71ead7d2f..13a7e5866 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 Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -37,8 +37,9 @@ namespace JSC {
class CodeBlock;
class ExecState;
- class JSLexicalEnvironment;
+ class JSActivation;
class JSObject;
+ class JSPropertyNameIterator;
class JSScope;
typedef ExecState CallFrame;
@@ -51,19 +52,18 @@ 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;
- JSLexicalEnvironment* lexicalEnvironment() const;
+ JSActivation* activation() const;
CallFrame* callFrame() const;
CodeBlock* codeBlock() const;
- JSObject* object() const;
+ JSObject* function() const;
+ JSPropertyNameIterator* propertyNameIterator() const;
JSScope* scope() const;
int32_t unboxedInt32() const;
int64_t unboxedInt52() const;
@@ -82,6 +82,8 @@ namespace JSC {
return r;
}
+ static Register withCallee(JSObject* callee);
+
private:
union {
EncodedJSValue value;
@@ -111,12 +113,6 @@ 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);
diff --git a/Source/JavaScriptCore/interpreter/StackVisitor.cpp b/Source/JavaScriptCore/interpreter/StackVisitor.cpp
index 6fe792b7e..d922e7f8f 100644
--- a/Source/JavaScriptCore/interpreter/StackVisitor.cpp
+++ b/Source/JavaScriptCore/interpreter/StackVisitor.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
+ * 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
@@ -26,11 +26,11 @@
#include "config.h"
#include "StackVisitor.h"
+#include "Arguments.h"
#include "CallFrameInlines.h"
-#include "ClonedArguments.h"
#include "Executable.h"
#include "Interpreter.h"
-#include "JSCInlines.h"
+#include "Operations.h"
#include <wtf/DataLog.h>
namespace JSC {
@@ -38,20 +38,7 @@ namespace JSC {
StackVisitor::StackVisitor(CallFrame* startFrame)
{
m_frame.m_index = 0;
- 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();
+ readFrame(startFrame);
}
void StackVisitor::gotoNextFrame()
@@ -61,15 +48,15 @@ void StackVisitor::gotoNextFrame()
InlineCallFrame* inlineCallFrame = m_frame.inlineCallFrame();
CodeOrigin* callerCodeOrigin = &inlineCallFrame->caller;
readInlinedFrame(m_frame.callFrame(), callerCodeOrigin);
- return;
- }
+
+ } else
#endif // ENABLE(DFG_JIT)
- m_frame.m_VMEntryFrame = m_frame.m_CallerVMEntryFrame;
- readFrame(m_frame.callerFrame());
+ readFrame(m_frame.callerFrame());
}
void StackVisitor::readFrame(CallFrame* callFrame)
{
+ ASSERT(!callFrame->isVMEntrySentinel());
if (!callFrame) {
m_frame.setToEnd();
return;
@@ -117,10 +104,9 @@ void StackVisitor::readNonInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOri
{
m_frame.m_callFrame = callFrame;
m_frame.m_argumentCountIncludingThis = callFrame->argumentCountIncludingThis();
- 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_callerFrame = callFrame->callerFrameSkippingVMEntrySentinel();
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
@@ -141,6 +127,7 @@ static int inlinedFrameOffset(CodeOrigin* codeOrigin)
void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin)
{
ASSERT(codeOrigin);
+ ASSERT(!callFrame->isVMEntrySentinel());
int frameOffset = inlinedFrameOffset(codeOrigin);
bool isInlined = !!frameOffset;
@@ -149,15 +136,14 @@ void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin
m_frame.m_callFrame = callFrame;
m_frame.m_inlineCallFrame = inlineCallFrame;
- 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_argumentCountIncludingThis = inlineCallFrame->arguments.size();
m_frame.m_codeBlock = inlineCallFrame->baselineCodeBlock();
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
@@ -196,7 +182,7 @@ String StackVisitor::Frame::functionName()
switch (codeType()) {
case CodeType::Eval:
- traceLine = ASCIILiteral("eval code");
+ traceLine = "eval code";
break;
case CodeType::Native:
if (callee)
@@ -206,7 +192,7 @@ String StackVisitor::Frame::functionName()
traceLine = getCalculatedDisplayName(callFrame(), callee).impl();
break;
case CodeType::Global:
- traceLine = ASCIILiteral("global code");
+ traceLine = "global code";
break;
}
return traceLine.isNull() ? emptyString() : traceLine;
@@ -226,7 +212,7 @@ String StackVisitor::Frame::sourceURL()
break;
}
case CodeType::Native:
- traceLine = ASCIILiteral("[native code]");
+ traceLine = "[native code]";
break;
}
return traceLine.isNull() ? emptyString() : traceLine;
@@ -255,26 +241,48 @@ String StackVisitor::Frame::toString()
return traceBuild.toString().impl();
}
-ClonedArguments* StackVisitor::Frame::createArguments()
+Arguments* StackVisitor::Frame::createArguments()
{
ASSERT(m_callFrame);
CallFrame* physicalFrame = m_callFrame;
- ClonedArguments* arguments;
- ArgumentsMode mode;
- if (Options::enableFunctionDotArguments())
- mode = ArgumentsMode::Cloned;
- else
- mode = ArgumentsMode::FakeValues;
+ VM& vm = physicalFrame->vm();
+ Arguments* arguments;
#if ENABLE(DFG_JIT)
if (isInlinedFrame()) {
ASSERT(m_inlineCallFrame);
- arguments = ClonedArguments::createWithInlineFrame(physicalFrame, physicalFrame, m_inlineCallFrame, mode);
+ arguments = Arguments::create(vm, physicalFrame, m_inlineCallFrame);
+ arguments->tearOff(physicalFrame, m_inlineCallFrame);
} else
#endif
- arguments = ClonedArguments::createWithMachineFrame(physicalFrame, physicalFrame, mode);
+ {
+ arguments = Arguments::create(vm, physicalFrame);
+ arguments->tearOff(physicalFrame);
+ }
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();
@@ -291,11 +299,8 @@ void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column)
unsigned divotColumn = 0;
retrieveExpressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn);
- line = divotLine + codeBlock->ownerExecutable()->firstLine();
+ line = divotLine + codeBlock->ownerExecutable()->lineNo();
column = divotColumn + (divotLine ? 1 : codeBlock->firstLineColumnOffset());
-
- if (codeBlock->ownerExecutable()->hasOverrideLineNumber())
- line = codeBlock->ownerExecutable()->overrideLineNumber();
}
void StackVisitor::Frame::retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column)
@@ -313,106 +318,157 @@ 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(" ");
}
-template<typename... Types>
-void log(unsigned indent, const Types&... values)
+static void printif(int indentLevels, const char* format, ...)
{
- printIndents(indent);
- dataLog(values...);
-}
+ va_list argList;
+ va_start(argList, format);
-template<typename... Types>
-void logF(unsigned indent, const char* format, const Types&... values)
-{
- printIndents(indent);
+ if (indentLevels)
+ printIndents(indentLevels);
-#if COMPILER(GCC_OR_CLANG)
+#if COMPILER(CLANG) || (COMPILER(GCC) && GCC_VERSION_AT_LEAST(4, 6, 0))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
#pragma GCC diagnostic ignored "-Wmissing-format-attribute"
#endif
- dataLogF(format, values...);
+ WTF::dataLogFV(format, argList);
-#if COMPILER(GCC_OR_CLANG)
+#if COMPILER(CLANG) || (COMPILER(GCC) && GCC_VERSION_AT_LEAST(4, 6, 0))
#pragma GCC diagnostic pop
#endif
+
+ va_end(argList);
}
-void StackVisitor::Frame::print(int indent)
+void StackVisitor::Frame::print(int indentLevel)
{
+ int i = indentLevel;
+
if (!this->callFrame()) {
- log(indent, "frame 0x0\n");
+ printif(i, "frame 0x0\n");
return;
}
CodeBlock* codeBlock = this->codeBlock();
- logF(indent, "frame %p {\n", this->callFrame());
+ printif(i, "frame %p {\n", this->callFrame());
- {
- indent++;
-
- CallFrame* callFrame = m_callFrame;
- CallFrame* callerFrame = this->callerFrame();
- void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr;
+ CallFrame* callFrame = m_callFrame;
+ CallFrame* callerFrame = this->callerFrame();
+ void* returnPC = callFrame->hasReturnPC() ? callFrame->returnPC().value() : nullptr;
- log(indent, "name: ", functionName(), "\n");
- log(indent, "sourceURL: ", sourceURL(), "\n");
+ printif(i, " name '%s'\n", functionName().utf8().data());
+ printif(i, " sourceURL '%s'\n", sourceURL().utf8().data());
+ printif(i, " isVMEntrySentinel %d\n", callerFrame->isVMEntrySentinel());
- bool isInlined = false;
#if ENABLE(DFG_JIT)
- isInlined = isInlinedFrame();
- log(indent, "isInlinedFrame: ", isInlinedFrame(), "\n");
- if (isInlinedFrame())
- logF(indent, "InlineCallFrame: %p\n", m_inlineCallFrame);
+ printif(i, " isInlinedFrame %d\n", isInlinedFrame());
+ if (isInlinedFrame())
+ printif(i, " InlineCallFrame %p\n", m_inlineCallFrame);
#endif
- logF(indent, "callee: %p\n", callee());
- logF(indent, "returnPC: %p\n", returnPC);
- logF(indent, "callerFrame: %p\n", callerFrame);
- unsigned locationRawBits = callFrame->locationAsRawBits();
- 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->hasLocationAsBytecodeOffset()) {
- unsigned bytecodeOffset = callFrame->locationAsBytecodeOffset();
- log(indent, "bytecodeOffset: ", bytecodeOffset, " of ", codeBlock->instructions().size(), "\n");
+ 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());
#if ENABLE(DFG_JIT)
- } else {
- log(indent, "hasCodeOrigins: ", codeBlock->hasCodeOrigins(), "\n");
- if (codeBlock->hasCodeOrigins()) {
- unsigned codeOriginIndex = callFrame->locationAsCodeOriginIndex();
- log(indent, "codeOriginIndex: ", codeOriginIndex, " 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());
- }
- }
+ } else {
+ unsigned codeOriginIndex = callFrame->locationAsCodeOriginIndex();
+ printif(i, " codeOriginIdex %u %p / %zu\n", codeOriginIndex, reinterpret_cast<void*>(codeOriginIndex), codeBlock->codeOrigins().size());
#endif
- }
- unsigned line = 0;
- unsigned column = 0;
- computeLineAndColumn(line, column);
- log(indent, "line: ", line, "\n");
- log(indent, "column: ", column, "\n");
-
- indent--;
}
- 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
}
- log(indent, "}\n");
+ printif(i, "}\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 0036a789c..990a226b3 100644
--- a/Source/JavaScriptCore/interpreter/StackVisitor.h
+++ b/Source/JavaScriptCore/interpreter/StackVisitor.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
+ * 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
@@ -26,7 +26,6 @@
#ifndef StackVisitor_h
#define StackVisitor_h
-#include "VMEntryRecord.h"
#include <wtf/text/WTFString.h>
namespace JSC {
@@ -34,12 +33,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;
@@ -57,9 +56,9 @@ public:
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)
@@ -78,11 +77,13 @@ public:
CodeType codeType() const;
JS_EXPORT_PRIVATE void computeLineAndColumn(unsigned& line, unsigned& column);
- ClonedArguments* createArguments();
- VMEntryFrame* vmEntryFrame() const { return m_VMEntryFrame; }
+ Arguments* createArguments();
+ Arguments* existingArguments();
CallFrame* callFrame() const { return m_callFrame; }
+#ifndef NDEBUG
JS_EXPORT_PRIVATE void print(int indentLevel);
+#endif
private:
Frame() { }
@@ -93,13 +94,11 @@ 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
@@ -145,32 +144,6 @@ 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
deleted file mode 100644
index 50ac65f78..000000000
--- a/Source/JavaScriptCore/interpreter/VMEntryRecord.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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; }
-
- VMEntryFrame* prevTopVMEntryFrame() { 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
new file mode 100644
index 000000000..fbb49413d
--- /dev/null
+++ b/Source/JavaScriptCore/interpreter/VMInspector.cpp
@@ -0,0 +1,572 @@
+/*
+ * 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
new file mode 100644
index 000000000..6623068dc
--- /dev/null
+++ b/Source/JavaScriptCore/interpreter/VMInspector.h
@@ -0,0 +1,89 @@
+/*
+ * 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