summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/interpreter
diff options
context:
space:
mode:
authorOswald Buddenhagen <oswald.buddenhagen@qt.io>2017-05-30 12:48:17 +0200
committerOswald Buddenhagen <oswald.buddenhagen@qt.io>2017-05-30 12:48:17 +0200
commit881da28418d380042aa95a97f0cbd42560a64f7c (patch)
treea794dff3274695e99c651902dde93d934ea7a5af /Source/JavaScriptCore/interpreter
parent7e104c57a70fdf551bb3d22a5d637cdcbc69dbea (diff)
parent0fcedcd17cc00d3dd44c718b3cb36c1033319671 (diff)
downloadqtwebkit-881da28418d380042aa95a97f0cbd42560a64f7c.tar.gz
Merge 'wip/next' into dev
Change-Id: Iff9ee5e23bb326c4371ec8ed81d56f2f05d680e9
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.h36
-rw-r--r--Source/JavaScriptCore/interpreter/CallFrame.cpp346
-rw-r--r--Source/JavaScriptCore/interpreter/CallFrame.h311
-rw-r--r--Source/JavaScriptCore/interpreter/CallFrameClosure.h21
-rw-r--r--Source/JavaScriptCore/interpreter/Interpreter.cpp1414
-rw-r--r--Source/JavaScriptCore/interpreter/Interpreter.h193
-rw-r--r--Source/JavaScriptCore/interpreter/JSStack.cpp149
-rw-r--r--Source/JavaScriptCore/interpreter/JSStack.h149
-rw-r--r--Source/JavaScriptCore/interpreter/JSStackInlines.h213
-rw-r--r--Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp53
-rw-r--r--Source/JavaScriptCore/interpreter/ProtoCallFrame.h76
-rw-r--r--Source/JavaScriptCore/interpreter/Register.h76
-rw-r--r--Source/JavaScriptCore/interpreter/StackVisitor.cpp440
-rw-r--r--Source/JavaScriptCore/interpreter/StackVisitor.h179
-rw-r--r--Source/JavaScriptCore/interpreter/VMEntryRecord.h56
-rw-r--r--Source/JavaScriptCore/interpreter/VMInspector.cpp573
-rw-r--r--Source/JavaScriptCore/interpreter/VMInspector.h89
19 files changed, 2205 insertions, 2174 deletions
diff --git a/Source/JavaScriptCore/interpreter/AbstractPC.cpp b/Source/JavaScriptCore/interpreter/AbstractPC.cpp
index 8600b7228..09b24d5dc 100644
--- a/Source/JavaScriptCore/interpreter/AbstractPC.cpp
+++ b/Source/JavaScriptCore/interpreter/AbstractPC.cpp
@@ -27,9 +27,9 @@
#include "AbstractPC.h"
#include "CallFrame.h"
-#include "VM.h"
#include "JSObject.h"
-
+#include "JSCInlines.h"
+#include "VM.h"
namespace JSC {
diff --git a/Source/JavaScriptCore/interpreter/AbstractPC.h b/Source/JavaScriptCore/interpreter/AbstractPC.h
index c30027d9e..ac2aaa7ad 100644
--- a/Source/JavaScriptCore/interpreter/AbstractPC.h
+++ b/Source/JavaScriptCore/interpreter/AbstractPC.h
@@ -27,7 +27,6 @@
#define AbstractPC_h
#include "MacroAssemblerCodeRef.h"
-#include <wtf/Platform.h>
namespace JSC {
diff --git a/Source/JavaScriptCore/interpreter/CachedCall.h b/Source/JavaScriptCore/interpreter/CachedCall.h
index ddf9a40ad..30cad2a14 100644
--- a/Source/JavaScriptCore/interpreter/CachedCall.h
+++ b/Source/JavaScriptCore/interpreter/CachedCall.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 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
@@ -27,9 +27,12 @@
#define CachedCall_h
#include "CallFrameClosure.h"
+#include "ExceptionHelpers.h"
#include "JSFunction.h"
#include "JSGlobalObject.h"
#include "Interpreter.h"
+#include "ProtoCallFrame.h"
+#include "VMEntryScope.h"
namespace JSC {
class CachedCall {
@@ -38,10 +41,14 @@ namespace JSC {
CachedCall(CallFrame* callFrame, JSFunction* function, int argumentCount)
: m_valid(false)
, m_interpreter(callFrame->interpreter())
- , m_globalObjectScope(callFrame->vm(), function->scope()->globalObject())
+ , m_entryScope(callFrame->vm(), function->scope()->globalObject())
{
- ASSERT(!function->isHostFunction());
- m_closure = m_interpreter->prepareForRepeatCall(function->jsExecutable(), callFrame, function, argumentCount + 1, function->scope());
+ ASSERT(!function->isHostFunctionNonInline());
+ if (callFrame->vm().isSafeToRecurse()) {
+ m_arguments.resize(argumentCount);
+ m_closure = m_interpreter->prepareForRepeatCall(function->jsExecutable(), callFrame, &m_protoCallFrame, function, argumentCount + 1, function->scope(), m_arguments.data());
+ } else
+ throwStackOverflowError(callFrame);
m_valid = !callFrame->hadException();
}
@@ -50,26 +57,15 @@ namespace JSC {
ASSERT(m_valid);
return m_interpreter->execute(m_closure);
}
- void setThis(JSValue v) { m_closure.setThis(v); }
- void setArgument(int n, JSValue v) { m_closure.setArgument(n, v); }
+ void setThis(JSValue v) { m_protoCallFrame.setThisValue(v); }
+ void setArgument(int n, JSValue v) { m_protoCallFrame.setArgument(n, v); }
- CallFrame* newCallFrame(ExecState* exec)
- {
- CallFrame* callFrame = m_closure.newCallFrame;
- callFrame->setScope(exec->scope());
- return callFrame;
- }
-
- ~CachedCall()
- {
- if (m_valid)
- m_interpreter->endRepeatCall(m_closure);
- }
-
private:
bool m_valid;
Interpreter* m_interpreter;
- DynamicGlobalObjectScope m_globalObjectScope;
+ VMEntryScope m_entryScope;
+ ProtoCallFrame m_protoCallFrame;
+ Vector<JSValue> m_arguments;
CallFrameClosure m_closure;
};
}
diff --git a/Source/JavaScriptCore/interpreter/CallFrame.cpp b/Source/JavaScriptCore/interpreter/CallFrame.cpp
index bb61020ce..3d3897b6b 100644
--- a/Source/JavaScriptCore/interpreter/CallFrame.cpp
+++ b/Source/JavaScriptCore/interpreter/CallFrame.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2013 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008, 2013, 2014 Apple Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -27,23 +27,74 @@
#include "CallFrame.h"
#include "CodeBlock.h"
+#include "InlineCallFrame.h"
#include "Interpreter.h"
-#include "Operations.h"
+#include "JSLexicalEnvironment.h"
+#include "JSCInlines.h"
+#include "VMEntryScope.h"
+#include <wtf/StringPrintStream.h>
namespace JSC {
-#ifndef NDEBUG
-void CallFrame::dumpCaller()
+bool CallFrame::callSiteBitsAreBytecodeOffset() const
{
- int signedLineNumber;
- intptr_t sourceID;
- String urlString;
- JSValue function;
-
- interpreter()->retrieveLastCaller(this, signedLineNumber, sourceID, urlString, function);
- dataLogF("Callpoint => %s:%d\n", urlString.utf8().data(), signedLineNumber);
+ ASSERT(codeBlock());
+ switch (codeBlock()->jitType()) {
+ case JITCode::InterpreterThunk:
+ case JITCode::BaselineJIT:
+ return true;
+ case JITCode::None:
+ case JITCode::HostCallThunk:
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+ default:
+ return false;
+ }
+
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+}
+
+bool CallFrame::callSiteBitsAreCodeOriginIndex() const
+{
+ ASSERT(codeBlock());
+ switch (codeBlock()->jitType()) {
+ case JITCode::DFGJIT:
+ case JITCode::FTLJIT:
+ return true;
+ case JITCode::None:
+ case JITCode::HostCallThunk:
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+ default:
+ return false;
+ }
+
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+}
+
+unsigned CallFrame::callSiteAsRawBits() const
+{
+ return this[JSStack::ArgumentCount].tag();
+}
+
+SUPPRESS_ASAN unsigned CallFrame::unsafeCallSiteAsRawBits() const
+{
+ return this[JSStack::ArgumentCount].unsafeTag();
+}
+
+CallSiteIndex CallFrame::callSiteIndex() const
+{
+ return CallSiteIndex(callSiteAsRawBits());
+}
+
+SUPPRESS_ASAN CallSiteIndex CallFrame::unsafeCallSiteIndex() const
+{
+ return CallSiteIndex(unsafeCallSiteAsRawBits());
}
+#ifndef NDEBUG
JSStack* CallFrame::stack()
{
return &interpreter()->stack();
@@ -52,170 +103,175 @@ JSStack* CallFrame::stack()
#endif
#if USE(JSVALUE32_64)
-unsigned CallFrame::bytecodeOffsetForNonDFGCode() const
+Instruction* CallFrame::currentVPC() const
{
- ASSERT(codeBlock());
- return currentVPC() - codeBlock()->instructions().begin();
+ return bitwise_cast<Instruction*>(callSiteIndex().bits());
}
-void CallFrame::setBytecodeOffsetForNonDFGCode(unsigned offset)
+void CallFrame::setCurrentVPC(Instruction* vpc)
+{
+ CallSiteIndex callSite(vpc);
+ this[JSStack::ArgumentCount].tag() = callSite.bits();
+}
+
+unsigned CallFrame::callSiteBitsAsBytecodeOffset() const
{
ASSERT(codeBlock());
- setCurrentVPC(codeBlock()->instructions().begin() + offset);
+ ASSERT(callSiteBitsAreBytecodeOffset());
+ return currentVPC() - codeBlock()->instructions().begin();
}
-#else
+
+#else // USE(JSVALUE32_64)
Instruction* CallFrame::currentVPC() const
{
- return codeBlock()->instructions().begin() + bytecodeOffsetForNonDFGCode();
+ ASSERT(callSiteBitsAreBytecodeOffset());
+ return codeBlock()->instructions().begin() + callSiteBitsAsBytecodeOffset();
}
+
void CallFrame::setCurrentVPC(Instruction* vpc)
{
- setBytecodeOffsetForNonDFGCode(vpc - codeBlock()->instructions().begin());
+ CallSiteIndex callSite(vpc - codeBlock()->instructions().begin());
+ this[JSStack::ArgumentCount].tag() = static_cast<int32_t>(callSite.bits());
}
-#endif
-
-#if ENABLE(DFG_JIT)
-bool CallFrame::isInlineCallFrameSlow()
+
+unsigned CallFrame::callSiteBitsAsBytecodeOffset() const
{
- if (!callee())
- return false;
- JSCell* calleeAsFunctionCell = getJSFunction(callee());
- if (!calleeAsFunctionCell)
- return false;
- JSFunction* calleeAsFunction = jsCast<JSFunction*>(calleeAsFunctionCell);
- return calleeAsFunction->executable() != codeBlock()->ownerExecutable();
+ ASSERT(codeBlock());
+ ASSERT(callSiteBitsAreBytecodeOffset());
+ return callSiteIndex().bits();
}
-CallFrame* CallFrame::trueCallFrame(AbstractPC pc)
-{
- // Am I an inline call frame? If so, we're done.
- if (isInlineCallFrame())
- return this;
-
- // If I don't have a code block, then I'm not DFG code, so I'm the true call frame.
- CodeBlock* machineCodeBlock = codeBlock();
- if (!machineCodeBlock)
- return this;
-
- // If the code block does not have any code origins, then there was no inlining, so
- // I'm done.
- if (!machineCodeBlock->hasCodeOrigins())
- return this;
-
- // At this point the PC must be due either to the DFG, or it must be unset.
- ASSERT(pc.hasJITReturnAddress() || !pc);
+#endif
- // Try to determine the CodeOrigin. If we don't have a pc set then the only way
- // that this makes sense is if the CodeOrigin index was set in the call frame.
- // FIXME: Note that you will see "Not currently in inlined code" comments below.
- // Currently, we do not record code origins for code that is not inlined, because
- // the only thing that we use code origins for is determining the inline stack.
- // But in the future, we'll want to use this same functionality (having a code
- // origin mapping for any calls out of JIT code) to determine the PC at any point
- // in the stack even if not in inlined code. When that happens, the code below
- // will have to change the way it detects the presence of inlining: it will always
- // get a code origin, but sometimes, that code origin will not have an inline call
- // frame. In that case, this method should bail and return this.
- CodeOrigin codeOrigin;
- if (pc.isSet()) {
- ReturnAddressPtr currentReturnPC = pc.jitReturnAddress();
-
- bool hasCodeOrigin = machineCodeBlock->codeOriginForReturn(currentReturnPC, codeOrigin);
- ASSERT(hasCodeOrigin);
- if (!hasCodeOrigin) {
- // In release builds, if we find ourselves in a situation where the return PC doesn't
- // correspond to a valid CodeOrigin, we return zero instead of continuing. Some of
- // the callers of trueCallFrame() will be able to recover and do conservative things,
- // while others will crash.
- return 0;
- }
- } else {
- unsigned index = codeOriginIndexForDFG();
- ASSERT(machineCodeBlock->canGetCodeOrigin(index));
- if (!machineCodeBlock->canGetCodeOrigin(index)) {
- // See above. In release builds, we try to protect ourselves from crashing even
- // though stack walking will be goofed up.
- return 0;
+unsigned CallFrame::bytecodeOffset()
+{
+ if (!codeBlock())
+ return 0;
+#if ENABLE(DFG_JIT)
+ if (callSiteBitsAreCodeOriginIndex()) {
+ ASSERT(codeBlock());
+ CodeOrigin codeOrigin = this->codeOrigin();
+ for (InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame; inlineCallFrame;) {
+ codeOrigin = inlineCallFrame->directCaller;
+ inlineCallFrame = codeOrigin.inlineCallFrame;
}
- codeOrigin = machineCodeBlock->codeOrigin(index);
+ return codeOrigin.bytecodeIndex;
}
+#endif
+ ASSERT(callSiteBitsAreBytecodeOffset());
+ return callSiteBitsAsBytecodeOffset();
+}
- if (!codeOrigin.inlineCallFrame)
- return this; // Not currently in inlined code.
-
- for (InlineCallFrame* inlineCallFrame = codeOrigin.inlineCallFrame; inlineCallFrame;) {
- InlineCallFrame* nextInlineCallFrame = inlineCallFrame->caller.inlineCallFrame;
-
- CallFrame* inlinedCaller = this + inlineCallFrame->stackOffset;
-
- JSFunction* calleeAsFunction = inlineCallFrame->callee.get();
-
- // Fill in the inlinedCaller
- inlinedCaller->setCodeBlock(machineCodeBlock);
- if (calleeAsFunction)
- inlinedCaller->setScope(calleeAsFunction->scope());
- if (nextInlineCallFrame)
- inlinedCaller->setCallerFrame(this + nextInlineCallFrame->stackOffset);
- else
- inlinedCaller->setCallerFrame(this);
-
- inlinedCaller->setInlineCallFrame(inlineCallFrame);
- inlinedCaller->setArgumentCountIncludingThis(inlineCallFrame->arguments.size());
- if (calleeAsFunction)
- inlinedCaller->setCallee(calleeAsFunction);
-
- inlineCallFrame = nextInlineCallFrame;
+CodeOrigin CallFrame::codeOrigin()
+{
+ if (!codeBlock())
+ return CodeOrigin(0);
+#if ENABLE(DFG_JIT)
+ if (callSiteBitsAreCodeOriginIndex()) {
+ CallSiteIndex index = callSiteIndex();
+ ASSERT(codeBlock()->canGetCodeOrigin(index));
+ return codeBlock()->codeOrigin(index);
}
-
- return this + codeOrigin.inlineCallFrame->stackOffset;
+#endif
+ return CodeOrigin(callSiteBitsAsBytecodeOffset());
}
-
-CallFrame* CallFrame::trueCallerFrame()
+
+Register* CallFrame::topOfFrameInternal()
{
- if (!codeBlock())
- return callerFrame()->removeHostCallFrameFlag();
-
- // this -> The callee; this is either an inlined callee in which case it already has
- // a pointer to the true caller. Otherwise it contains current PC in the machine
- // caller.
- //
- // machineCaller -> The caller according to the machine, which may be zero or
- // more frames above the true caller due to inlining.
-
- // Am I an inline call frame? If so, we're done.
- if (isInlineCallFrame())
- return callerFrame()->removeHostCallFrameFlag();
-
- // I am a machine call frame, so the question is: is my caller a machine call frame
- // that has inlines or a machine call frame that doesn't?
- CallFrame* machineCaller = callerFrame()->removeHostCallFrameFlag();
- if (!machineCaller)
- return 0;
- ASSERT(!machineCaller->isInlineCallFrame());
-
- // Figure out how we want to get the current code location.
- if (!hasReturnPC() || returnAddressIsInCtiTrampoline(returnPC()))
- return machineCaller->trueCallFrameFromVMCode()->removeHostCallFrameFlag();
-
- return machineCaller->trueCallFrame(returnPC())->removeHostCallFrameFlag();
+ CodeBlock* codeBlock = this->codeBlock();
+ ASSERT(codeBlock);
+ return registers() + codeBlock->stackPointerOffset();
}
-CodeBlock* CallFrame::someCodeBlockForPossiblyInlinedCode()
+JSGlobalObject* CallFrame::vmEntryGlobalObject()
{
- if (!isInlineCallFrame())
- return codeBlock();
-
- return jsCast<FunctionExecutable*>(inlineCallFrame()->executable.get())->baselineCodeBlockFor(
- inlineCallFrame()->isCall ? CodeForCall : CodeForConstruct);
+ if (this == lexicalGlobalObject()->globalExec())
+ return lexicalGlobalObject();
+
+ // For any ExecState that's not a globalExec, the
+ // dynamic global object must be set since code is running
+ ASSERT(vm().entryScope);
+ return vm().entryScope->globalObject();
}
-#endif
+CallFrame* CallFrame::callerFrame(VMEntryFrame*& currVMEntryFrame)
+{
+ if (callerFrameOrVMEntryFrame() == currVMEntryFrame) {
+ VMEntryRecord* currVMEntryRecord = vmEntryRecord(currVMEntryFrame);
+ currVMEntryFrame = currVMEntryRecord->prevTopVMEntryFrame();
+ return currVMEntryRecord->prevTopCallFrame();
+ }
+ return static_cast<CallFrame*>(callerFrameOrVMEntryFrame());
+}
-Register* CallFrame::frameExtentInternal()
+SUPPRESS_ASAN CallFrame* CallFrame::unsafeCallerFrame(VMEntryFrame*& currVMEntryFrame)
+{
+ if (unsafeCallerFrameOrVMEntryFrame() == currVMEntryFrame) {
+ VMEntryRecord* currVMEntryRecord = vmEntryRecord(currVMEntryFrame);
+ currVMEntryFrame = currVMEntryRecord->unsafePrevTopVMEntryFrame();
+ return currVMEntryRecord->unsafePrevTopCallFrame();
+ }
+ return static_cast<CallFrame*>(unsafeCallerFrameOrVMEntryFrame());
+}
+
+String CallFrame::friendlyFunctionName()
{
CodeBlock* codeBlock = this->codeBlock();
- ASSERT(codeBlock);
- return registers() + codeBlock->m_numCalleeRegisters;
+ if (!codeBlock)
+ return emptyString();
+
+ switch (codeBlock->codeType()) {
+ case EvalCode:
+ return ASCIILiteral("eval code");
+ case ModuleCode:
+ return ASCIILiteral("module code");
+ case GlobalCode:
+ return ASCIILiteral("global code");
+ case FunctionCode:
+ if (callee())
+ return getCalculatedDisplayName(this, callee());
+ return emptyString();
+ }
+
+ ASSERT_NOT_REACHED();
+ return emptyString();
+}
+
+void CallFrame::dump(PrintStream& out)
+{
+ if (CodeBlock* codeBlock = this->codeBlock()) {
+ out.print(codeBlock->inferredName(), "#", codeBlock->hashAsStringIfPossible(), " [", codeBlock->jitType(), "]");
+
+ out.print("(");
+ thisValue().dumpForBacktrace(out);
+
+ for (size_t i = 0; i < argumentCount(); ++i) {
+ out.print(", ");
+ JSValue value = argument(i);
+ value.dumpForBacktrace(out);
+ }
+
+ out.print(")");
+
+ return;
+ }
+
+ out.print(returnPC());
}
+const char* CallFrame::describeFrame()
+{
+ const size_t bufferSize = 200;
+ static char buffer[bufferSize + 1];
+
+ WTF::StringPrintStream stringStream;
+
+ dump(stringStream);
+
+ strncpy(buffer, stringStream.toCString().data(), bufferSize);
+ buffer[bufferSize] = '\0';
+
+ return buffer;
}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/interpreter/CallFrame.h b/Source/JavaScriptCore/interpreter/CallFrame.h
index c09132c57..980b92fb2 100644
--- a/Source/JavaScriptCore/interpreter/CallFrame.h
+++ b/Source/JavaScriptCore/interpreter/CallFrame.h
@@ -1,7 +1,7 @@
/*
* Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
* Copyright (C) 2001 Peter Kelly (pmk@post.com)
- * Copyright (C) 2003, 2007, 2008, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2003, 2007, 2008, 2011, 2013-2015 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -24,36 +24,62 @@
#define CallFrame_h
#include "AbstractPC.h"
-#include "VM.h"
#include "JSStack.h"
#include "MacroAssemblerCodeRef.h"
#include "Register.h"
+#include "StackVisitor.h"
+#include "VM.h"
+#include "VMEntryRecord.h"
namespace JSC {
class Arguments;
- class JSActivation;
class Interpreter;
class JSScope;
+ struct CallSiteIndex {
+ CallSiteIndex()
+ : m_bits(UINT_MAX)
+ {
+ }
+
+ explicit CallSiteIndex(uint32_t bits)
+ : m_bits(bits)
+ { }
+#if USE(JSVALUE32_64)
+ explicit CallSiteIndex(Instruction* instruction)
+ : m_bits(bitwise_cast<uint32_t>(instruction))
+ { }
+#endif
+
+ explicit operator bool() const { return m_bits != UINT_MAX; }
+
+ inline uint32_t bits() const { return m_bits; }
+
+ private:
+ uint32_t m_bits;
+ };
+
// Represents the current state of script execution.
// Passed as the first argument to most functions.
class ExecState : private Register {
public:
JSValue calleeAsValue() const { return this[JSStack::Callee].jsValue(); }
- JSObject* callee() const { return this[JSStack::Callee].function(); }
+ JSObject* callee() const { return this[JSStack::Callee].object(); }
+ SUPPRESS_ASAN JSValue unsafeCallee() const { return this[JSStack::Callee].asanUnsafeJSValue(); }
CodeBlock* codeBlock() const { return this[JSStack::CodeBlock].Register::codeBlock(); }
- JSScope* scope() const
+ SUPPRESS_ASAN CodeBlock* unsafeCodeBlock() const { return this[JSStack::CodeBlock].Register::asanUnsafeCodeBlock(); }
+ JSScope* scope(int scopeRegisterOffset) const
{
- ASSERT(this[JSStack::ScopeChain].Register::scope());
- return this[JSStack::ScopeChain].Register::scope();
+ ASSERT(this[scopeRegisterOffset].Register::scope());
+ return this[scopeRegisterOffset].Register::scope();
}
// Global object in which execution began.
- JSGlobalObject* dynamicGlobalObject();
+ JS_EXPORT_PRIVATE JSGlobalObject* vmEntryGlobalObject();
// Global object in which the currently executing code was defined.
- // Differs from dynamicGlobalObject() during function calls across web browser frames.
+ // Differs from vmEntryGlobalObject() during function calls across web browser frames.
JSGlobalObject* lexicalGlobalObject() const;
// Differs from lexicalGlobalObject because this will have DOM window shell rather than
@@ -67,132 +93,101 @@ namespace JSC {
// pointer, so these are inefficient, and should be used sparingly in new code.
// But they're used in many places in legacy code, so they're not going away any time soon.
- void clearException() { vm().exception = JSValue(); }
- void clearSupplementaryExceptionInfo()
- {
- vm().clearExceptionStack();
- }
+ void clearException() { vm().clearException(); }
+
+ Exception* exception() const { return vm().exception(); }
+ bool hadException() const { return !!vm().exception(); }
- JSValue exception() const { return vm().exception; }
- bool hadException() const { return vm().exception; }
+ Exception* lastException() const { return vm().lastException(); }
+ void clearLastException() { vm().clearLastException(); }
+ AtomicStringTable* atomicStringTable() const { return vm().atomicStringTable(); }
const CommonIdentifiers& propertyNames() const { return *vm().propertyNames; }
const MarkedArgumentBuffer& emptyList() const { return *vm().emptyList; }
Interpreter* interpreter() { return vm().interpreter; }
Heap* heap() { return &vm().heap; }
-#ifndef NDEBUG
- void dumpCaller();
-#endif
- static const HashTable* arrayConstructorTable(CallFrame* callFrame) { return callFrame->vm().arrayConstructorTable; }
- static const HashTable* arrayPrototypeTable(CallFrame* callFrame) { return callFrame->vm().arrayPrototypeTable; }
- static const HashTable* booleanPrototypeTable(CallFrame* callFrame) { return callFrame->vm().booleanPrototypeTable; }
- static const HashTable* dateTable(CallFrame* callFrame) { return callFrame->vm().dateTable; }
- static const HashTable* dateConstructorTable(CallFrame* callFrame) { return callFrame->vm().dateConstructorTable; }
- static const HashTable* errorPrototypeTable(CallFrame* callFrame) { return callFrame->vm().errorPrototypeTable; }
- static const HashTable* globalObjectTable(CallFrame* callFrame) { return callFrame->vm().globalObjectTable; }
- static const HashTable* jsonTable(CallFrame* callFrame) { return callFrame->vm().jsonTable; }
- static const HashTable* mathTable(CallFrame* callFrame) { return callFrame->vm().mathTable; }
- static const HashTable* numberConstructorTable(CallFrame* callFrame) { return callFrame->vm().numberConstructorTable; }
- static const HashTable* numberPrototypeTable(CallFrame* callFrame) { return callFrame->vm().numberPrototypeTable; }
- static const HashTable* objectConstructorTable(CallFrame* callFrame) { return callFrame->vm().objectConstructorTable; }
- static const HashTable* privateNamePrototypeTable(CallFrame* callFrame) { return callFrame->vm().privateNamePrototypeTable; }
- static const HashTable* regExpTable(CallFrame* callFrame) { return callFrame->vm().regExpTable; }
- static const HashTable* regExpConstructorTable(CallFrame* callFrame) { return callFrame->vm().regExpConstructorTable; }
- static const HashTable* regExpPrototypeTable(CallFrame* callFrame) { return callFrame->vm().regExpPrototypeTable; }
- static const HashTable* stringConstructorTable(CallFrame* callFrame) { return callFrame->vm().stringConstructorTable; }
+
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 this[JSStack::CallerFrame].callFrame(); }
-#if ENABLE(JIT) || ENABLE(LLINT)
- ReturnAddressPtr returnPC() const { return ReturnAddressPtr(this[JSStack::ReturnPC].vPC()); }
- bool hasReturnPC() const { return !!this[JSStack::ReturnPC].vPC(); }
- void clearReturnPC() { registers()[JSStack::ReturnPC] = static_cast<Instruction*>(0); }
-#endif
+ CallFrame* callerFrame() const { return static_cast<CallFrame*>(callerFrameOrVMEntryFrame()); }
+ void* callerFrameOrVMEntryFrame() const { return callerFrameAndPC().callerFrame; }
+ SUPPRESS_ASAN void* unsafeCallerFrameOrVMEntryFrame() const { return unsafeCallerFrameAndPC().callerFrame; }
+
+ CallFrame* unsafeCallerFrame(VMEntryFrame*&);
+ JS_EXPORT_PRIVATE CallFrame* callerFrame(VMEntryFrame*&);
+
+ static ptrdiff_t callerFrameOffset() { return OBJECT_OFFSETOF(CallerFrameAndPC, callerFrame); }
+
+ ReturnAddressPtr returnPC() const { return ReturnAddressPtr(callerFrameAndPC().pc); }
+ bool hasReturnPC() const { return !!callerFrameAndPC().pc; }
+ void clearReturnPC() { callerFrameAndPC().pc = 0; }
+ static ptrdiff_t returnPCOffset() { return OBJECT_OFFSETOF(CallerFrameAndPC, pc); }
AbstractPC abstractReturnPC(VM& vm) { return AbstractPC(vm, this); }
-#if USE(JSVALUE32_64)
- unsigned bytecodeOffsetForNonDFGCode() const;
- void setBytecodeOffsetForNonDFGCode(unsigned offset);
-#else
- unsigned bytecodeOffsetForNonDFGCode() const
- {
- ASSERT(codeBlock());
- return this[JSStack::ArgumentCount].tag();
- }
+
+ bool callSiteBitsAreBytecodeOffset() const;
+ bool callSiteBitsAreCodeOriginIndex() const;
+
+ unsigned callSiteAsRawBits() const;
+ unsigned unsafeCallSiteAsRawBits() const;
+ CallSiteIndex callSiteIndex() const;
+ CallSiteIndex unsafeCallSiteIndex() const;
+ private:
+ unsigned callSiteBitsAsBytecodeOffset() const;
+ public:
+
+ // This will try to get you the bytecode offset, but you should be aware that
+ // this bytecode offset may be bogus in the presence of inlining. This will
+ // also return 0 if the call frame has no notion of bytecode offsets (for
+ // example if it's native code).
+ // https://bugs.webkit.org/show_bug.cgi?id=121754
+ unsigned bytecodeOffset();
- void setBytecodeOffsetForNonDFGCode(unsigned offset)
- {
- ASSERT(codeBlock());
- this[JSStack::ArgumentCount].tag() = static_cast<int32_t>(offset);
- }
-#endif
+ // This will get you a CodeOrigin. It will always succeed. May return
+ // CodeOrigin(0) if we're in native code.
+ CodeOrigin codeOrigin();
- Register* frameExtent()
+ Register* topOfFrame()
{
if (!codeBlock())
return registers();
- return frameExtentInternal();
+ return topOfFrameInternal();
}
- Register* frameExtentInternal();
-
-#if ENABLE(DFG_JIT)
- InlineCallFrame* inlineCallFrame() const { return this[JSStack::ReturnPC].asInlineCallFrame(); }
- unsigned codeOriginIndexForDFG() const { return this[JSStack::ArgumentCount].tag(); }
-#else
- // This will never be called if !ENABLE(DFG_JIT) since all calls should be guarded by
- // isInlineCallFrame(). But to make it easier to write code without having a bunch of
- // #if's, we make a dummy implementation available anyway.
- InlineCallFrame* inlineCallFrame() const
- {
- RELEASE_ASSERT_NOT_REACHED();
- return 0;
- }
-#endif
-#if USE(JSVALUE32_64)
- Instruction* currentVPC() const
- {
- return bitwise_cast<Instruction*>(this[JSStack::ArgumentCount].tag());
- }
- void setCurrentVPC(Instruction* vpc)
- {
- this[JSStack::ArgumentCount].tag() = bitwise_cast<int32_t>(vpc);
- }
-#else
- Instruction* currentVPC() const;
+ Instruction* currentVPC() const; // This only makes sense in the LLInt and baseline.
void setCurrentVPC(Instruction* vpc);
-#endif
- void setCallerFrame(CallFrame* callerFrame) { static_cast<Register*>(this)[JSStack::CallerFrame] = callerFrame; }
- void setScope(JSScope* scope) { static_cast<Register*>(this)[JSStack::ScopeChain] = scope; }
+ void setCallerFrame(CallFrame* frame) { callerFrameAndPC().callerFrame = frame; }
+ void setScope(int scopeRegisterOffset, JSScope* scope) { static_cast<Register*>(this)[scopeRegisterOffset] = scope; }
- ALWAYS_INLINE void init(CodeBlock* codeBlock, Instruction* vPC, JSScope* scope,
- CallFrame* callerFrame, int argc, JSObject* callee)
- {
- ASSERT(callerFrame); // Use noCaller() rather than 0 for the outer host call frame caller.
- ASSERT(callerFrame == noCaller() || callerFrame->removeHostCallFrameFlag()->stack()->end() >= 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);
+ ALWAYS_INLINE void init(CodeBlock* codeBlock, Instruction* vPC,
+ CallFrame* callerFrame, int argc, JSObject* callee)
+ {
+ ASSERT(callerFrame == noCaller() || callerFrame->stack()->containsAddress(this));
+
+ setCodeBlock(codeBlock);
+ setCallerFrame(callerFrame);
+ setReturnPC(vPC); // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*.
+ setArgumentCountIncludingThis(argc); // original argument count (for the sake of the "arguments" object)
+ setCallee(callee);
}
// Read a register from the codeframe (or constant from the CodeBlock).
Register& r(int);
- // Read a register for a non-constant
+ 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; }
size_t argumentCountIncludingThis() const { return this[JSStack::ArgumentCount].payload(); }
- static int argumentOffset(int argument) { return s_firstArgumentOffset - argument; }
- static int argumentOffsetIncludingThis(int argument) { return s_thisArgumentOffset - argument; }
+ static int argumentOffset(int argument) { return (JSStack::FirstArgument + argument); }
+ static int argumentOffsetIncludingThis(int argument) { return (JSStack::ThisArgument + argument); }
// In the following (argument() and setArgument()), the 'argument'
// parameter is the index of the arguments of the target function of
@@ -207,85 +202,69 @@ namespace JSC {
{
if (argument >= argumentCount())
return jsUndefined();
- return this[argumentOffset(argument)].jsValue();
+ return getArgumentUnsafe(argument);
+ }
+ JSValue uncheckedArgument(size_t argument)
+ {
+ ASSERT(argument < argumentCount());
+ return getArgumentUnsafe(argument);
}
void setArgument(size_t argument, JSValue value)
{
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; }
- JSValue argumentAfterCapture(size_t argument);
-
- static int offsetFor(size_t argumentCountIncludingThis) { return argumentCountIncludingThis + JSStack::CallFrameHeaderSize; }
+ // 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(); }
- // FIXME: Remove these.
- int hostThisRegister() { return thisArgumentOffset(); }
- JSValue hostThisValue() { return thisValue(); }
+ JSValue argumentAfterCapture(size_t argument);
- static CallFrame* noCaller() { return reinterpret_cast<CallFrame*>(HostCallFrameFlag); }
+ static int offsetFor(size_t argumentCountIncludingThis) { return argumentCountIncludingThis + JSStack::ThisArgument - 1; }
- bool hasHostCallFrameFlag() const { return reinterpret_cast<intptr_t>(this) & HostCallFrameFlag; }
- CallFrame* addHostCallFrameFlag() const { return reinterpret_cast<CallFrame*>(reinterpret_cast<intptr_t>(this) | HostCallFrameFlag); }
- CallFrame* removeHostCallFrameFlag() { return reinterpret_cast<CallFrame*>(reinterpret_cast<intptr_t>(this) & ~HostCallFrameFlag); }
+ static CallFrame* noCaller() { return 0; }
void setArgumentCountIncludingThis(int count) { static_cast<Register*>(this)[JSStack::ArgumentCount].payload() = count; }
- void setCallee(JSObject* callee) { static_cast<Register*>(this)[JSStack::Callee] = Register::withCallee(callee); }
+ void setCallee(JSObject* callee) { static_cast<Register*>(this)[JSStack::Callee] = callee; }
void setCodeBlock(CodeBlock* codeBlock) { static_cast<Register*>(this)[JSStack::CodeBlock] = codeBlock; }
- void setReturnPC(void* value) { static_cast<Register*>(this)[JSStack::ReturnPC] = (Instruction*)value; }
-
-#if ENABLE(DFG_JIT)
- bool isInlineCallFrame();
-
- void setInlineCallFrame(InlineCallFrame* inlineCallFrame) { static_cast<Register*>(this)[JSStack::ReturnPC] = inlineCallFrame; }
-
- // Call this to get the semantically correct JS CallFrame* for the
- // currently executing function.
- CallFrame* trueCallFrame(AbstractPC);
-
- // Call this to get the semantically correct JS CallFrame* corresponding
- // to the caller. This resolves issues surrounding inlining and the
- // HostCallFrameFlag stuff.
- CallFrame* trueCallerFrame();
-
- CodeBlock* someCodeBlockForPossiblyInlinedCode();
-#else
- bool isInlineCallFrame() { return false; }
-
- CallFrame* trueCallFrame(AbstractPC) { return this; }
- CallFrame* trueCallerFrame() { return callerFrame()->removeHostCallFrameFlag(); }
-
- CodeBlock* someCodeBlockForPossiblyInlinedCode() { return codeBlock(); }
-#endif
- CallFrame* callerFrameNoFlags() { return callerFrame()->removeHostCallFrameFlag(); }
-
- // Call this to get the true call frame (accounted for inlining and any
- // other optimizations), when you have entered into VM code through one
- // of the "blessed" entrypoints (JITStubs or DFGOperations). This means
- // that if you're pretty much anywhere in the VM you can safely call this;
- // though if you were to magically get an ExecState* by, say, interrupting
- // a thread that is running JS code and brutishly scraped the call frame
- // register, calling this method would probably lead to horrible things
- // happening.
- CallFrame* trueCallFrameFromVMCode() { return trueCallFrame(AbstractPC()); }
+ void setReturnPC(void* value) { callerFrameAndPC().pc = reinterpret_cast<Instruction*>(value); }
+
+ String friendlyFunctionName();
+
+ // CallFrame::iterate() expects a Functor that implements the following method:
+ // StackVisitor::Status operator()(StackVisitor&);
+
+ template <typename Functor> void iterate(Functor& functor)
+ {
+ StackVisitor::visit<Functor>(this, functor);
+ }
+
+ void dump(PrintStream&);
+ JS_EXPORT_PRIVATE const char* describeFrame();
private:
- static const intptr_t HostCallFrameFlag = 1;
- static const int s_thisArgumentOffset = -1 - JSStack::CallFrameHeaderSize;
- static const int s_firstArgumentOffset = s_thisArgumentOffset - 1;
#ifndef NDEBUG
JSStack* stack();
#endif
-#if ENABLE(DFG_JIT)
- bool isInlineCallFrameSlow();
-#endif
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:
@@ -299,24 +278,18 @@ namespace JSC {
int offset = reg - this->registers();
// The offset is defined (based on argumentOffset()) to be:
- // offset = s_firstArgumentOffset - argIndex;
+ // offset = JSStack::FirstArgument - argIndex;
// Hence:
- // argIndex = s_firstArgumentOffset - offset;
- size_t argIndex = s_firstArgumentOffset - offset;
+ // argIndex = JSStack::FirstArgument - offset;
+ size_t argIndex = offset - JSStack::FirstArgument;
return argIndex;
}
- JSValue getArgumentUnsafe(size_t argIndex)
- {
- // User beware! This method does not verify that there is a valid
- // argument at the specified argIndex. This is used for debugging
- // and verification code only. The caller is expected to know what
- // he/she is doing when calling this method.
- return this[argumentOffset(argIndex)].jsValue();
- }
+ CallerFrameAndPC& callerFrameAndPC() { return *reinterpret_cast<CallerFrameAndPC*>(this); }
+ const CallerFrameAndPC& callerFrameAndPC() const { return *reinterpret_cast<const CallerFrameAndPC*>(this); }
+ SUPPRESS_ASAN const CallerFrameAndPC& unsafeCallerFrameAndPC() const { return *reinterpret_cast<const CallerFrameAndPC*>(this); }
friend class JSStack;
- friend class VMInspector;
};
} // namespace JSC
diff --git a/Source/JavaScriptCore/interpreter/CallFrameClosure.h b/Source/JavaScriptCore/interpreter/CallFrameClosure.h
index 7ae1e6fdf..2c03b7452 100644
--- a/Source/JavaScriptCore/interpreter/CallFrameClosure.h
+++ b/Source/JavaScriptCore/interpreter/CallFrameClosure.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 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
@@ -26,11 +26,13 @@
#ifndef CallFrameClosure_h
#define CallFrameClosure_h
+#include "ProtoCallFrame.h"
+
namespace JSC {
struct CallFrameClosure {
CallFrame* oldCallFrame;
- CallFrame* newCallFrame;
+ ProtoCallFrame* protoCallFrame;
JSFunction* function;
FunctionExecutable* functionExecutable;
VM* vm;
@@ -40,23 +42,12 @@ struct CallFrameClosure {
void setThis(JSValue value)
{
- newCallFrame->setThisValue(value);
+ protoCallFrame->setThisValue(value);
}
void setArgument(int argument, JSValue value)
{
- newCallFrame->setArgument(argument, value);
- }
-
- void resetCallFrame()
- {
- newCallFrame->setScope(scope);
- // setArgument() takes an arg index that starts from 0 for the first
- // argument after the 'this' value. Since both argumentCountIncludingThis
- // and parameterCountIncludingThis includes the 'this' value, we need to
- // subtract 1 from them to make i a valid argument index for setArgument().
- for (int i = argumentCountIncludingThis-1; i < parameterCountIncludingThis-1; ++i)
- newCallFrame->setArgument(i, jsUndefined());
+ protoCallFrame->setArgument(argument, value);
}
};
diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp
index 844f5f252..18a90760d 100644
--- a/Source/JavaScriptCore/interpreter/Interpreter.cpp
+++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2009, 2010, 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2010, 2012-2015 Apple Inc. All rights reserved.
* Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca>
*
* Redistribution and use in source and binary forms, with or without
@@ -11,7 +11,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -30,44 +30,53 @@
#include "config.h"
#include "Interpreter.h"
-#include "Arguments.h"
#include "BatchedTransitionOptimizer.h"
-#include "CallFrame.h"
#include "CallFrameClosure.h"
+#include "ClonedArguments.h"
#include "CodeBlock.h"
+#include "DirectArguments.h"
#include "Heap.h"
#include "Debugger.h"
#include "DebuggerCallFrame.h"
#include "ErrorInstance.h"
#include "EvalCodeCache.h"
+#include "Exception.h"
#include "ExceptionHelpers.h"
#include "GetterSetter.h"
-#include "JSActivation.h"
#include "JSArray.h"
#include "JSBoundFunction.h"
-#include "JSNameScope.h"
+#include "JSCInlines.h"
+#include "JSLexicalEnvironment.h"
+#include "JSModuleEnvironment.h"
#include "JSNotAnObject.h"
-#include "JSPropertyNameIterator.h"
#include "JSStackInlines.h"
#include "JSString.h"
#include "JSWithScope.h"
#include "LLIntCLoop.h"
+#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 "VMStackBounds.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>
@@ -77,36 +86,53 @@
#include "JIT.h"
#endif
-#define WTF_USE_GCC_COMPUTED_GOTO_WORKAROUND (ENABLE(LLINT) && !defined(__llvm__))
-
using namespace std;
namespace JSC {
-Interpreter::ErrorHandlingMode::ErrorHandlingMode(ExecState *exec)
- : m_interpreter(*exec->interpreter())
+String StackFrame::friendlySourceURL() const
{
- if (!m_interpreter.m_errorHandlingModeReentry)
- m_interpreter.stack().enableErrorStackReserve();
- m_interpreter.m_errorHandlingModeReentry++;
+ String traceLine;
+
+ switch (codeType) {
+ case StackFrameEvalCode:
+ case StackFrameModuleCode:
+ case StackFrameFunctionCode:
+ case StackFrameGlobalCode:
+ if (!sourceURL.isEmpty())
+ traceLine = sourceURL.impl();
+ break;
+ case StackFrameNativeCode:
+ traceLine = "[native code]";
+ break;
+ }
+ return traceLine.isNull() ? emptyString() : traceLine;
}
-Interpreter::ErrorHandlingMode::~ErrorHandlingMode()
+String StackFrame::friendlyFunctionName(CallFrame* callFrame) const
{
- m_interpreter.m_errorHandlingModeReentry--;
- ASSERT(m_interpreter.m_errorHandlingModeReentry >= 0);
- if (!m_interpreter.m_errorHandlingModeReentry)
- m_interpreter.stack().disableErrorStackReserve();
-}
-
-static CallFrame* getCallerInfo(VM*, CallFrame*, unsigned& bytecodeOffset, CodeBlock*& callerOut);
-
-// Returns the depth of the scope chain within a given call frame.
-static int depth(CodeBlock* codeBlock, JSScope* sc)
-{
- if (!codeBlock->needsFullScopeChain())
- return 0;
- return sc->localDepth();
+ String traceLine;
+ JSObject* stackFrameCallee = callee.get();
+
+ switch (codeType) {
+ case StackFrameEvalCode:
+ traceLine = "eval code";
+ break;
+ case StackFrameModuleCode:
+ traceLine = "module code";
+ break;
+ case StackFrameNativeCode:
+ if (callee)
+ traceLine = getCalculatedDisplayName(callFrame, stackFrameCallee).impl();
+ break;
+ case StackFrameFunctionCode:
+ traceLine = getCalculatedDisplayName(callFrame, stackFrameCallee).impl();
+ break;
+ case StackFrameGlobalCode:
+ traceLine = "global code";
+ break;
+ }
+ return traceLine.isNull() ? emptyString() : traceLine;
}
JSValue eval(CallFrame* callFrame)
@@ -117,21 +143,27 @@ JSValue eval(CallFrame* callFrame)
JSValue program = callFrame->argument(0);
if (!program.isString())
return program;
-
+
TopCallFrameSetter topCallFrame(callFrame->vm(), callFrame);
+ JSGlobalObject* globalObject = callFrame->lexicalGlobalObject();
+ if (!globalObject->evalEnabled()) {
+ callFrame->vm().throwException(callFrame, createEvalError(callFrame, globalObject->evalDisabledErrorMessage()));
+ return jsUndefined();
+ }
String programSource = asString(program)->value(callFrame);
if (callFrame->hadException())
return JSValue();
CallFrame* callerFrame = callFrame->callerFrame();
CodeBlock* callerCodeBlock = callerFrame->codeBlock();
- JSScope* callerScopeChain = callerFrame->scope();
- EvalExecutable* eval = callerCodeBlock->evalCodeCache().tryGet(callerCodeBlock->isStrictMode(), programSource, callerScopeChain);
+ JSScope* callerScopeChain = callerFrame->uncheckedR(callerCodeBlock->scopeRegister().offset()).Register::scope();
+ UnlinkedCodeBlock* callerUnlinkedCodeBlock = callerCodeBlock->unlinkedCodeBlock();
+
+ bool isArrowFunctionContext = callerUnlinkedCodeBlock->isArrowFunction() || callerUnlinkedCodeBlock->isArrowFunctionContext();
+ EvalExecutable* eval = callerCodeBlock->evalCodeCache().tryGet(callerCodeBlock->isStrictMode(), programSource, isArrowFunctionContext, callerScopeChain);
if (!eval) {
if (!callerCodeBlock->isStrictMode()) {
- // FIXME: We can use the preparser in strict mode, we just need additional logic
- // to prevent duplicates.
if (programSource.is8Bit()) {
LiteralParser<LChar> preparser(callFrame, programSource.characters8(), programSource.length(), NonStrictJSON);
if (JSValue parsedObject = preparser.tryLiteralParse())
@@ -144,102 +176,124 @@ JSValue eval(CallFrame* callFrame)
}
// If the literal parser bailed, it should not have thrown exceptions.
- ASSERT(!callFrame->vm().exception);
+ ASSERT(!callFrame->vm().exception());
- JSValue exceptionValue;
- eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock->unlinkedCodeBlock()->codeCacheForEval().get(), callerCodeBlock->ownerExecutable(), callerCodeBlock->isStrictMode(), programSource, callerScopeChain, exceptionValue);
-
- ASSERT(!eval == exceptionValue);
- if (UNLIKELY(!eval))
- return throwError(callFrame, exceptionValue);
+ ThisTDZMode thisTDZMode = ThisTDZMode::CheckIfNeeded;
+ if (callerUnlinkedCodeBlock->constructorKind() == ConstructorKind::Derived)
+ thisTDZMode = ThisTDZMode::AlwaysCheck;
+
+ eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock, callerCodeBlock->isStrictMode(), thisTDZMode, callerCodeBlock->unlinkedCodeBlock()->derivedContextType(), callerCodeBlock->unlinkedCodeBlock()->isArrowFunction(), programSource, callerScopeChain);
+ if (!eval)
+ return jsUndefined();
}
JSValue thisValue = callerFrame->thisValue();
- ASSERT(isValidThisObject(thisValue, callFrame));
Interpreter* interpreter = callFrame->vm().interpreter;
return interpreter->execute(eval, callFrame, thisValue, callerScopeChain);
}
-CallFrame* loadVarargs(CallFrame* callFrame, JSStack* stack, JSValue thisValue, JSValue arguments, int firstFreeRegister)
+unsigned sizeOfVarargs(CallFrame* callFrame, JSValue arguments, uint32_t firstVarArgOffset)
{
- if (!arguments) { // f.apply(x, arguments), with arguments unmodified.
- unsigned argumentCountIncludingThis = callFrame->argumentCountIncludingThis();
- CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister + argumentCountIncludingThis + JSStack::CallFrameHeaderSize);
- if (argumentCountIncludingThis > Arguments::MaxArguments + 1 || !stack->grow(newCallFrame->registers())) {
- callFrame->vm().exception = createStackOverflowError(callFrame);
+ if (UNLIKELY(!arguments.isCell())) {
+ if (arguments.isUndefinedOrNull())
return 0;
- }
-
- newCallFrame->setArgumentCountIncludingThis(argumentCountIncludingThis);
- newCallFrame->setThisValue(thisValue);
- for (size_t i = 0; i < callFrame->argumentCount(); ++i)
- newCallFrame->setArgument(i, callFrame->argumentAfterCapture(i));
- return newCallFrame;
+
+ callFrame->vm().throwException(callFrame, createInvalidFunctionApplyParameterError(callFrame, arguments));
+ return 0;
}
-
- if (arguments.isUndefinedOrNull()) {
- CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister + 1 + JSStack::CallFrameHeaderSize);
- if (!stack->grow(newCallFrame->registers())) {
- callFrame->vm().exception = createStackOverflowError(callFrame);
- return 0;
- }
- newCallFrame->setArgumentCountIncludingThis(1);
- newCallFrame->setThisValue(thisValue);
- return newCallFrame;
+
+ JSCell* cell = arguments.asCell();
+ unsigned length;
+ switch (cell->type()) {
+ case DirectArgumentsType:
+ length = jsCast<DirectArguments*>(cell)->length(callFrame);
+ break;
+ case ScopedArgumentsType:
+ length =jsCast<ScopedArguments*>(cell)->length(callFrame);
+ break;
+ case StringType:
+ callFrame->vm().throwException(callFrame, createInvalidFunctionApplyParameterError(callFrame, arguments));
+ return 0;
+ default:
+ ASSERT(arguments.isObject());
+ if (isJSArray(cell))
+ length = jsCast<JSArray*>(cell)->length();
+ else
+ length = jsCast<JSObject*>(cell)->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame);
+ break;
}
+
+ if (length >= firstVarArgOffset)
+ length -= firstVarArgOffset;
+ else
+ length = 0;
+
+ return length;
+}
- if (!arguments.isObject()) {
- callFrame->vm().exception = createInvalidParameterError(callFrame, "Function.prototype.apply", arguments);
+unsigned sizeFrameForVarargs(CallFrame* callFrame, JSStack* stack, JSValue arguments, unsigned numUsedStackSlots, uint32_t firstVarArgOffset)
+{
+ unsigned length = sizeOfVarargs(callFrame, arguments, firstVarArgOffset);
+
+ CallFrame* calleeFrame = calleeFrameForVarargs(callFrame, numUsedStackSlots, length + 1);
+ if (length > maxArguments || !stack->ensureCapacityFor(calleeFrame->registers())) {
+ throwStackOverflowError(callFrame);
return 0;
}
+
+ return length;
+}
- if (asObject(arguments)->classInfo() == &Arguments::s_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().exception = createStackOverflowError(callFrame);
- return 0;
+void loadVarargs(CallFrame* callFrame, VirtualRegister firstElementDest, JSValue arguments, uint32_t offset, uint32_t length)
+{
+ if (UNLIKELY(!arguments.isCell()) || !length)
+ return;
+
+ JSCell* cell = arguments.asCell();
+ switch (cell->type()) {
+ case DirectArgumentsType:
+ jsCast<DirectArguments*>(cell)->copyToArguments(callFrame, firstElementDest, offset, length);
+ 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;
}
- newCallFrame->setArgumentCountIncludingThis(argCount + 1);
- newCallFrame->setThisValue(thisValue);
- argsObject->copyToArguments(callFrame, newCallFrame, argCount);
- return newCallFrame;
- }
+ unsigned i;
+ for (i = 0; i < length && object->canGetIndexQuickly(i + offset); ++i)
+ callFrame->r(firstElementDest + i) = object->getIndexQuickly(i + offset);
+ for (; i < length; ++i)
+ callFrame->r(firstElementDest + i) = object->get(callFrame, i + offset);
+ return;
+ } }
+}
- 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().exception = createStackOverflowError(callFrame);
- return 0;
- }
- newCallFrame->setArgumentCountIncludingThis(argCount + 1);
- newCallFrame->setThisValue(thisValue);
- array->copyToArguments(callFrame, newCallFrame, argCount);
- return newCallFrame;
- }
+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);
+
+ newCallFrame->setArgumentCountIncludingThis(length + 1);
+}
- 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().exception = createStackOverflowError(callFrame);
- return 0;
- }
- newCallFrame->setArgumentCountIncludingThis(argCount + 1);
+void setupVarargsFrameAndSetThis(CallFrame* callFrame, CallFrame* newCallFrame, JSValue thisValue, JSValue arguments, uint32_t firstVarArgOffset, uint32_t length)
+{
+ setupVarargsFrame(callFrame, newCallFrame, arguments, firstVarArgOffset, length);
newCallFrame->setThisValue(thisValue);
- for (size_t i = 0; i < argCount; ++i) {
- newCallFrame->setArgument(i, asObject(arguments)->get(callFrame, i));
- if (UNLIKELY(callFrame->vm().exception))
- return 0;
- }
- return newCallFrame;
}
Interpreter::Interpreter(VM& vm)
: m_sampleEntryDepth(0)
+ , m_vm(vm)
, m_stack(vm)
, m_errorHandlingModeReentry(0)
#if !ASSERT_DISABLED
@@ -252,11 +306,9 @@ Interpreter::~Interpreter()
{
}
-void Interpreter::initialize(bool canUseJIT)
+void Interpreter::initialize()
{
- UNUSED_PARAM(canUseJIT);
-
-#if ENABLE(COMPUTED_GOTO_OPCODES) && ENABLE(LLINT)
+#if ENABLE(COMPUTED_GOTO_OPCODES)
m_opcodeTable = LLInt::opcodeMap();
for (int i = 0; i < numOpcodeIDs; ++i)
m_opcodeIDTable.add(m_opcodeTable[i], static_cast<OpcodeID>(i));
@@ -285,6 +337,34 @@ void Interpreter::dumpCallFrame(CallFrame* callFrame)
dumpRegisters(callFrame);
}
+class DumpRegisterFunctor {
+public:
+ DumpRegisterFunctor(const Register*& it)
+ : m_hasSkippedFirstFrame(false)
+ , m_it(it)
+ {
+ }
+
+ StackVisitor::Status operator()(StackVisitor& visitor)
+ {
+ if (!m_hasSkippedFirstFrame) {
+ m_hasSkippedFirstFrame = true;
+ return StackVisitor::Continue;
+ }
+
+ unsigned line = 0;
+ unsigned unusedColumn = 0;
+ visitor->computeLineAndColumn(line, unusedColumn);
+ dataLogF("[ReturnVPC] | %10p | %d (line %d)\n", m_it, visitor->bytecodeOffset(), line);
+ --m_it;
+ return StackVisitor::Done;
+ }
+
+private:
+ bool m_hasSkippedFirstFrame;
+ const Register*& m_it;
+};
+
void Interpreter::dumpRegisters(CallFrame* callFrame)
{
dataLogF("Register frame: \n\n");
@@ -296,63 +376,57 @@ void Interpreter::dumpRegisters(CallFrame* callFrame)
const Register* it;
const Register* end;
- it = callFrame->registers() - JSStack::CallFrameHeaderSize - callFrame->argumentCountIncludingThis();
- end = callFrame->registers() - JSStack::CallFrameHeaderSize;
- while (it < end) {
+ it = callFrame->registers() + JSStack::ThisArgument + callFrame->argumentCount();
+ end = callFrame->registers() + JSStack::ThisArgument - 1;
+ while (it > end) {
JSValue v = it->jsValue();
int registerNumber = it - callFrame->registers();
- String name = codeBlock->nameForRegister(registerNumber);
+ String name = codeBlock->nameForRegister(VirtualRegister(registerNumber));
dataLogF("[r% 3d %14s] | %10p | %-16s 0x%lld \n", registerNumber, name.ascii().data(), it, toCString(v).data(), (long long)JSValue::encode(v));
- it++;
+ --it;
}
dataLogF("-----------------------------------------------------------------------------\n");
dataLogF("[ArgumentCount] | %10p | %lu \n", it, (unsigned long) callFrame->argumentCount());
- ++it;
+ --it;
dataLogF("[CallerFrame] | %10p | %p \n", it, callFrame->callerFrame());
- ++it;
+ --it;
dataLogF("[Callee] | %10p | %p \n", it, callFrame->callee());
- ++it;
- dataLogF("[ScopeChain] | %10p | %p \n", it, callFrame->scope());
- ++it;
+ --it;
+ // FIXME: Remove the next decrement when the ScopeChain slot is removed from the call header
+ --it;
#if ENABLE(JIT)
AbstractPC pc = callFrame->abstractReturnPC(callFrame->vm());
if (pc.hasJITReturnAddress())
dataLogF("[ReturnJITPC] | %10p | %p \n", it, pc.jitReturnAddress().value());
#endif
- unsigned bytecodeOffset = 0;
- int line = 0;
- CodeBlock* callerCodeBlock = 0;
- getCallerInfo(&callFrame->vm(), callFrame, bytecodeOffset, callerCodeBlock);
- line = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset);
- dataLogF("[ReturnVPC] | %10p | %d (line %d)\n", it, bytecodeOffset, line);
- ++it;
+
+ DumpRegisterFunctor functor(it);
+ callFrame->iterate(functor);
+
dataLogF("[CodeBlock] | %10p | %p \n", it, callFrame->codeBlock());
- ++it;
+ --it;
dataLogF("-----------------------------------------------------------------------------\n");
- int registerCount = 0;
-
- end = it + codeBlock->m_numVars;
+ end = it - codeBlock->m_numVars;
if (it != end) {
do {
JSValue v = it->jsValue();
int registerNumber = it - callFrame->registers();
- String name = codeBlock->nameForRegister(registerNumber);
+ String name = codeBlock->nameForRegister(VirtualRegister(registerNumber));
dataLogF("[r% 3d %14s] | %10p | %-16s 0x%lld \n", registerNumber, name.ascii().data(), it, toCString(v).data(), (long long)JSValue::encode(v));
- ++it;
- ++registerCount;
+ --it;
} while (it != end);
}
dataLogF("-----------------------------------------------------------------------------\n");
- end = it + codeBlock->m_numCalleeRegisters - codeBlock->m_numVars;
+ end = it - codeBlock->m_numCalleeLocals + codeBlock->m_numVars;
if (it != end) {
do {
JSValue v = (*it).jsValue();
- dataLogF("[r% 3d] | %10p | %-16s 0x%lld \n", registerCount, it, toCString(v).data(), (long long)JSValue::encode(v));
- ++it;
- ++registerCount;
+ int registerNumber = it - callFrame->registers();
+ dataLogF("[r% 3d] | %10p | %-16s 0x%lld \n", registerNumber, it, toCString(v).data(), (long long)JSValue::encode(v));
+ --it;
} while (it != end);
}
dataLogF("-----------------------------------------------------------------------------\n");
@@ -363,212 +437,28 @@ 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
}
-NEVER_INLINE bool Interpreter::unwindCallFrame(CallFrame*& callFrame, JSValue exceptionValue, unsigned& bytecodeOffset, CodeBlock*& codeBlock)
-{
- CodeBlock* oldCodeBlock = codeBlock;
- JSScope* scope = callFrame->scope();
-
- if (Debugger* debugger = callFrame->dynamicGlobalObject()->debugger()) {
- DebuggerCallFrame debuggerCallFrame(callFrame, exceptionValue);
- if (callFrame->callee())
- debugger->returnEvent(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->lastLine(), 0);
- else
- debugger->didExecuteProgram(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->ownerExecutable()->lastLine(), 0);
- }
-
- JSValue activation;
- if (oldCodeBlock->codeType() == FunctionCode && oldCodeBlock->needsActivation()) {
- activation = callFrame->uncheckedR(oldCodeBlock->activationRegister()).jsValue();
- if (activation)
- jsCast<JSActivation*>(activation)->tearOff(*scope->vm());
- }
-
- if (oldCodeBlock->codeType() == FunctionCode && oldCodeBlock->usesArguments()) {
- if (JSValue arguments = callFrame->uncheckedR(unmodifiedArgumentsRegister(oldCodeBlock->argumentsRegister())).jsValue()) {
- if (activation)
- jsCast<Arguments*>(arguments)->didTearOffActivation(callFrame, jsCast<JSActivation*>(activation));
- else
- jsCast<Arguments*>(arguments)->tearOff(callFrame);
- }
- }
-
- CallFrame* callerFrame = callFrame->callerFrame();
- callFrame->vm().topCallFrame = callerFrame;
- if (callerFrame->hasHostCallFrameFlag())
- return false;
- callFrame = getCallerInfo(&callFrame->vm(), callFrame, bytecodeOffset, codeBlock);
- return true;
-}
-
-static void appendSourceToError(CallFrame* callFrame, ErrorInstance* exception, unsigned bytecodeOffset)
-{
- exception->clearAppendSourceToMessage();
-
- if (!callFrame->codeBlock()->hasExpressionInfo())
- return;
-
- int startOffset = 0;
- int endOffset = 0;
- int divotPoint = 0;
- unsigned line = 0;
- unsigned column = 0;
-
- CodeBlock* codeBlock = callFrame->codeBlock();
- codeBlock->expressionRangeForBytecodeOffset(bytecodeOffset, divotPoint, startOffset, endOffset, line, column);
-
- int expressionStart = divotPoint - startOffset;
- int expressionStop = divotPoint + endOffset;
-
- const String& sourceString = codeBlock->source()->source();
- if (!expressionStop || expressionStart > static_cast<int>(sourceString.length()))
- return;
-
- VM* vm = &callFrame->vm();
- JSValue jsMessage = exception->getDirect(*vm, vm->propertyNames->message);
- if (!jsMessage || !jsMessage.isString())
- return;
-
- String message = asString(jsMessage)->value(callFrame);
-
- if (expressionStart < expressionStop)
- message = makeString(message, " (evaluating '", codeBlock->source()->getRange(expressionStart, expressionStop), "')");
- else {
- // No range information, so give a few characters of context
- const StringImpl* data = sourceString.impl();
- int dataLength = sourceString.length();
- int start = expressionStart;
- int stop = expressionStart;
- // Get up to 20 characters of context to the left and right of the divot, clamping to the line.
- // then strip whitespace.
- while (start > 0 && (expressionStart - start < 20) && (*data)[start - 1] != '\n')
- start--;
- while (start < (expressionStart - 1) && isStrWhiteSpace((*data)[start]))
- start++;
- while (stop < dataLength && (stop - expressionStart < 20) && (*data)[stop] != '\n')
- stop++;
- while (stop > expressionStart && isStrWhiteSpace((*data)[stop - 1]))
- stop--;
- message = makeString(message, " (near '...", codeBlock->source()->getRange(start, stop), "...')");
- }
-
- exception->putDirect(*vm, vm->propertyNames->message, jsString(vm, message));
-}
-
-static unsigned getBytecodeOffsetForCallFrame(CallFrame* callFrame)
-{
- callFrame = callFrame->removeHostCallFrameFlag();
- CodeBlock* codeBlock = callFrame->codeBlock();
- if (!codeBlock)
- return 0;
-#if ENABLE(DFG_JIT)
- if (codeBlock->getJITType() == JITCode::DFGJIT)
- return codeBlock->codeOrigin(callFrame->codeOriginIndexForDFG()).bytecodeIndex;
-#endif
- return callFrame->bytecodeOffsetForNonDFGCode();
-}
-
-static CallFrame* getCallerInfo(VM* vm, CallFrame* callFrame, unsigned& bytecodeOffset, CodeBlock*& caller)
+static StackFrameCodeType getStackFrameCodeType(StackVisitor& visitor)
{
- ASSERT_UNUSED(vm, vm);
- bytecodeOffset = 0;
- ASSERT(!callFrame->hasHostCallFrameFlag());
- CallFrame* trueCallerFrame = callFrame->trueCallerFrame();
- bool wasCalledByHost = callFrame->callerFrame()->hasHostCallFrameFlag();
- ASSERT(!trueCallerFrame->hasHostCallFrameFlag());
-
- if (trueCallerFrame == CallFrame::noCaller() || !trueCallerFrame || !trueCallerFrame->codeBlock()) {
- caller = 0;
- return trueCallerFrame;
- }
-
- CodeBlock* callerCodeBlock = trueCallerFrame->codeBlock();
-
- if (!callFrame->hasReturnPC())
- wasCalledByHost = true;
-
- if (wasCalledByHost) {
-#if ENABLE(DFG_JIT)
- if (callerCodeBlock && callerCodeBlock->getJITType() == JITCode::DFGJIT) {
- unsigned codeOriginIndex = callFrame->callerFrame()->removeHostCallFrameFlag()->codeOriginIndexForDFG();
- CodeOrigin origin = callerCodeBlock->codeOrigin(codeOriginIndex);
- bytecodeOffset = origin.bytecodeIndex;
- if (InlineCallFrame* inlineCallFrame = origin.inlineCallFrame)
- callerCodeBlock = inlineCallFrame->baselineCodeBlock();
- } else
-#endif
- bytecodeOffset = trueCallerFrame->bytecodeOffsetForNonDFGCode();
- } else {
-#if ENABLE(DFG_JIT)
- if (callFrame->isInlineCallFrame()) {
- InlineCallFrame* icf = callFrame->inlineCallFrame();
- bytecodeOffset = icf->caller.bytecodeIndex;
- if (InlineCallFrame* parentCallFrame = icf->caller.inlineCallFrame) {
- FunctionExecutable* executable = static_cast<FunctionExecutable*>(parentCallFrame->executable.get());
- CodeBlock* newCodeBlock = executable->baselineCodeBlockFor(parentCallFrame->isCall ? CodeForCall : CodeForConstruct);
- ASSERT(newCodeBlock);
- ASSERT(newCodeBlock->instructionCount() > bytecodeOffset);
- callerCodeBlock = newCodeBlock;
- }
- } else if (callerCodeBlock && callerCodeBlock->getJITType() == JITCode::DFGJIT) {
- CodeOrigin origin;
- if (!callerCodeBlock->codeOriginForReturn(callFrame->returnPC(), origin)) {
- // This should not be possible, but we're seeing cases where it does happen
- // CallFrame already has robustness against bogus stack walks, so
- // we'll extend that to here as well.
- ASSERT_NOT_REACHED();
- caller = 0;
- return 0;
- }
- bytecodeOffset = origin.bytecodeIndex;
- if (InlineCallFrame* icf = origin.inlineCallFrame) {
- FunctionExecutable* executable = static_cast<FunctionExecutable*>(icf->executable.get());
- CodeBlock* newCodeBlock = executable->baselineCodeBlockFor(icf->isCall ? CodeForCall : CodeForConstruct);
- ASSERT(newCodeBlock);
- ASSERT(newCodeBlock->instructionCount() > bytecodeOffset);
- callerCodeBlock = newCodeBlock;
- }
- } else
-#endif
- {
- RELEASE_ASSERT(callerCodeBlock);
- bytecodeOffset = callerCodeBlock->bytecodeOffset(trueCallerFrame, callFrame->returnPC());
- }
- }
-
- RELEASE_ASSERT(callerCodeBlock);
- caller = callerCodeBlock;
- return trueCallerFrame;
-}
-
-static ALWAYS_INLINE const String getSourceURLFromCallFrame(CallFrame* callFrame)
-{
- ASSERT(!callFrame->hasHostCallFrameFlag());
- return callFrame->codeBlock()->ownerExecutable()->sourceURL();
-}
-
-static StackFrameCodeType getStackFrameCodeType(CallFrame* callFrame)
-{
- ASSERT(!callFrame->hasHostCallFrameFlag());
-
- switch (callFrame->codeBlock()->codeType()) {
- case EvalCode:
+ switch (visitor->codeType()) {
+ case StackVisitor::Frame::Eval:
return StackFrameEvalCode;
- case FunctionCode:
+ case StackVisitor::Frame::Module:
+ return StackFrameModuleCode;
+ case StackVisitor::Frame::Function:
return StackFrameFunctionCode;
- case GlobalCode:
+ case StackVisitor::Frame::Global:
return StackFrameGlobalCode;
+ case StackVisitor::Frame::Native:
+ ASSERT_NOT_REACHED();
+ return StackFrameNativeCode;
}
RELEASE_ASSERT_NOT_REACHED();
return StackFrameGlobalCode;
@@ -591,6 +481,9 @@ void StackFrame::computeLineAndColumn(unsigned& line, unsigned& column)
line = divotLine + lineOffset;
column = divotColumn + (divotLine ? 1 : firstLineColumnOffset);
+
+ if (executable->hasOverrideLineNumber())
+ line = executable->overrideLineNumber();
}
void StackFrame::expressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column)
@@ -623,147 +516,286 @@ String StackFrame::toString(CallFrame* callFrame)
return traceBuild.toString().impl();
}
-void Interpreter::getStackTrace(VM* vm, Vector<StackFrame>& results, size_t maxStackSize)
+static inline bool isWebAssemblyExecutable(ExecutableBase* executable)
{
- CallFrame* callFrame = vm->topCallFrame->removeHostCallFrameFlag();
- if (!callFrame || callFrame == CallFrame::noCaller())
- return;
- unsigned bytecodeOffset = getBytecodeOffsetForCallFrame(callFrame);
- callFrame = callFrame->trueCallFrameFromVMCode();
- if (!callFrame)
- return;
- CodeBlock* callerCodeBlock = callFrame->codeBlock();
-
- while (callFrame && callFrame != CallFrame::noCaller() && maxStackSize--) {
- String sourceURL;
- if (callerCodeBlock) {
- sourceURL = getSourceURLFromCallFrame(callFrame);
- StackFrame s = {
- Strong<JSObject>(*vm, callFrame->callee()),
- getStackFrameCodeType(callFrame),
- Strong<ExecutableBase>(*vm, callerCodeBlock->ownerExecutable()),
- Strong<UnlinkedCodeBlock>(*vm, callerCodeBlock->unlinkedCodeBlock()),
- callerCodeBlock->source(),
- callerCodeBlock->ownerExecutable()->lineNo(),
- callerCodeBlock->firstLineColumnOffset(),
- callerCodeBlock->sourceOffset(),
- bytecodeOffset,
- sourceURL
- };
-
- results.append(s);
- } else {
- StackFrame s = { Strong<JSObject>(*vm, callFrame->callee()), StackFrameNativeCode, Strong<ExecutableBase>(), Strong<UnlinkedCodeBlock>(), 0, 0, 0, 0, 0, String()};
- results.append(s);
- }
- callFrame = getCallerInfo(vm, callFrame, bytecodeOffset, callerCodeBlock);
- }
+#if !ENABLE(WEBASSEMBLY)
+ UNUSED_PARAM(executable);
+ return false;
+#else
+ return executable->isWebAssemblyExecutable();
+#endif
}
-void Interpreter::addStackTraceIfNecessary(CallFrame* callFrame, JSValue error)
-{
- VM* vm = &callFrame->vm();
- ASSERT(callFrame == vm->topCallFrame || callFrame == callFrame->lexicalGlobalObject()->globalExec() || callFrame == callFrame->dynamicGlobalObject()->globalExec());
-
- if (error.isObject()) {
- if (asObject(error)->hasProperty(callFrame, vm->propertyNames->stack))
- return;
+class GetStackTraceFunctor {
+public:
+ GetStackTraceFunctor(VM& vm, Vector<StackFrame>& results, size_t remainingCapacity)
+ : m_vm(vm)
+ , m_results(results)
+ , m_remainingCapacityForFrameCapture(remainingCapacity)
+ {
}
+
+ StackVisitor::Status operator()(StackVisitor& visitor)
+ {
+ VM& vm = m_vm;
+ if (m_remainingCapacityForFrameCapture) {
+ if (visitor->isJSFrame()
+ && !isWebAssemblyExecutable(visitor->codeBlock()->ownerExecutable())
+ && !visitor->codeBlock()->unlinkedCodeBlock()->isBuiltinFunction()) {
+ CodeBlock* codeBlock = visitor->codeBlock();
+ StackFrame s = {
+ Strong<JSObject>(vm, visitor->callee()),
+ getStackFrameCodeType(visitor),
+ Strong<ScriptExecutable>(vm, codeBlock->ownerScriptExecutable()),
+ Strong<UnlinkedCodeBlock>(vm, codeBlock->unlinkedCodeBlock()),
+ codeBlock->source(),
+ codeBlock->ownerScriptExecutable()->firstLine(),
+ codeBlock->firstLineColumnOffset(),
+ codeBlock->sourceOffset(),
+ visitor->bytecodeOffset(),
+ visitor->sourceURL()
+ };
+ m_results.append(s);
+ } else {
+ StackFrame s = { Strong<JSObject>(vm, visitor->callee()), StackFrameNativeCode, Strong<ScriptExecutable>(), Strong<UnlinkedCodeBlock>(), 0, 0, 0, 0, 0, String()};
+ m_results.append(s);
+ }
- Vector<StackFrame> stackTrace;
- getStackTrace(&callFrame->vm(), stackTrace);
- vm->exceptionStack() = RefCountedArray<StackFrame>(stackTrace);
- if (stackTrace.isEmpty() || !error.isObject())
+ m_remainingCapacityForFrameCapture--;
+ return StackVisitor::Continue;
+ }
+ return StackVisitor::Done;
+ }
+
+private:
+ VM& m_vm;
+ Vector<StackFrame>& m_results;
+ size_t m_remainingCapacityForFrameCapture;
+};
+
+void Interpreter::getStackTrace(Vector<StackFrame>& results, size_t maxStackSize)
+{
+ VM& vm = m_vm;
+ CallFrame* callFrame = vm.topCallFrame;
+ if (!callFrame)
return;
- JSObject* errorObject = asObject(error);
- JSGlobalObject* globalObject = 0;
- if (isTerminatedExecutionException(error))
- globalObject = vm->dynamicGlobalObject;
- else
- globalObject = errorObject->globalObject();
+ GetStackTraceFunctor functor(vm, results, maxStackSize);
+ callFrame->iterate(functor);
+}
+JSString* Interpreter::stackTraceAsString(ExecState* exec, Vector<StackFrame> stackTrace)
+{
// FIXME: JSStringJoiner could be more efficient than StringBuilder here.
StringBuilder builder;
for (unsigned i = 0; i < stackTrace.size(); i++) {
- builder.append(String(stackTrace[i].toString(globalObject->globalExec()).impl()));
+ builder.append(String(stackTrace[i].toString(exec)));
if (i != stackTrace.size() - 1)
builder.append('\n');
}
-
- errorObject->putDirect(*vm, vm->propertyNames->stack, jsString(vm, builder.toString()), ReadOnly | DontDelete);
+ return jsString(&exec->vm(), builder.toString());
}
-NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSValue& exceptionValue, unsigned bytecodeOffset)
+ALWAYS_INLINE static HandlerInfo* findExceptionHandler(StackVisitor& visitor, CodeBlock* codeBlock, CodeBlock::RequiredHandler requiredHandler)
{
- CodeBlock* codeBlock = callFrame->codeBlock();
- bool isTermination = false;
+ ASSERT(codeBlock);
+#if ENABLE(DFG_JIT)
+ ASSERT(!visitor->isInlinedFrame());
+#endif
- ASSERT(!exceptionValue.isEmpty());
- ASSERT(!exceptionValue.isCell() || exceptionValue.asCell());
- // This shouldn't be possible (hence the assertions), but we're already in the slowest of
- // slow cases, so let's harden against it anyway to be safe.
- if (exceptionValue.isEmpty() || (exceptionValue.isCell() && !exceptionValue.asCell()))
- exceptionValue = jsNull();
+ CallFrame* callFrame = visitor->callFrame();
+ unsigned exceptionHandlerIndex;
+ if (JITCode::isOptimizingJIT(codeBlock->jitType()))
+ exceptionHandlerIndex = callFrame->callSiteIndex().bits();
+ else
+ exceptionHandlerIndex = callFrame->bytecodeOffset();
+
+ return codeBlock->handlerForIndex(exceptionHandlerIndex, requiredHandler);
+}
+
+class GetCatchHandlerFunctor {
+public:
+ GetCatchHandlerFunctor()
+ : m_handler(0)
+ {
+ }
+
+ HandlerInfo* handler() { return m_handler; }
- // Set up the exception object
- if (exceptionValue.isObject()) {
- JSObject* exception = asObject(exceptionValue);
+ StackVisitor::Status operator()(StackVisitor& visitor)
+ {
+ visitor.unwindToMachineCodeBlockFrame();
+
+ CodeBlock* codeBlock = visitor->codeBlock();
+ if (!codeBlock)
+ return StackVisitor::Continue;
+
+ m_handler = findExceptionHandler(visitor, codeBlock, CodeBlock::RequiredHandler::CatchHandler);
+ if (m_handler)
+ return StackVisitor::Done;
+
+ return StackVisitor::Continue;
+ }
+
+private:
+ HandlerInfo* m_handler;
+};
+
+ALWAYS_INLINE static void notifyDebuggerOfUnwinding(CallFrame* callFrame)
+{
+ if (Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger()) {
+ SuspendExceptionScope scope(&callFrame->vm());
+ if (jsDynamicCast<JSFunction*>(callFrame->callee()))
+ debugger->returnEvent(callFrame);
+ else
+ debugger->didExecuteProgram(callFrame);
+ ASSERT(!callFrame->hadException());
+ }
+}
- if (exception->isErrorInstance() && static_cast<ErrorInstance*>(exception)->appendSourceToMessage())
- appendSourceToError(callFrame, static_cast<ErrorInstance*>(exception), bytecodeOffset);
+class UnwindFunctor {
+public:
+ UnwindFunctor(CallFrame*& callFrame, bool isTermination, CodeBlock*& codeBlock, HandlerInfo*& handler)
+ : m_callFrame(callFrame)
+ , m_isTermination(isTermination)
+ , m_codeBlock(codeBlock)
+ , m_handler(handler)
+ {
+ }
- if (!hasErrorInfo(callFrame, exception)) {
- // FIXME: should only really be adding these properties to VM generated exceptions,
- // but the inspector currently requires these for all thrown objects.
- addErrorInfo(callFrame, exception, codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), codeBlock->ownerExecutable()->source());
+ StackVisitor::Status operator()(StackVisitor& visitor)
+ {
+ visitor.unwindToMachineCodeBlockFrame();
+ VM& vm = m_callFrame->vm();
+ m_callFrame = visitor->callFrame();
+ m_codeBlock = visitor->codeBlock();
+
+ m_handler = nullptr;
+ if (!m_isTermination) {
+ if (m_codeBlock && !isWebAssemblyExecutable(m_codeBlock->ownerExecutable()))
+ m_handler = findExceptionHandler(visitor, m_codeBlock, CodeBlock::RequiredHandler::AnyHandler);
}
- isTermination = isTerminatedExecutionException(exception);
- } else {
- if (!callFrame->vm().exceptionStack().size()) {
- Vector<StackFrame> stack;
- Interpreter::getStackTrace(&callFrame->vm(), stack);
- callFrame->vm().exceptionStack() = RefCountedArray<StackFrame>(stack);
+ if (m_handler)
+ return StackVisitor::Done;
+
+ notifyDebuggerOfUnwinding(m_callFrame);
+
+ bool shouldStopUnwinding = visitor->callerIsVMEntryFrame();
+ if (shouldStopUnwinding) {
+ if (LegacyProfiler* profiler = vm.enabledProfiler())
+ profiler->exceptionUnwind(m_callFrame);
+
+ copyCalleeSavesToVMCalleeSavesBuffer(visitor);
+
+ return StackVisitor::Done;
}
- }
- if (Debugger* debugger = callFrame->dynamicGlobalObject()->debugger()) {
- DebuggerCallFrame debuggerCallFrame(callFrame, exceptionValue);
- bool hasHandler = codeBlock->handlerForBytecodeOffset(bytecodeOffset);
- debugger->exception(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), 0, hasHandler);
+ copyCalleeSavesToVMCalleeSavesBuffer(visitor);
+
+ return StackVisitor::Continue;
}
- // Calculate an exception handler vPC, unwinding call frames as necessary.
- HandlerInfo* handler = 0;
- while (isTermination || !(handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset))) {
- if (!unwindCallFrame(callFrame, exceptionValue, bytecodeOffset, codeBlock)) {
- if (LegacyProfiler* profiler = callFrame->vm().enabledProfiler())
- profiler->exceptionUnwind(callFrame);
- return 0;
+private:
+ void copyCalleeSavesToVMCalleeSavesBuffer(StackVisitor& visitor)
+ {
+#if ENABLE(JIT) && NUMBER_OF_CALLEE_SAVES_REGISTERS > 0
+
+ if (!visitor->isJSFrame())
+ return;
+
+#if ENABLE(DFG_JIT)
+ if (visitor->inlineCallFrame())
+ return;
+#endif
+ RegisterAtOffsetList* currentCalleeSaves = m_codeBlock ? m_codeBlock->calleeSaveRegisters() : nullptr;
+
+ if (!currentCalleeSaves)
+ return;
+
+ VM& vm = m_callFrame->vm();
+ RegisterAtOffsetList* allCalleeSaves = vm.getAllCalleeSaveRegisterOffsets();
+ RegisterSet dontCopyRegisters = RegisterSet::stackRegisters();
+ intptr_t* frame = reinterpret_cast<intptr_t*>(m_callFrame->registers());
+
+ unsigned registerCount = currentCalleeSaves->size();
+ for (unsigned i = 0; i < registerCount; i++) {
+ RegisterAtOffset currentEntry = currentCalleeSaves->at(i);
+ if (dontCopyRegisters.get(currentEntry.reg()))
+ continue;
+ RegisterAtOffset* vmCalleeSavesEntry = allCalleeSaves->find(currentEntry.reg());
+
+ vm.calleeSaveRegistersBuffer[vmCalleeSavesEntry->offsetAsIndex()] = *(frame + currentEntry.offsetAsIndex());
}
+#else
+ UNUSED_PARAM(visitor);
+#endif
}
- if (LegacyProfiler* profiler = callFrame->vm().enabledProfiler())
- profiler->exceptionUnwind(callFrame);
-
- // Unwind the scope chain within the exception handler's call frame.
- JSScope* scope = callFrame->scope();
- int scopeDelta = 0;
- if (!codeBlock->needsFullScopeChain() || codeBlock->codeType() != FunctionCode
- || callFrame->uncheckedR(codeBlock->activationRegister()).jsValue()) {
- int currentDepth = depth(codeBlock, scope);
- int targetDepth = handler->scopeDepth;
- scopeDelta = currentDepth - targetDepth;
- RELEASE_ASSERT(scopeDelta >= 0);
+ CallFrame*& m_callFrame;
+ bool m_isTermination;
+ CodeBlock*& m_codeBlock;
+ HandlerInfo*& m_handler;
+};
+
+NEVER_INLINE HandlerInfo* Interpreter::unwind(VM& vm, CallFrame*& callFrame, Exception* exception, UnwindStart unwindStart)
+{
+ if (unwindStart == UnwindFromCallerFrame) {
+ if (callFrame->callerFrameOrVMEntryFrame() == vm.topVMEntryFrame)
+ return nullptr;
+
+ callFrame = callFrame->callerFrame();
+ vm.topCallFrame = callFrame;
}
- while (scopeDelta--)
- scope = scope->next();
- callFrame->setScope(scope);
+
+ CodeBlock* codeBlock = callFrame->codeBlock();
+
+ 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
+ // slow cases, so let's harden against it anyway to be safe.
+ if (exceptionValue.isEmpty() || (exceptionValue.isCell() && !exceptionValue.asCell()))
+ exceptionValue = jsNull();
+
+ ASSERT(vm.exception() && vm.exception()->stack().size());
+
+ // Calculate an exception handler vPC, unwinding call frames as necessary.
+ HandlerInfo* handler = nullptr;
+ UnwindFunctor functor(callFrame, isTerminatedExecutionException(exception), codeBlock, handler);
+ callFrame->iterate(functor);
+ if (!handler)
+ return nullptr;
return handler;
}
+void Interpreter::notifyDebuggerOfExceptionToBeThrown(CallFrame* callFrame, Exception* exception)
+{
+ Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger();
+ if (debugger && debugger->needsExceptionCallbacks() && !exception->didNotifyInspectorOfThrow()) {
+ // This code assumes that if the debugger is enabled then there is no inlining.
+ // If that assumption turns out to be false then we'll ignore the inlined call
+ // frames.
+ // https://bugs.webkit.org/show_bug.cgi?id=121754
+
+ bool hasCatchHandler;
+ bool isTermination = isTerminatedExecutionException(exception);
+ if (isTermination)
+ hasCatchHandler = false;
+ else {
+ GetCatchHandlerFunctor functor;
+ callFrame->iterate(functor);
+ HandlerInfo* handler = functor.handler();
+ ASSERT(!handler || handler->isCatchHandler());
+ hasCatchHandler = !!handler;
+ }
+
+ debugger->exception(callFrame, exception->value(), hasCatchHandler);
+ }
+ exception->setDidNotifyInspectorOfThrow();
+}
+
static inline JSValue checkedReturn(JSValue returnValue)
{
ASSERT(returnValue);
@@ -794,28 +826,26 @@ private:
JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, JSObject* thisObj)
{
SamplingScope samplingScope(this);
-
- JSScope* scope = callFrame->scope();
+
+ JSScope* scope = thisObj->globalObject()->globalScope();
VM& vm = *scope->vm();
- ASSERT(isValidThisObject(thisObj, callFrame));
- ASSERT(!vm.exception);
+ ASSERT(!vm.exception());
ASSERT(!vm.isCollectorBusy());
+ RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock());
if (vm.isCollectorBusy())
return jsNull();
- StackStats::CheckPoint stackCheckPoint;
- const VMStackBounds vmStackBounds(vm, wtfThreadData().stack());
- if (!vmStackBounds.isSafeToRecurse())
+ if (!vm.isSafeToRecurse())
return checkedReturn(throwStackOverflowError(callFrame));
// First check if the "program" is actually just a JSON object. If so,
// we'll handle the JSON object here. Else, we'll handle real JS code
// below at failedJSONP.
- DynamicGlobalObjectScope globalObjectScope(vm, scope->globalObject());
+
Vector<JSONPData> JSONPData;
bool parseResult;
- const String programSource = program->source().toString();
+ StringView programSource = program->source().view();
if (programSource.isNull())
return jsUndefined();
if (programSource.is8Bit()) {
@@ -834,12 +864,9 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J
JSONPPath.swap(JSONPData[entry].m_path);
JSValue JSONPValue = JSONPData[entry].m_value.get();
if (JSONPPath.size() == 1 && JSONPPath[0].m_type == JSONPPathEntryTypeDeclare) {
- if (globalObject->hasProperty(callFrame, JSONPPath[0].m_pathEntryName)) {
- PutPropertySlot slot;
- globalObject->methodTable()->put(globalObject, callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, slot);
- } else
- globalObject->methodTable()->putDirectVirtual(globalObject, callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, DontEnum | DontDelete);
- // var declarations return undefined
+ globalObject->addVar(callFrame, JSONPPath[0].m_pathEntryName);
+ PutPropertySlot slot(globalObject);
+ globalObject->methodTable()->put(globalObject, callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, slot);
result = jsUndefined();
continue;
}
@@ -849,10 +876,10 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J
switch (JSONPPath[i].m_type) {
case JSONPPathEntryTypeDot: {
if (i == 0) {
- PropertySlot slot(globalObject);
+ PropertySlot slot(globalObject, PropertySlot::InternalMethodType::Get);
if (!globalObject->getPropertySlot(callFrame, JSONPPath[i].m_pathEntryName, slot)) {
if (entry)
- return throwError(callFrame, createUndefinedVariableError(globalObject->globalExec(), JSONPPath[i].m_pathEntryName));
+ return callFrame->vm().throwException(callFrame, createUndefinedVariableError(callFrame, JSONPPath[i].m_pathEntryName));
goto failedJSONP;
}
baseObject = slot.getValue(callFrame, JSONPPath[i].m_pathEntryName);
@@ -873,7 +900,7 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J
return jsUndefined();
}
}
- PutPropertySlot slot;
+ PutPropertySlot slot(baseObject);
switch (JSONPPath.last().m_type) {
case JSONPPathEntryTypeCall: {
JSValue function = baseObject.get(callFrame, JSONPPath.last().m_pathEntryName);
@@ -882,7 +909,7 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J
CallData callData;
CallType callType = getCallData(function, callData);
if (callType == CallTypeNone)
- return throwError(callFrame, createNotAFunctionError(callFrame, function));
+ return callFrame->vm().throwException(callFrame, createNotAFunctionError(callFrame, function));
MarkedArgumentBuffer jsonArg;
jsonArg.append(JSONPValue);
JSValue thisValue = JSONPPath.size() == 1 ? jsUndefined(): baseObject;
@@ -915,47 +942,37 @@ failedJSONP:
// If we get here, then we have already proven that the script is not a JSON
// object.
+ VMEntryScope entryScope(vm, scope->globalObject());
+
// Compile source to bytecode if necessary:
if (JSObject* error = program->initializeGlobalProperties(vm, callFrame, scope))
- return checkedReturn(throwError(callFrame, error));
+ return checkedReturn(callFrame->vm().throwException(callFrame, error));
- if (JSObject* error = program->compile(callFrame, scope))
- return checkedReturn(throwError(callFrame, error));
+ if (JSObject* error = program->prepareForExecution(callFrame, nullptr, scope, CodeForCall))
+ return checkedReturn(callFrame->vm().throwException(callFrame, error));
- ProgramCodeBlock* codeBlock = &program->generatedBytecode();
+ ProgramCodeBlock* codeBlock = program->codeBlock();
- if (UNLIKELY(vm.watchdog.didFire(callFrame)))
+ if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
return throwTerminatedExecutionException(callFrame);
- // Push the call frame for this invocation:
ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'.
- CallFrame* newCallFrame = m_stack.pushFrame(callFrame, codeBlock, scope, 1, 0);
- if (UNLIKELY(!newCallFrame))
- return checkedReturn(throwStackOverflowError(callFrame));
- // Set the arguments for the callee:
- newCallFrame->setThisValue(thisObj);
+ ProtoCallFrame protoCallFrame;
+ protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), thisObj, 1);
if (LegacyProfiler* profiler = vm.enabledProfiler())
- profiler->willExecute(callFrame, program->sourceURL(), program->lineNo());
+ profiler->willExecute(callFrame, program->sourceURL(), program->firstLine(), program->startColumn());
// Execute the code:
JSValue result;
{
SamplingTool::CallRecord callRecord(m_sampler.get());
- Watchdog::Scope watchdogScope(vm.watchdog);
-
-#if ENABLE(LLINT_C_LOOP)
- result = LLInt::CLoop::execute(newCallFrame, llint_program_prologue);
-#elif ENABLE(JIT)
- result = program->generatedJITCode().execute(&m_stack, newCallFrame, &vm);
-#endif // ENABLE(JIT)
+ result = program->generatedJITCode()->execute(&vm, &protoCallFrame);
}
if (LegacyProfiler* profiler = vm.enabledProfiler())
- profiler->didExecute(callFrame, program->sourceURL(), program->lineNo());
-
- m_stack.popFrame(newCallFrame);
+ profiler->didExecute(callFrame, program->sourceURL(), program->firstLine(), program->startColumn());
return checkedReturn(result);
}
@@ -963,52 +980,47 @@ failedJSONP:
JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args)
{
VM& vm = callFrame->vm();
- ASSERT(isValidThisObject(thisValue, callFrame));
ASSERT(!callFrame->hadException());
ASSERT(!vm.isCollectorBusy());
if (vm.isCollectorBusy())
return jsNull();
- StackStats::CheckPoint stackCheckPoint;
- const VMStackBounds vmStackBounds(vm, wtfThreadData().stack());
- if (!vmStackBounds.isSafeToRecurse())
- return checkedReturn(throwStackOverflowError(callFrame));
-
bool isJSCall = (callType == CallTypeJS);
- JSScope* scope;
+ JSScope* scope = nullptr;
CodeBlock* newCodeBlock;
size_t argsCount = 1 + args.size(); // implicit "this" parameter
- if (isJSCall)
+ JSGlobalObject* globalObject;
+
+ if (isJSCall) {
scope = callData.js.scope;
- else {
+ globalObject = scope->globalObject();
+ } else {
ASSERT(callType == CallTypeHost);
- scope = callFrame->scope();
+ globalObject = function->globalObject();
}
- DynamicGlobalObjectScope globalObjectScope(vm, scope->globalObject());
+
+ VMEntryScope entryScope(vm, globalObject);
+ if (!vm.isSafeToRecurse())
+ return checkedReturn(throwStackOverflowError(callFrame));
if (isJSCall) {
// Compile the callee:
- JSObject* compileError = callData.js.functionExecutable->compileForCall(callFrame, scope);
+ JSObject* compileError = callData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(function), scope, CodeForCall);
if (UNLIKELY(!!compileError)) {
- return checkedReturn(throwError(callFrame, compileError));
+ return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
}
- newCodeBlock = &callData.js.functionExecutable->generatedBytecodeForCall();
+ newCodeBlock = callData.js.functionExecutable->codeBlockForCall();
ASSERT(!!newCodeBlock);
+ newCodeBlock->m_shouldAlwaysBeInlined = false;
} else
newCodeBlock = 0;
- if (UNLIKELY(vm.watchdog.didFire(callFrame)))
+ if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
return throwTerminatedExecutionException(callFrame);
- CallFrame* newCallFrame = m_stack.pushFrame(callFrame, newCodeBlock, scope, argsCount, function);
- if (UNLIKELY(!newCallFrame))
- return checkedReturn(throwStackOverflowError(callFrame));
-
- // Set the arguments for the callee:
- newCallFrame->setThisValue(thisValue);
- for (size_t i = 0; i < args.size(); ++i)
- newCallFrame->setArgument(i, args.at(i));
+ ProtoCallFrame protoCallFrame;
+ protoCallFrame.init(newCodeBlock, function, thisValue, argsCount, args.data());
if (LegacyProfiler* profiler = vm.enabledProfiler())
profiler->willExecute(callFrame, function);
@@ -1016,27 +1028,24 @@ 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) {
-#if ENABLE(LLINT_C_LOOP)
- result = LLInt::CLoop::execute(newCallFrame, llint_function_for_call_prologue);
-#elif ENABLE(JIT)
- result = callData.js.functionExecutable->generatedJITCodeForCall().execute(&m_stack, newCallFrame, &vm);
-#endif // ENABLE(JIT)
- } else
- result = JSValue::decode(callData.native.function(newCallFrame));
+ 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();
+ }
}
if (LegacyProfiler* profiler = vm.enabledProfiler())
profiler->didExecute(callFrame, function);
- m_stack.popFrame(newCallFrame);
return checkedReturn(result);
}
-JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* constructor, ConstructType constructType, const ConstructData& constructData, const ArgList& args)
+JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* constructor, ConstructType constructType, const ConstructData& constructData, const ArgList& args, JSValue newTarget)
{
VM& vm = callFrame->vm();
ASSERT(!callFrame->hadException());
@@ -1046,47 +1055,42 @@ JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* construc
if (vm.isCollectorBusy())
return checkedReturn(throwStackOverflowError(callFrame));
- StackStats::CheckPoint stackCheckPoint;
- const VMStackBounds vmStackBounds(vm, wtfThreadData().stack());
- if (!vmStackBounds.isSafeToRecurse())
- return checkedReturn(throwStackOverflowError(callFrame));
-
bool isJSConstruct = (constructType == ConstructTypeJS);
- JSScope* scope;
+ JSScope* scope = nullptr;
CodeBlock* newCodeBlock;
size_t argsCount = 1 + args.size(); // implicit "this" parameter
- if (isJSConstruct)
+ JSGlobalObject* globalObject;
+
+ if (isJSConstruct) {
scope = constructData.js.scope;
- else {
+ globalObject = scope->globalObject();
+ } else {
ASSERT(constructType == ConstructTypeHost);
- scope = callFrame->scope();
+ globalObject = constructor->globalObject();
}
- DynamicGlobalObjectScope globalObjectScope(vm, scope->globalObject());
+ VMEntryScope entryScope(vm, globalObject);
+ if (!vm.isSafeToRecurse())
+ return checkedReturn(throwStackOverflowError(callFrame));
if (isJSConstruct) {
// Compile the callee:
- JSObject* compileError = constructData.js.functionExecutable->compileForConstruct(callFrame, scope);
+ JSObject* compileError = constructData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(constructor), scope, CodeForConstruct);
if (UNLIKELY(!!compileError)) {
- return checkedReturn(throwError(callFrame, compileError));
+ return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
}
- newCodeBlock = &constructData.js.functionExecutable->generatedBytecodeForConstruct();
+ newCodeBlock = constructData.js.functionExecutable->codeBlockForConstruct();
ASSERT(!!newCodeBlock);
+ newCodeBlock->m_shouldAlwaysBeInlined = false;
} else
newCodeBlock = 0;
- if (UNLIKELY(vm.watchdog.didFire(callFrame)))
+ if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
return throwTerminatedExecutionException(callFrame);
- CallFrame* newCallFrame = m_stack.pushFrame(callFrame, newCodeBlock, scope, argsCount, constructor);
- if (UNLIKELY(!newCallFrame))
- return checkedReturn(throwStackOverflowError(callFrame));
-
- // Set the arguments for the callee:
- newCallFrame->setThisValue(jsUndefined());
- for (size_t i = 0; i < args.size(); ++i)
- newCallFrame->setArgument(i, args.at(i));
+ ProtoCallFrame protoCallFrame;
+ protoCallFrame.init(newCodeBlock, constructor, newTarget, argsCount, args.data());
if (LegacyProfiler* profiler = vm.enabledProfiler())
profiler->willExecute(callFrame, constructor);
@@ -1094,68 +1098,49 @@ 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) {
-#if ENABLE(LLINT_C_LOOP)
- result = LLInt::CLoop::execute(newCallFrame, llint_function_for_construct_prologue);
-#elif ENABLE(JIT)
- result = constructData.js.functionExecutable->generatedJITCodeForConstruct().execute(&m_stack, newCallFrame, &vm);
-#endif // ENABLE(JIT)
- } else
- result = JSValue::decode(constructData.native.function(newCallFrame));
+ if (isJSConstruct)
+ result = constructData.js.functionExecutable->generatedJITCodeForConstruct()->execute(&vm, &protoCallFrame);
+ else {
+ result = JSValue::decode(vmEntryToNative(reinterpret_cast<void*>(constructData.native.function), &vm, &protoCallFrame));
+
+ if (!callFrame->hadException())
+ RELEASE_ASSERT(result.isObject());
+ }
}
if (LegacyProfiler* profiler = vm.enabledProfiler())
profiler->didExecute(callFrame, constructor);
- m_stack.popFrame(newCallFrame);
-
if (callFrame->hadException())
return 0;
ASSERT(result.isObject());
return checkedReturn(asObject(result));
}
-CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionExecutable, CallFrame* callFrame, JSFunction* function, int argumentCountIncludingThis, JSScope* scope)
+CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionExecutable, CallFrame* callFrame, ProtoCallFrame* protoCallFrame, JSFunction* function, int argumentCountIncludingThis, JSScope* scope, JSValue* args)
{
VM& vm = *scope->vm();
- ASSERT(!vm.exception);
+ ASSERT(!vm.exception());
if (vm.isCollectorBusy())
return CallFrameClosure();
- StackStats::CheckPoint stackCheckPoint;
- const VMStackBounds vmStackBounds(vm, wtfThreadData().stack());
- if (!vmStackBounds.isSafeToRecurse()) {
- throwStackOverflowError(callFrame);
- return CallFrameClosure();
- }
-
// Compile the callee:
- JSObject* error = functionExecutable->compileForCall(callFrame, scope);
+ JSObject* error = functionExecutable->prepareForExecution(callFrame, function, scope, CodeForCall);
if (error) {
- throwError(callFrame, error);
+ callFrame->vm().throwException(callFrame, error);
return CallFrameClosure();
}
- CodeBlock* newCodeBlock = &functionExecutable->generatedBytecodeForCall();
+ CodeBlock* newCodeBlock = functionExecutable->codeBlockForCall();
+ newCodeBlock->m_shouldAlwaysBeInlined = false;
size_t argsCount = argumentCountIncludingThis;
- CallFrame* newCallFrame = m_stack.pushFrame(callFrame, newCodeBlock, scope, argsCount, function);
- if (UNLIKELY(!newCallFrame)) {
- throwStackOverflowError(callFrame);
- return CallFrameClosure();
- }
-
- if (UNLIKELY(!newCallFrame)) {
- throwStackOverflowError(callFrame);
- return CallFrameClosure();
- }
-
+ protoCallFrame->init(newCodeBlock, function, jsUndefined(), argsCount, args);
// Return the successful closure:
- CallFrameClosure result = { callFrame, newCallFrame, function, functionExecutable, &vm, scope, newCodeBlock->numParameters(), argumentCountIncludingThis };
+ CallFrameClosure result = { callFrame, protoCallFrame, function, functionExecutable, &vm, scope, newCodeBlock->numParameters(), argumentCountIncludingThis };
return result;
}
@@ -1165,253 +1150,226 @@ JSValue Interpreter::execute(CallFrameClosure& closure)
SamplingScope samplingScope(this);
ASSERT(!vm.isCollectorBusy());
+ RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock());
if (vm.isCollectorBusy())
return jsNull();
StackStats::CheckPoint stackCheckPoint;
- m_stack.validateFence(closure.newCallFrame, "BEFORE");
- closure.resetCallFrame();
- m_stack.validateFence(closure.newCallFrame, "STEP 1");
if (LegacyProfiler* profiler = vm.enabledProfiler())
profiler->willExecute(closure.oldCallFrame, closure.function);
- if (UNLIKELY(vm.watchdog.didFire(closure.oldCallFrame)))
+ if (UNLIKELY(vm.shouldTriggerTermination(closure.oldCallFrame)))
return throwTerminatedExecutionException(closure.oldCallFrame);
- // The code execution below may push more frames and point the topCallFrame
- // to those newer frames, or it may pop to the top frame to the caller of
- // the current repeat frame, or it may leave the top frame pointing to the
- // current repeat frame.
- //
- // Hence, we need to preserve the topCallFrame here ourselves before
- // repeating this call on a second callback function.
-
- TopCallFrameSetter topCallFrame(vm, closure.newCallFrame);
-
// Execute the code:
JSValue result;
{
SamplingTool::CallRecord callRecord(m_sampler.get());
- Watchdog::Scope watchdogScope(vm.watchdog);
-
-#if ENABLE(LLINT_C_LOOP)
- result = LLInt::CLoop::execute(closure.newCallFrame, llint_function_for_call_prologue);
-#elif ENABLE(JIT)
- result = closure.functionExecutable->generatedJITCodeForCall().execute(&m_stack, closure.newCallFrame, &vm);
-#endif // ENABLE(JIT)
+ result = closure.functionExecutable->generatedJITCodeForCall()->execute(&vm, closure.protoCallFrame);
}
if (LegacyProfiler* profiler = vm.enabledProfiler())
profiler->didExecute(closure.oldCallFrame, closure.function);
- m_stack.validateFence(closure.newCallFrame, "AFTER");
return checkedReturn(result);
}
-void Interpreter::endRepeatCall(CallFrameClosure& closure)
-{
- m_stack.popFrame(closure.newCallFrame);
-}
-
JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue thisValue, JSScope* scope)
{
VM& vm = *scope->vm();
SamplingScope samplingScope(this);
ASSERT(scope->vm() == &callFrame->vm());
- ASSERT(isValidThisObject(thisValue, callFrame));
- ASSERT(!vm.exception);
+ ASSERT(!vm.exception());
ASSERT(!vm.isCollectorBusy());
+ RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock());
if (vm.isCollectorBusy())
return jsNull();
- DynamicGlobalObjectScope globalObjectScope(vm, scope->globalObject());
+ VMEntryScope entryScope(vm, scope->globalObject());
+ if (!vm.isSafeToRecurse())
+ return checkedReturn(throwStackOverflowError(callFrame));
- StackStats::CheckPoint stackCheckPoint;
- const VMStackBounds vmStackBounds(vm, wtfThreadData().stack());
- if (!vmStackBounds.isSafeToRecurse())
- return checkedReturn(throwStackOverflowError(callFrame));
+ unsigned numVariables = eval->numVariables();
+ int numFunctions = eval->numberOfFunctionDecls();
- // Compile the callee:
- JSObject* compileError = eval->compile(callFrame, scope);
+ JSScope* variableObject;
+ if ((numVariables || numFunctions) && eval->isStrictMode()) {
+ scope = StrictEvalActivation::create(callFrame, scope);
+ variableObject = scope;
+ } else {
+ for (JSScope* node = scope; ; node = node->next()) {
+ RELEASE_ASSERT(node);
+ if (node->isGlobalObject()) {
+ variableObject = node;
+ break;
+ }
+ if (JSLexicalEnvironment* lexicalEnvironment = jsDynamicCast<JSLexicalEnvironment*>(node)) {
+ if (lexicalEnvironment->symbolTable()->scopeType() == SymbolTable::ScopeType::VarScope) {
+ variableObject = node;
+ break;
+ }
+ }
+ }
+ }
+
+ JSObject* compileError = eval->prepareForExecution(callFrame, nullptr, scope, CodeForCall);
if (UNLIKELY(!!compileError))
- return checkedReturn(throwError(callFrame, compileError));
- EvalCodeBlock* codeBlock = &eval->generatedBytecode();
-
- JSObject* variableObject;
- for (JSScope* node = scope; ; node = node->next()) {
- RELEASE_ASSERT(node);
- if (node->isVariableObject() && !node->isNameScopeObject()) {
- variableObject = node;
- break;
+ return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
+ EvalCodeBlock* codeBlock = eval->codeBlock();
+
+ // We can't declare a "var"/"function" that overwrites a global "let"/"const"/"class" in a sloppy-mode eval.
+ if (variableObject->isGlobalObject() && !eval->isStrictMode() && (numVariables || numFunctions)) {
+ JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast<JSGlobalObject*>(variableObject)->globalLexicalEnvironment();
+ for (unsigned i = 0; i < numVariables; ++i) {
+ const Identifier& ident = codeBlock->variable(i);
+ PropertySlot slot(globalLexicalEnvironment, PropertySlot::InternalMethodType::VMInquiry);
+ if (JSGlobalLexicalEnvironment::getOwnPropertySlot(globalLexicalEnvironment, callFrame, ident, slot)) {
+ return checkedReturn(callFrame->vm().throwException(callFrame,
+ createTypeError(callFrame, makeString("Can't create duplicate global variable in eval: '", String(ident.impl()), "'"))));
+ }
+ }
+
+ for (int i = 0; i < numFunctions; ++i) {
+ FunctionExecutable* function = codeBlock->functionDecl(i);
+ PropertySlot slot(globalLexicalEnvironment, PropertySlot::InternalMethodType::VMInquiry);
+ if (JSGlobalLexicalEnvironment::getOwnPropertySlot(globalLexicalEnvironment, callFrame, function->name(), slot)) {
+ return checkedReturn(callFrame->vm().throwException(callFrame,
+ createTypeError(callFrame, makeString("Can't create duplicate global variable in eval: '", String(function->name().impl()), "'"))));
+ }
}
}
- unsigned numVariables = codeBlock->numVariables();
- int numFunctions = codeBlock->numberOfFunctionDecls();
if (numVariables || numFunctions) {
- if (codeBlock->isStrictMode()) {
- scope = StrictEvalActivation::create(callFrame);
- variableObject = scope;
- }
- // Scope for BatchedTransitionOptimizer
BatchedTransitionOptimizer optimizer(vm, variableObject);
+ if (variableObject->next())
+ variableObject->globalObject()->varInjectionWatchpoint()->fireAll("Executed eval, fired VarInjection watchpoint");
for (unsigned i = 0; i < numVariables; ++i) {
const Identifier& ident = codeBlock->variable(i);
if (!variableObject->hasProperty(callFrame, ident)) {
- PutPropertySlot slot;
+ PutPropertySlot slot(variableObject);
variableObject->methodTable()->put(variableObject, callFrame, ident, jsUndefined(), slot);
}
}
for (int i = 0; i < numFunctions; ++i) {
FunctionExecutable* function = codeBlock->functionDecl(i);
- PutPropertySlot slot;
- variableObject->methodTable()->put(variableObject, callFrame, function->name(), JSFunction::create(callFrame, function, scope), slot);
+ PutPropertySlot slot(variableObject);
+ variableObject->methodTable()->put(variableObject, callFrame, function->name(), JSFunction::create(vm, function, scope), slot);
}
}
- if (UNLIKELY(vm.watchdog.didFire(callFrame)))
+ if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
return throwTerminatedExecutionException(callFrame);
- // Push the frame:
ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'.
- CallFrame* newCallFrame = m_stack.pushFrame(callFrame, codeBlock, scope, 1, 0);
- if (UNLIKELY(!newCallFrame))
- return checkedReturn(throwStackOverflowError(callFrame));
- // Set the arguments for the callee:
- newCallFrame->setThisValue(thisValue);
+ ProtoCallFrame protoCallFrame;
+ protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), thisValue, 1);
if (LegacyProfiler* profiler = vm.enabledProfiler())
- profiler->willExecute(callFrame, eval->sourceURL(), eval->lineNo());
+ profiler->willExecute(callFrame, eval->sourceURL(), eval->firstLine(), eval->startColumn());
// Execute the code:
JSValue result;
{
SamplingTool::CallRecord callRecord(m_sampler.get());
- Watchdog::Scope watchdogScope(vm.watchdog);
-
-#if ENABLE(LLINT_C_LOOP)
- result = LLInt::CLoop::execute(newCallFrame, llint_eval_prologue);
-#elif ENABLE(JIT)
- result = eval->generatedJITCode().execute(&m_stack, newCallFrame, &vm);
-#endif // ENABLE(JIT)
+ result = eval->generatedJITCode()->execute(&vm, &protoCallFrame);
}
if (LegacyProfiler* profiler = vm.enabledProfiler())
- profiler->didExecute(callFrame, eval->sourceURL(), eval->lineNo());
+ profiler->didExecute(callFrame, eval->sourceURL(), eval->firstLine(), eval->startColumn());
- m_stack.popFrame(newCallFrame);
return checkedReturn(result);
}
-NEVER_INLINE void Interpreter::debug(CallFrame* callFrame, DebugHookID debugHookID, int firstLine, int lastLine, int column)
+JSValue Interpreter::execute(ModuleProgramExecutable* executable, CallFrame* callFrame, JSModuleEnvironment* scope)
{
- Debugger* debugger = callFrame->dynamicGlobalObject()->debugger();
- if (!debugger)
- return;
+ VM& vm = *scope->vm();
+ SamplingScope samplingScope(this);
- switch (debugHookID) {
- case DidEnterCallFrame:
- debugger->callEvent(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), firstLine, column);
- return;
- case WillLeaveCallFrame:
- debugger->returnEvent(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), lastLine, column);
- return;
- case WillExecuteStatement:
- debugger->atStatement(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), firstLine, column);
- return;
- case WillExecuteProgram:
- debugger->willExecuteProgram(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), firstLine, column);
- return;
- case DidExecuteProgram:
- debugger->didExecuteProgram(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), lastLine, column);
- return;
- case DidReachBreakpoint:
- debugger->didReachBreakpoint(callFrame, callFrame->codeBlock()->ownerExecutable()->sourceID(), lastLine, column);
- return;
- }
-}
-
-JSValue Interpreter::retrieveArgumentsFromVMCode(CallFrame* callFrame, JSFunction* function) const
-{
- CallFrame* functionCallFrame = findFunctionCallFrameFromVMCode(callFrame, function);
- if (!functionCallFrame)
+ ASSERT(scope->vm() == &callFrame->vm());
+ ASSERT(!vm.exception());
+ ASSERT(!vm.isCollectorBusy());
+ RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock());
+ if (vm.isCollectorBusy())
return jsNull();
- Arguments* arguments = Arguments::create(functionCallFrame->vm(), functionCallFrame);
- arguments->tearOff(functionCallFrame);
- return JSValue(arguments);
-}
+ VMEntryScope entryScope(vm, scope->globalObject());
+ if (!vm.isSafeToRecurse())
+ return checkedReturn(throwStackOverflowError(callFrame));
-JSValue Interpreter::retrieveCallerFromVMCode(CallFrame* callFrame, JSFunction* function) const
-{
- CallFrame* functionCallFrame = findFunctionCallFrameFromVMCode(callFrame, function);
+ JSObject* compileError = executable->prepareForExecution(callFrame, nullptr, scope, CodeForCall);
+ if (UNLIKELY(!!compileError))
+ return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
+ ModuleProgramCodeBlock* codeBlock = executable->codeBlock();
- if (!functionCallFrame)
- return jsNull();
-
- unsigned bytecodeOffset;
- CodeBlock* unusedCallerCodeBlock = 0;
- CallFrame* callerFrame = getCallerInfo(&callFrame->vm(), functionCallFrame, bytecodeOffset, unusedCallerCodeBlock);
- if (!callerFrame)
- return jsNull();
- JSValue caller = callerFrame->callee();
- if (!caller)
- return jsNull();
+ if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
+ return throwTerminatedExecutionException(callFrame);
+
+ ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'.
+
+ // The |this| of the module is always `undefined`.
+ // http://www.ecma-international.org/ecma-262/6.0/#sec-module-environment-records-hasthisbinding
+ // http://www.ecma-international.org/ecma-262/6.0/#sec-module-environment-records-getthisbinding
+ ProtoCallFrame protoCallFrame;
+ protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), jsUndefined(), 1);
+
+ if (LegacyProfiler* profiler = vm.enabledProfiler())
+ profiler->willExecute(callFrame, executable->sourceURL(), executable->firstLine(), executable->startColumn());
- // Skip over function bindings.
- ASSERT(caller.isObject());
- while (asObject(caller)->inherits(&JSBoundFunction::s_info)) {
- callerFrame = getCallerInfo(&callFrame->vm(), callerFrame, bytecodeOffset, unusedCallerCodeBlock);
- if (!callerFrame)
- return jsNull();
- caller = callerFrame->callee();
- if (!caller)
- return jsNull();
+ // Execute the code:
+ JSValue result;
+ {
+ SamplingTool::CallRecord callRecord(m_sampler.get());
+ result = executable->generatedJITCode()->execute(&vm, &protoCallFrame);
}
- return caller;
+ if (LegacyProfiler* profiler = vm.enabledProfiler())
+ profiler->didExecute(callFrame, executable->sourceURL(), executable->firstLine(), executable->startColumn());
+
+ return checkedReturn(result);
}
-void Interpreter::retrieveLastCaller(CallFrame* callFrame, int& lineNumber, intptr_t& sourceID, String& sourceURL, JSValue& function) const
+NEVER_INLINE void Interpreter::debug(CallFrame* callFrame, DebugHookID debugHookID)
{
- function = JSValue();
- lineNumber = -1;
- sourceURL = String();
-
- CallFrame* callerFrame = callFrame->callerFrame();
- if (callerFrame->hasHostCallFrameFlag())
+ Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger();
+ if (!debugger)
return;
- CodeBlock* callerCodeBlock = callerFrame->codeBlock();
- if (!callerCodeBlock)
- return;
- unsigned bytecodeOffset = 0;
- bytecodeOffset = callerCodeBlock->bytecodeOffset(callerFrame, callFrame->returnPC());
- lineNumber = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset - 1);
- sourceID = callerCodeBlock->ownerExecutable()->sourceID();
- sourceURL = callerCodeBlock->ownerExecutable()->sourceURL();
- function = callerFrame->callee();
-}
+ ASSERT(callFrame->codeBlock()->hasDebuggerRequests());
+ ASSERT(!callFrame->hadException());
-CallFrame* Interpreter::findFunctionCallFrameFromVMCode(CallFrame* callFrame, JSFunction* function)
-{
- for (CallFrame* candidate = callFrame->trueCallFrameFromVMCode(); candidate; candidate = candidate->trueCallerFrame()) {
- if (candidate->callee() == function)
- return candidate;
+ switch (debugHookID) {
+ case DidEnterCallFrame:
+ debugger->callEvent(callFrame);
+ break;
+ case WillLeaveCallFrame:
+ debugger->returnEvent(callFrame);
+ break;
+ case WillExecuteStatement:
+ debugger->atStatement(callFrame);
+ break;
+ case WillExecuteProgram:
+ debugger->willExecuteProgram(callFrame);
+ break;
+ case DidExecuteProgram:
+ debugger->didExecuteProgram(callFrame);
+ break;
+ case DidReachBreakpoint:
+ debugger->didReachBreakpoint(callFrame);
+ break;
}
- return 0;
-}
+ ASSERT(!callFrame->hadException());
+}
void Interpreter::enableSampler()
{
#if ENABLE(OPCODE_SAMPLING)
if (!m_sampler) {
- m_sampler = adoptPtr(new SamplingTool(this));
+ m_sampler = std::make_unique<SamplingTool>(this);
m_sampler->setup();
}
#endif
diff --git a/Source/JavaScriptCore/interpreter/Interpreter.h b/Source/JavaScriptCore/interpreter/Interpreter.h
index e5eca0e1c..aba745f17 100644
--- a/Source/JavaScriptCore/interpreter/Interpreter.h
+++ b/Source/JavaScriptCore/interpreter/Interpreter.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2013, 2015 Apple Inc. All rights reserved.
* Copyright (C) 2012 Research In Motion Limited. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,7 +11,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -33,11 +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>
@@ -49,16 +50,23 @@ namespace JSC {
class ExecutableBase;
class FunctionExecutable;
class VM;
+ class JSFunction;
class JSGlobalObject;
+ class JSModuleEnvironment;
+ class JSModuleRecord;
class LLIntOffsetsExtractor;
class ProgramExecutable;
+ class ModuleProgramExecutable;
class Register;
class JSScope;
class SamplingTool;
struct CallFrameClosure;
struct HandlerInfo;
struct Instruction;
-
+ struct ProtoCallFrame;
+
+ enum UnwindStart { UnwindFromCurrentFrame, UnwindFromCallerFrame };
+
enum DebugHookID {
WillExecuteProgram,
DidExecuteProgram,
@@ -71,6 +79,7 @@ namespace JSC {
enum StackFrameCodeType {
StackFrameGlobalCode,
StackFrameEvalCode,
+ StackFrameModuleCode,
StackFrameFunctionCode,
StackFrameNativeCode
};
@@ -78,7 +87,7 @@ namespace JSC {
struct StackFrame {
Strong<JSObject> callee;
StackFrameCodeType codeType;
- Strong<ExecutableBase> executable;
+ Strong<ScriptExecutable> executable;
Strong<UnlinkedCodeBlock> codeBlock;
RefPtr<SourceProvider> code;
int lineOffset;
@@ -87,58 +96,38 @@ namespace JSC {
unsigned bytecodeOffset;
String sourceURL;
JS_EXPORT_PRIVATE String toString(CallFrame*);
- String friendlySourceURL() const
- {
- String traceLine;
-
- switch (codeType) {
- case StackFrameEvalCode:
- case StackFrameFunctionCode:
- case StackFrameGlobalCode:
- if (!sourceURL.isEmpty())
- traceLine = sourceURL.impl();
- break;
- case StackFrameNativeCode:
- traceLine = "[native code]";
- break;
- }
- return traceLine.isNull() ? emptyString() : traceLine;
- }
- String friendlyFunctionName(CallFrame* callFrame) const
- {
- String traceLine;
- JSObject* stackFrameCallee = callee.get();
-
- switch (codeType) {
- case StackFrameEvalCode:
- traceLine = "eval code";
- break;
- case StackFrameNativeCode:
- if (callee)
- traceLine = getCalculatedDisplayName(callFrame, stackFrameCallee).impl();
- break;
- case StackFrameFunctionCode:
- traceLine = getCalculatedDisplayName(callFrame, stackFrameCallee).impl();
- break;
- case StackFrameGlobalCode:
- traceLine = "global code";
- break;
- }
- return traceLine.isNull() ? emptyString() : traceLine;
- }
+ String friendlySourceURL() const;
+ String friendlyFunctionName(CallFrame*) const;
JS_EXPORT_PRIVATE void computeLineAndColumn(unsigned& line, unsigned& column);
private:
void expressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column);
};
+ class SuspendExceptionScope {
+ public:
+ SuspendExceptionScope(VM* vm)
+ : m_vm(vm)
+ {
+ oldException = vm->exception();
+ vm->clearException();
+ }
+ ~SuspendExceptionScope()
+ {
+ m_vm->restorePreviousException(oldException);
+ }
+ private:
+ Exception* oldException;
+ VM* m_vm;
+ };
+
class TopCallFrameSetter {
public:
- TopCallFrameSetter(VM& global, CallFrame* callFrame)
- : vm(global)
- , oldCallFrame(global.topCallFrame)
+ TopCallFrameSetter(VM& currentVM, CallFrame* callFrame)
+ : vm(currentVM)
+ , oldCallFrame(currentVM.topCallFrame)
{
- global.topCallFrame = callFrame;
+ currentVM.topCallFrame = callFrame;
}
~TopCallFrameSetter()
@@ -152,33 +141,52 @@ namespace JSC {
class NativeCallFrameTracer {
public:
- ALWAYS_INLINE NativeCallFrameTracer(VM* global, CallFrame* callFrame)
+ ALWAYS_INLINE NativeCallFrameTracer(VM* vm, CallFrame* callFrame)
{
- ASSERT(global);
+ ASSERT(vm);
ASSERT(callFrame);
- global->topCallFrame = callFrame;
+ ASSERT(callFrame < vm->topVMEntryFrame);
+ vm->topCallFrame = callFrame;
}
};
+ class NativeCallFrameTracerWithRestore {
+ public:
+ ALWAYS_INLINE NativeCallFrameTracerWithRestore(VM* vm, VMEntryFrame* vmEntryFrame, CallFrame* callFrame)
+ : m_vm(vm)
+ {
+ ASSERT(vm);
+ ASSERT(callFrame);
+ m_savedTopVMEntryFrame = vm->topVMEntryFrame;
+ m_savedTopCallFrame = vm->topCallFrame;
+ vm->topVMEntryFrame = vmEntryFrame;
+ vm->topCallFrame = callFrame;
+ }
+
+ ALWAYS_INLINE ~NativeCallFrameTracerWithRestore()
+ {
+ m_vm->topVMEntryFrame = m_savedTopVMEntryFrame;
+ m_vm->topCallFrame = m_savedTopCallFrame;
+ }
+
+ private:
+ VM* m_vm;
+ VMEntryFrame* m_savedTopVMEntryFrame;
+ CallFrame* m_savedTopCallFrame;
+ };
+
class Interpreter {
WTF_MAKE_FAST_ALLOCATED;
friend class CachedCall;
friend class LLIntOffsetsExtractor;
friend class JIT;
+ friend class VM;
public:
- class ErrorHandlingMode {
- public:
- JS_EXPORT_PRIVATE ErrorHandlingMode(ExecState*);
- JS_EXPORT_PRIVATE ~ErrorHandlingMode();
- private:
- Interpreter& m_interpreter;
- };
-
Interpreter(VM &);
~Interpreter();
- void initialize(bool canUseJIT);
+ void initialize();
JSStack& stack() { return m_stack; }
@@ -195,7 +203,7 @@ namespace JSC {
OpcodeID getOpcodeID(Opcode opcode)
{
ASSERT(m_initialized);
-#if ENABLE(COMPUTED_GOTO_OPCODES) && ENABLE(LLINT)
+#if ENABLE(COMPUTED_GOTO_OPCODES)
ASSERT(isOpcode(opcode));
return m_opcodeIDTable.get(opcode);
#else
@@ -207,24 +215,23 @@ namespace JSC {
JSValue execute(ProgramExecutable*, CallFrame*, JSObject* thisObj);
JSValue executeCall(CallFrame*, JSObject* function, CallType, const CallData&, JSValue thisValue, const ArgList&);
- JSObject* executeConstruct(CallFrame*, JSObject* function, ConstructType, const ConstructData&, const ArgList&);
+ JSObject* executeConstruct(CallFrame*, JSObject* function, ConstructType, const ConstructData&, const ArgList&, JSValue newTarget);
JSValue execute(EvalExecutable*, CallFrame*, JSValue thisValue, JSScope*);
+ JSValue execute(ModuleProgramExecutable*, CallFrame*, JSModuleEnvironment*);
- JSValue retrieveArgumentsFromVMCode(CallFrame*, JSFunction*) const;
- JSValue retrieveCallerFromVMCode(CallFrame*, JSFunction*) const;
- JS_EXPORT_PRIVATE void retrieveLastCaller(CallFrame*, int& lineNumber, intptr_t& sourceID, String& sourceURL, JSValue& function) const;
-
void getArgumentsData(CallFrame*, JSFunction*&, ptrdiff_t& firstParameterIndex, Register*& argv, int& argc);
SamplingTool* sampler() { return m_sampler.get(); }
- bool isInErrorHandlingMode() { return m_errorHandlingModeReentry; }
+ NEVER_INLINE HandlerInfo* unwind(VM&, CallFrame*&, Exception*, UnwindStart);
+ void notifyDebuggerOfExceptionToBeThrown(CallFrame*, Exception*);
+ NEVER_INLINE void debug(CallFrame*, DebugHookID);
+ JSString* stackTraceAsString(ExecState*, Vector<StackFrame>);
- NEVER_INLINE HandlerInfo* throwException(CallFrame*&, JSValue&, unsigned bytecodeOffset);
- NEVER_INLINE void debug(CallFrame*, DebugHookID, int firstLine, int lastLine, int column);
- static const String getTraceLine(CallFrame*, StackFrameCodeType, const String&, int);
- JS_EXPORT_PRIVATE static void getStackTrace(VM*, Vector<StackFrame>& results, size_t maxStackSize = std::numeric_limits<size_t>::max());
- static void addStackTraceIfNecessary(CallFrame*, JSValue error);
+ static EncodedJSValue JSC_HOST_CALL constructWithErrorConstructor(ExecState*);
+ static EncodedJSValue JSC_HOST_CALL callErrorConstructor(ExecState*);
+ static EncodedJSValue JSC_HOST_CALL constructWithNativeErrorConstructor(ExecState*);
+ static EncodedJSValue JSC_HOST_CALL callNativeErrorConstructor(ExecState*);
void dumpSampleData(ExecState* exec);
void startSampling();
@@ -232,29 +239,30 @@ 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 };
- CallFrameClosure prepareForRepeatCall(FunctionExecutable*, CallFrame*, JSFunction*, int argumentCountIncludingThis, JSScope*);
- void endRepeatCall(CallFrameClosure&);
+ CallFrameClosure prepareForRepeatCall(FunctionExecutable*, CallFrame*, ProtoCallFrame*, JSFunction*, int argumentCountIncludingThis, JSScope*, JSValue*);
+
JSValue execute(CallFrameClosure&);
- NEVER_INLINE bool unwindCallFrame(CallFrame*&, JSValue, unsigned& bytecodeOffset, CodeBlock*&);
- static CallFrame* findFunctionCallFrameFromVMCode(CallFrame*, JSFunction*);
void dumpRegisters(CallFrame*);
- bool isCallBytecode(Opcode opcode) { return opcode == getOpcode(op_call) || opcode == getOpcode(op_construct) || opcode == getOpcode(op_call_eval); }
+ bool isCallBytecode(Opcode opcode) { return opcode == getOpcode(op_call) || opcode == getOpcode(op_construct) || opcode == getOpcode(op_call_eval) || opcode == getOpcode(op_tail_call); }
void enableSampler();
int m_sampleEntryDepth;
- OwnPtr<SamplingTool> m_sampler;
+ std::unique_ptr<SamplingTool> m_sampler;
+ VM& m_vm;
JSStack m_stack;
int m_errorHandlingModeReentry;
-#if ENABLE(COMPUTED_GOTO_OPCODES) && ENABLE(LLINT)
+#if ENABLE(COMPUTED_GOTO_OPCODES)
Opcode* m_opcodeTable; // Maps OpcodeID => Opcode for compiling
HashMap<Opcode, OpcodeID> m_opcodeIDTable; // Maps Opcode => OpcodeID for decompiling
#endif
@@ -264,15 +272,30 @@ namespace JSC {
#endif
};
- // This value must not be an object that would require this conversion (WebCore's global object).
- inline bool isValidThisObject(JSValue thisValue, ExecState* exec)
+ JSValue eval(CallFrame*);
+
+ inline CallFrame* calleeFrameForVarargs(CallFrame* callFrame, unsigned numUsedStackSlots, unsigned argumentCountIncludingThis)
{
- return !thisValue.isObject() || thisValue.toThisObject(exec) == thisValue;
+ // We want the new frame to be allocated on a stack aligned offset with a stack
+ // aligned size. Align the size here.
+ argumentCountIncludingThis = WTF::roundUpToMultipleOf(
+ stackAlignmentRegisters(),
+ argumentCountIncludingThis + JSStack::CallFrameHeaderSize) - JSStack::CallFrameHeaderSize;
+
+ // Align the frame offset here.
+ unsigned paddedCalleeFrameOffset = WTF::roundUpToMultipleOf(
+ stackAlignmentRegisters(),
+ numUsedStackSlots + argumentCountIncludingThis + JSStack::CallFrameHeaderSize);
+ return CallFrame::create(callFrame->registers() - paddedCalleeFrameOffset);
}
- JSValue eval(CallFrame*);
- CallFrame* loadVarargs(CallFrame*, JSStack*, JSValue thisValue, JSValue arguments, int firstFreeRegister);
-
+ unsigned sizeOfVarargs(CallFrame* exec, JSValue arguments, uint32_t firstVarArgOffset);
+ static const unsigned maxArguments = 0x10000;
+ unsigned sizeFrameForVarargs(CallFrame* exec, JSStack*, JSValue arguments, unsigned numUsedStackSlots, uint32_t firstVarArgOffset);
+ void loadVarargs(CallFrame* execCaller, VirtualRegister firstElementDest, JSValue source, uint32_t offset, uint32_t length);
+ void setupVarargsFrame(CallFrame* execCaller, CallFrame* execCallee, JSValue arguments, uint32_t firstVarArgOffset, uint32_t length);
+ void setupVarargsFrameAndSetThis(CallFrame* execCaller, CallFrame* execCallee, JSValue thisValue, JSValue arguments, uint32_t firstVarArgOffset, uint32_t length);
+
} // namespace JSC
#endif // Interpreter_h
diff --git a/Source/JavaScriptCore/interpreter/JSStack.cpp b/Source/JavaScriptCore/interpreter/JSStack.cpp
index ec2962a92..6e441dfd5 100644
--- a/Source/JavaScriptCore/interpreter/JSStack.cpp
+++ b/Source/JavaScriptCore/interpreter/JSStack.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2013, 2014, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -27,125 +27,158 @@
*/
#include "config.h"
-#include "JSStack.h"
#include "JSStackInlines.h"
#include "ConservativeRoots.h"
#include "Interpreter.h"
+#include "JSCInlines.h"
+#include "Options.h"
+#include <wtf/Lock.h>
namespace JSC {
+#if !ENABLE(JIT)
static size_t committedBytesCount = 0;
-static Mutex& stackStatisticsMutex()
+static size_t commitSize()
{
- DEFINE_STATIC_LOCAL(Mutex, staticMutex, ());
- return staticMutex;
-}
+ static size_t size = std::max<size_t>(16 * 1024, pageSize());
+ return size;
+}
+
+static StaticLock stackStatisticsMutex;
+#endif // !ENABLE(JIT)
-JSStack::JSStack(VM& vm, size_t capacity)
- : m_end(0)
+JSStack::JSStack(VM& vm)
+ : m_vm(vm)
, m_topCallFrame(vm.topCallFrame)
+#if !ENABLE(JIT)
+ , m_end(0)
+ , m_reservedZoneSizeInRegisters(0)
+#endif
{
+#if !ENABLE(JIT)
+ size_t capacity = Options::maxPerThreadStackUsage();
ASSERT(capacity && isPageAligned(capacity));
- m_reservation = PageReservation::reserve(roundUpAllocationSize(capacity * sizeof(Register), commitSize), OSAllocator::JSVMStackPages);
- m_end = static_cast<Register*>(m_reservation.base());
- m_commitEnd = static_cast<Register*>(m_reservation.base());
-
- disableErrorStackReserve();
+ m_reservation = PageReservation::reserve(WTF::roundUpToMultipleOf(commitSize(), capacity), OSAllocator::JSVMStackPages);
+ setStackLimit(highAddress());
+ m_commitTop = highAddress();
+
+ m_lastStackTop = baseOfStack();
+#endif // !ENABLE(JIT)
m_topCallFrame = 0;
}
+#if !ENABLE(JIT)
JSStack::~JSStack()
{
- void* base = m_reservation.base();
- m_reservation.decommit(base, reinterpret_cast<intptr_t>(m_commitEnd) - reinterpret_cast<intptr_t>(base));
- addToCommittedByteCount(-(reinterpret_cast<intptr_t>(m_commitEnd) - reinterpret_cast<intptr_t>(base)));
+ ptrdiff_t sizeToDecommit = reinterpret_cast<char*>(highAddress()) - reinterpret_cast<char*>(m_commitTop);
+ m_reservation.decommit(reinterpret_cast<void*>(m_commitTop), sizeToDecommit);
+ addToCommittedByteCount(-sizeToDecommit);
m_reservation.deallocate();
}
-bool JSStack::growSlowCase(Register* newEnd)
+bool JSStack::growSlowCase(Register* newTopOfStack)
{
+ Register* newTopOfStackWithReservedZone = newTopOfStack - m_reservedZoneSizeInRegisters;
+
// If we have already committed enough memory to satisfy this request,
// just update the end pointer and return.
- if (newEnd <= m_commitEnd) {
- m_end = newEnd;
+ if (newTopOfStackWithReservedZone >= m_commitTop) {
+ setStackLimit(newTopOfStack);
return true;
}
// Compute the chunk size of additional memory to commit, and see if we
// have it is still within our budget. If not, we'll fail to grow and
// return false.
- long delta = roundUpAllocationSize(reinterpret_cast<char*>(newEnd) - reinterpret_cast<char*>(m_commitEnd), commitSize);
- if (reinterpret_cast<char*>(m_commitEnd) + delta > reinterpret_cast<char*>(m_useableEnd))
+ ptrdiff_t delta = reinterpret_cast<char*>(m_commitTop) - reinterpret_cast<char*>(newTopOfStackWithReservedZone);
+ delta = WTF::roundUpToMultipleOf(commitSize(), delta);
+ Register* newCommitTop = m_commitTop - (delta / sizeof(Register));
+ if (newCommitTop < reservationTop())
return false;
- // Otherwise, the growth is still within our budget. Go ahead and commit
- // it and return true.
- m_reservation.commit(m_commitEnd, delta);
+ // Otherwise, the growth is still within our budget. Commit it and return true.
+ m_reservation.commit(newCommitTop, delta);
addToCommittedByteCount(delta);
- m_commitEnd = reinterpret_cast_ptr<Register*>(reinterpret_cast<char*>(m_commitEnd) + delta);
- m_end = newEnd;
+ m_commitTop = newCommitTop;
+ setStackLimit(newTopOfStack);
return true;
}
-void JSStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots)
+void JSStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks)
{
- conservativeRoots.add(begin(), getTopOfStack());
+ conservativeRoots.add(topOfStack() + 1, highAddress(), jitStubRoutines, codeBlocks);
}
-void JSStack::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, DFGCodeBlocks& dfgCodeBlocks)
+void JSStack::sanitizeStack()
{
- conservativeRoots.add(begin(), getTopOfStack(), jitStubRoutines, dfgCodeBlocks);
+#if !ASAN_ENABLED
+ ASSERT(topOfStack() <= baseOfStack());
+
+ if (m_lastStackTop < topOfStack()) {
+ char* begin = reinterpret_cast<char*>(m_lastStackTop + 1);
+ char* end = reinterpret_cast<char*>(topOfStack() + 1);
+ memset(begin, 0, end - begin);
+ }
+
+ m_lastStackTop = topOfStack();
+#endif
}
void JSStack::releaseExcessCapacity()
{
- ptrdiff_t delta = reinterpret_cast<uintptr_t>(m_commitEnd) - reinterpret_cast<uintptr_t>(m_reservation.base());
- m_reservation.decommit(m_reservation.base(), delta);
+ Register* highAddressWithReservedZone = highAddress() - m_reservedZoneSizeInRegisters;
+ ptrdiff_t delta = reinterpret_cast<char*>(highAddressWithReservedZone) - reinterpret_cast<char*>(m_commitTop);
+ m_reservation.decommit(m_commitTop, delta);
addToCommittedByteCount(-delta);
- m_commitEnd = static_cast<Register*>(m_reservation.base());
+ m_commitTop = highAddressWithReservedZone;
}
-void JSStack::initializeThreading()
+void JSStack::addToCommittedByteCount(long byteCount)
{
- stackStatisticsMutex();
+ LockHolder locker(stackStatisticsMutex);
+ ASSERT(static_cast<long>(committedBytesCount) + byteCount > -1);
+ committedBytesCount += byteCount;
}
-size_t JSStack::committedByteCount()
+void JSStack::setReservedZoneSize(size_t reservedZoneSize)
{
- MutexLocker locker(stackStatisticsMutex());
- return committedBytesCount;
+ m_reservedZoneSizeInRegisters = reservedZoneSize / sizeof(Register);
+ if (m_commitTop >= (m_end + 1) - m_reservedZoneSizeInRegisters)
+ growSlowCase(m_end + 1);
}
+#endif // !ENABLE(JIT)
-void JSStack::addToCommittedByteCount(long byteCount)
+#if ENABLE(JIT)
+Register* JSStack::lowAddress() const
{
- MutexLocker locker(stackStatisticsMutex());
- ASSERT(static_cast<long>(committedBytesCount) + byteCount > -1);
- committedBytesCount += byteCount;
+ ASSERT(wtfThreadData().stack().isGrowingDownward());
+ return reinterpret_cast<Register*>(m_vm.stackLimit());
}
-void JSStack::enableErrorStackReserve()
+Register* JSStack::highAddress() const
{
- m_useableEnd = reservationEnd();
+ ASSERT(wtfThreadData().stack().isGrowingDownward());
+ return reinterpret_cast<Register*>(wtfThreadData().stack().origin());
}
+#endif // ENABLE(JIT)
-void JSStack::disableErrorStackReserve()
+size_t JSStack::committedByteCount()
{
- char* useableEnd = reinterpret_cast<char*>(reservationEnd()) - commitSize;
- m_useableEnd = reinterpret_cast_ptr<Register*>(useableEnd);
-
- // By the time we get here, we are guaranteed to be destructing the last
- // Interpreter::ErrorHandlingMode that enabled this reserve in the first
- // place. That means the stack space beyond m_useableEnd before we
- // enabled the reserve was not previously in use. Hence, it is safe to
- // shrink back to that m_useableEnd.
- if (m_end > m_useableEnd) {
- ASSERT(m_topCallFrame->frameExtent() <= m_useableEnd);
- shrink(m_useableEnd);
- }
+#if !ENABLE(JIT)
+ LockHolder locker(stackStatisticsMutex);
+ return committedBytesCount;
+#else
+ // When using the C stack, we don't know how many stack pages are actually
+ // committed. So, we use the current stack usage as an estimate.
+ ASSERT(wtfThreadData().stack().isGrowingDownward());
+ int8_t* current = reinterpret_cast<int8_t*>(&current);
+ int8_t* high = reinterpret_cast<int8_t*>(wtfThreadData().stack().origin());
+ return high - current;
+#endif
}
} // namespace JSC
diff --git a/Source/JavaScriptCore/interpreter/JSStack.h b/Source/JavaScriptCore/interpreter/JSStack.h
index fe4012d83..770db0920 100644
--- a/Source/JavaScriptCore/interpreter/JSStack.h
+++ b/Source/JavaScriptCore/interpreter/JSStack.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2013, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -35,124 +35,117 @@
#include <wtf/PageReservation.h>
#include <wtf/VMTags.h>
-#if !defined(NDEBUG) && !defined(ENABLE_DEBUG_JSSTACK)
-#define ENABLE_DEBUG_JSSTACK 1
-#endif
-
namespace JSC {
+ class CodeBlockSet;
class ConservativeRoots;
- class DFGCodeBlocks;
class ExecState;
class JITStubRoutineSet;
class VM;
class LLIntOffsetsExtractor;
+ struct Instruction;
+ typedef ExecState CallFrame;
+
+ struct CallerFrameAndPC {
+ CallFrame* callerFrame;
+ Instruction* pc;
+ };
+
class JSStack {
WTF_MAKE_NONCOPYABLE(JSStack);
public:
enum CallFrameHeaderEntry {
- CallFrameHeaderSize = 6,
-
- ArgumentCount = -6,
- CallerFrame = -5,
- Callee = -4,
- ScopeChain = -3,
- ReturnPC = -2, // This is either an Instruction* or a pointer into JIT generated code stored as an Instruction*.
- CodeBlock = -1,
+ CallerFrameAndPCSize = sizeof(CallerFrameAndPC) / sizeof(Register),
+ CodeBlock = CallerFrameAndPCSize,
+ Callee,
+ ArgumentCount,
+ CallFrameHeaderSize,
+
+ // The following entries are not part of the CallFrameHeader but are provided here as a convenience:
+ ThisArgument = CallFrameHeaderSize,
+ FirstArgument,
};
- static const size_t defaultCapacity = 512 * 1024;
- static const size_t commitSize = 16 * 1024;
// Allow 8k of excess registers before we start trying to reap the stack
static const ptrdiff_t maxExcessCapacity = 8 * 1024;
- JSStack(VM&, size_t capacity = defaultCapacity);
- ~JSStack();
+ JSStack(VM&);
- void gatherConservativeRoots(ConservativeRoots&);
- void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, DFGCodeBlocks&);
-
- Register* begin() const { return static_cast<Register*>(m_reservation.base()); }
- Register* end() const { return m_end; }
- size_t size() const { return end() - begin(); }
+ bool ensureCapacityFor(Register* newTopOfStack);
- bool grow(Register*);
-
+ bool containsAddress(Register* address) { return (lowAddress() <= address && address < highAddress()); }
static size_t committedByteCount();
- static void initializeThreading();
- Register* const * addressOfEnd() const
- {
- return &m_end;
- }
+#if ENABLE(JIT)
+ void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&) { }
+ void sanitizeStack() { }
+#else
+ ~JSStack();
- Register* getTopOfFrame(CallFrame*);
- Register* getStartOfFrame(CallFrame*);
- Register* getTopOfStack();
+ void gatherConservativeRoots(ConservativeRoots&, JITStubRoutineSet&, CodeBlockSet&);
+ void sanitizeStack();
- CallFrame* pushFrame(CallFrame* callerFrame, class CodeBlock*,
- JSScope*, int argsCount, JSObject* callee);
+ Register* baseOfStack() const
+ {
+ return highAddress() - 1;
+ }
- void popFrame(CallFrame*);
+ size_t size() const { return highAddress() - lowAddress(); }
- void enableErrorStackReserve();
- void disableErrorStackReserve();
+ void setReservedZoneSize(size_t);
-#if ENABLE(DEBUG_JSSTACK)
- void installFence(CallFrame*, const char *function = "", int lineNo = 0);
- void validateFence(CallFrame*, const char *function = "", int lineNo = 0);
- static const int FenceSize = 4;
-#else // !ENABLE(DEBUG_JSSTACK)
- void installFence(CallFrame*, const char* = "", int = 0) { }
- void validateFence(CallFrame*, const char* = "", int = 0) { }
-#endif // !ENABLE(DEBUG_JSSTACK)
+ inline Register* topOfStack();
+#endif // ENABLE(JIT)
private:
- Register* reservationEnd() const
+
+#if !ENABLE(JIT)
+ Register* lowAddress() const
{
- char* base = static_cast<char*>(m_reservation.base());
- char* reservationEnd = base + m_reservation.size();
- return reinterpret_cast_ptr<Register*>(reservationEnd);
+ return m_end + 1;
}
-#if ENABLE(DEBUG_JSSTACK)
- static JSValue generateFenceValue(size_t argIndex);
- void installTrapsAfterFrame(CallFrame*);
+ Register* highAddress() const
+ {
+ return reinterpret_cast_ptr<Register*>(static_cast<char*>(m_reservation.base()) + m_reservation.size());
+ }
#else
- void installTrapsAfterFrame(CallFrame*) { }
-#endif
+ Register* lowAddress() const;
+ Register* highAddress() const;
+#endif // !ENABLE(JIT)
+
+#if !ENABLE(JIT)
+ inline Register* topOfFrameFor(CallFrame*);
+
+ Register* reservationTop() const
+ {
+ char* reservationTop = static_cast<char*>(m_reservation.base());
+ return reinterpret_cast_ptr<Register*>(reservationTop);
+ }
- bool growSlowCase(Register*);
- void shrink(Register*);
+ bool grow(Register* newTopOfStack);
+ bool growSlowCase(Register* newTopOfStack);
+ void shrink(Register* newTopOfStack);
void releaseExcessCapacity();
void addToCommittedByteCount(long);
+ void setStackLimit(Register* newTopOfStack);
+#endif // !ENABLE(JIT)
+
+ VM& m_vm;
+ CallFrame*& m_topCallFrame;
+#if !ENABLE(JIT)
Register* m_end;
- Register* m_commitEnd;
- Register* m_useableEnd;
+ Register* m_commitTop;
PageReservation m_reservation;
- CallFrame*& m_topCallFrame;
+ Register* m_lastStackTop;
+ ptrdiff_t m_reservedZoneSizeInRegisters;
+#endif // !ENABLE(JIT)
friend class LLIntOffsetsExtractor;
};
- inline void JSStack::shrink(Register* newEnd)
- {
- if (newEnd >= m_end)
- return;
- m_end = newEnd;
- if (m_end == m_reservation.base() && (m_commitEnd - begin()) >= maxExcessCapacity)
- releaseExcessCapacity();
- }
-
- inline bool JSStack::grow(Register* newEnd)
- {
- if (newEnd <= m_end)
- return true;
- return growSlowCase(newEnd);
- }
-
} // namespace JSC
#endif // JSStack_h
diff --git a/Source/JavaScriptCore/interpreter/JSStackInlines.h b/Source/JavaScriptCore/interpreter/JSStackInlines.h
index d316b5992..69508ab5d 100644
--- a/Source/JavaScriptCore/interpreter/JSStackInlines.h
+++ b/Source/JavaScriptCore/interpreter/JSStackInlines.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -29,202 +29,65 @@
#include "CallFrame.h"
#include "CodeBlock.h"
#include "JSStack.h"
+#include "VM.h"
namespace JSC {
-inline Register* JSStack::getTopOfFrame(CallFrame* frame)
+inline bool JSStack::ensureCapacityFor(Register* newTopOfStack)
{
- if (UNLIKELY(!frame))
- return begin();
- return frame->frameExtent();
-}
-
-inline Register* JSStack::getTopOfStack()
-{
- return getTopOfFrame(m_topCallFrame);
+#if !ENABLE(JIT)
+ return grow(newTopOfStack);
+#else
+ ASSERT(wtfThreadData().stack().isGrowingDownward());
+ return newTopOfStack >= m_vm.stackLimit();
+#endif
}
-inline Register* JSStack::getStartOfFrame(CallFrame* frame)
-{
- CallFrame* callerFrame = frame->callerFrameNoFlags();
- return getTopOfFrame(callerFrame);
-}
+#if !ENABLE(JIT)
-inline CallFrame* JSStack::pushFrame(CallFrame* callerFrame,
- class CodeBlock* codeBlock, JSScope* scope, int argsCount, JSObject* callee)
+inline Register* JSStack::topOfFrameFor(CallFrame* frame)
{
- 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 + JSStack::CallFrameHeaderSize;
-#if ENABLE(DEBUG_JSSTACK)
- newCallFrameSlot += JSStack::FenceSize;
-#endif
- Register* newEnd = newCallFrameSlot;
- if (!!codeBlock)
- newEnd += codeBlock->m_numCalleeRegisters;
-
- // Ensure that we have the needed stack capacity to push the new frame:
- if (!grow(newEnd))
- return 0;
-
- // 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.
- callerFrame = m_topCallFrame;
-
- // Initialize the frame header:
- newCallFrame->init(codeBlock, 0, scope,
- callerFrame->addHostCallFrameFlag(), 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;
+ if (UNLIKELY(!frame))
+ return baseOfStack();
+ return frame->topOfFrame() - 1;
}
-inline void JSStack::popFrame(CallFrame* frame)
+inline Register* JSStack::topOfStack()
{
- validateFence(frame, __FUNCTION__, __LINE__);
- CallFrame* callerFrame = frame->callerFrameNoFlags();
-
- // 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(begin());
-
- installTrapsAfterFrame(callerFrame);
+ return topOfFrameFor(m_topCallFrame);
}
-
-#if ENABLE(DEBUG_JSSTACK)
-inline JSValue JSStack::generateFenceValue(size_t argIndex)
+inline void JSStack::shrink(Register* newTopOfStack)
{
- unsigned fenceBits = 0xfacebad0 | ((argIndex+1) & 0xf);
- JSValue fenceValue = JSValue(fenceBits);
- return fenceValue;
+ 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)
+ releaseExcessCapacity();
}
-// 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 *** |
-// |--------------------------------------|
-// | 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)
+inline bool JSStack::grow(Register* newTopOfStack)
{
- 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);
- }
+ Register* newEnd = newTopOfStack - 1;
+ if (newEnd >= m_end)
+ return true;
+ return growSlowCase(newTopOfStack);
}
-inline void JSStack::validateFence(CallFrame* frame, const char *function, int lineNo)
+inline void JSStack::setStackLimit(Register* newTopOfStack)
{
- 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);
- }
+ Register* newEnd = newTopOfStack - 1;
+ m_end = newEnd;
+ m_vm.setJSStackLimit(newTopOfStack);
}
-// When debugging the JSStack, we install bad values after the extent of the
-// topCallFrame at the end of pushFrame() and popFrame(). The intention is
-// to trigger crashes in the event that memory in this supposedly unused
-// region is read and consumed without proper initialization. After the trap
-// words are installed, the stack looks like this:
-//
-// | ... |
-// |-----------------------------|
-// | Frame Header of frame |
-// |-----------------------------|
-// topCallFrame --> | |
-// | Locals of frame |
-// |-----------------------------|
-// | *** Trap words *** |
-// |-----------------------------|
-// | Unused space ... |
-// | ... |
-
-inline void JSStack::installTrapsAfterFrame(CallFrame* frame)
-{
- Register* topOfFrame = getTopOfFrame(frame);
- const int sizeOfTrap = 64;
- int32_t* startOfTrap = reinterpret_cast<int32_t*>(topOfFrame);
- int32_t* endOfTrap = startOfTrap + sizeOfTrap;
- int32_t* endOfCommitedMemory = reinterpret_cast<int32_t*>(m_commitEnd);
-
- // Make sure we're not exceeding the amount of available memory to write to:
- if (endOfTrap > endOfCommitedMemory)
- endOfTrap = endOfCommitedMemory;
-
- // Lay the traps:
- int32_t* p = startOfTrap;
- while (p < endOfTrap)
- *p++ = 0xabadcafe; // A bad word to trigger a crash if deref'ed.
-}
-#endif // ENABLE(DEBUG_JSSTACK)
+#endif // !ENABLE(JIT)
} // namespace JSC
diff --git a/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp b/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp
new file mode 100644
index 000000000..eb80b2c23
--- /dev/null
+++ b/Source/JavaScriptCore/interpreter/ProtoCallFrame.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#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)
+{
+ this->args = otherArgs;
+ this->setCodeBlock(codeBlock);
+ 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);
+ this->clearCurrentVPC();
+ this->setThisValue(thisValue);
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/interpreter/ProtoCallFrame.h b/Source/JavaScriptCore/interpreter/ProtoCallFrame.h
new file mode 100644
index 000000000..af33a3072
--- /dev/null
+++ b/Source/JavaScriptCore/interpreter/ProtoCallFrame.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ProtoCallFrame_h
+#define ProtoCallFrame_h
+
+#include "Register.h"
+
+namespace JSC {
+
+struct ProtoCallFrame {
+ Register codeBlockValue;
+ Register calleeValue;
+ Register argCountAndCodeOriginValue;
+ Register thisArg;
+ uint32_t paddedArgCount;
+ bool arityMissMatch;
+ JSValue *args;
+
+ void init(CodeBlock*, 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; }
+
+ 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 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());
+ return args[argumentIndex];
+ }
+ void setArgument(size_t argumentIndex, JSValue value)
+ {
+ ASSERT(static_cast<int>(argumentIndex) < argumentCount());
+ args[argumentIndex] = value;
+ }
+};
+
+} // namespace JSC
+
+#endif // ProtoCallFrame_h
diff --git a/Source/JavaScriptCore/interpreter/Register.h b/Source/JavaScriptCore/interpreter/Register.h
index bc2335689..c0396c966 100644
--- a/Source/JavaScriptCore/interpreter/Register.h
+++ b/Source/JavaScriptCore/interpreter/Register.h
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -37,14 +37,10 @@ namespace JSC {
class CodeBlock;
class ExecState;
- class JSActivation;
+ class JSLexicalEnvironment;
class JSObject;
- class JSPropertyNameIterator;
class JSScope;
- struct InlineCallFrame;
- struct Instruction;
-
typedef ExecState CallFrame;
class Register {
@@ -55,28 +51,29 @@ 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=(Instruction*);
- Register& operator=(InlineCallFrame*);
+ Register& operator=(JSObject*);
int32_t i() const;
- JSActivation* activation() const;
CallFrame* callFrame() const;
CodeBlock* codeBlock() const;
- JSObject* function() const;
- JSPropertyNameIterator* propertyNameIterator() const;
+ CodeBlock* asanUnsafeCodeBlock() const;
+ JSObject* object() const;
JSScope* scope() const;
- Instruction* vPC() const;
- InlineCallFrame* asInlineCallFrame() const;
int32_t unboxedInt32() const;
+ int64_t unboxedInt52() const;
+ int64_t unboxedStrictInt52() const;
bool unboxedBoolean() const;
+ double unboxedDouble() const;
JSCell* unboxedCell() const;
int32_t payload() const;
int32_t tag() const;
+ int32_t unsafeTag() const;
int32_t& payload();
int32_t& tag();
@@ -86,16 +83,14 @@ namespace JSC {
return r;
}
- static Register withCallee(JSObject* callee);
-
private:
union {
EncodedJSValue value;
CallFrame* callFrame;
CodeBlock* codeBlock;
- Instruction* vPC;
- InlineCallFrame* inlineCallFrame;
EncodedValueDescriptor encodedValue;
+ double number;
+ int64_t integer;
} u;
};
@@ -117,6 +112,12 @@ namespace JSC {
return *this;
}
+ // FIXME (rdar://problem/19379214): ASan only needs to be suppressed for Register::jsValue() when called from prepareOSREntry(), but there is currently no way to express this short of adding a separate copy of the function.
+ SUPPRESS_ASAN ALWAYS_INLINE JSValue Register::asanUnsafeJSValue() const
+ {
+ return JSValue::decode(u.value);
+ }
+
ALWAYS_INLINE JSValue Register::jsValue() const
{
return JSValue::decode(u.value);
@@ -141,18 +142,6 @@ namespace JSC {
return *this;
}
- ALWAYS_INLINE Register& Register::operator=(Instruction* vPC)
- {
- u.vPC = vPC;
- return *this;
- }
-
- ALWAYS_INLINE Register& Register::operator=(InlineCallFrame* inlineCallFrame)
- {
- u.inlineCallFrame = inlineCallFrame;
- return *this;
- }
-
ALWAYS_INLINE int32_t Register::i() const
{
return jsValue().asInt32();
@@ -168,26 +157,36 @@ namespace JSC {
return u.codeBlock;
}
- ALWAYS_INLINE Instruction* Register::vPC() const
+ SUPPRESS_ASAN ALWAYS_INLINE CodeBlock* Register::asanUnsafeCodeBlock() const
{
- return u.vPC;
+ return u.codeBlock;
}
- ALWAYS_INLINE InlineCallFrame* Register::asInlineCallFrame() const
- {
- return u.inlineCallFrame;
- }
-
ALWAYS_INLINE int32_t Register::unboxedInt32() const
{
return payload();
}
+ ALWAYS_INLINE int64_t Register::unboxedInt52() const
+ {
+ return u.integer >> JSValue::int52ShiftAmount;
+ }
+
+ ALWAYS_INLINE int64_t Register::unboxedStrictInt52() const
+ {
+ return u.integer;
+ }
+
ALWAYS_INLINE bool Register::unboxedBoolean() const
{
return !!payload();
}
+ ALWAYS_INLINE double Register::unboxedDouble() const
+ {
+ return u.number;
+ }
+
ALWAYS_INLINE JSCell* Register::unboxedCell() const
{
#if USE(JSVALUE64)
@@ -207,6 +206,11 @@ namespace JSC {
return u.encodedValue.asBits.tag;
}
+ SUPPRESS_ASAN ALWAYS_INLINE int32_t Register::unsafeTag() const
+ {
+ return u.encodedValue.asBits.tag;
+ }
+
ALWAYS_INLINE int32_t& Register::payload()
{
return u.encodedValue.asBits.payload;
diff --git a/Source/JavaScriptCore/interpreter/StackVisitor.cpp b/Source/JavaScriptCore/interpreter/StackVisitor.cpp
new file mode 100644
index 000000000..c0c03d280
--- /dev/null
+++ b/Source/JavaScriptCore/interpreter/StackVisitor.cpp
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * 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 "StackVisitor.h"
+
+#include "ClonedArguments.h"
+#include "Executable.h"
+#include "InlineCallFrame.h"
+#include "Interpreter.h"
+#include "JSCInlines.h"
+#include <wtf/DataLog.h>
+
+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();
+}
+
+void StackVisitor::gotoNextFrame()
+{
+#if ENABLE(DFG_JIT)
+ if (m_frame.isInlinedFrame()) {
+ InlineCallFrame* inlineCallFrame = m_frame.inlineCallFrame();
+ CodeOrigin* callerCodeOrigin = inlineCallFrame->getCallerSkippingTailCalls();
+ if (!callerCodeOrigin) {
+ while (inlineCallFrame) {
+ readInlinedFrame(m_frame.callFrame(), &inlineCallFrame->directCaller);
+ inlineCallFrame = m_frame.inlineCallFrame();
+ }
+ m_frame.m_VMEntryFrame = m_frame.m_CallerVMEntryFrame;
+ readFrame(m_frame.callerFrame());
+ } else
+ readInlinedFrame(m_frame.callFrame(), callerCodeOrigin);
+ return;
+ }
+#endif // ENABLE(DFG_JIT)
+ m_frame.m_VMEntryFrame = m_frame.m_CallerVMEntryFrame;
+ readFrame(m_frame.callerFrame());
+}
+
+void StackVisitor::unwindToMachineCodeBlockFrame()
+{
+#if ENABLE(DFG_JIT)
+ while (m_frame.isInlinedFrame())
+ gotoNextFrame();
+#endif
+}
+
+void StackVisitor::readFrame(CallFrame* callFrame)
+{
+ if (!callFrame) {
+ m_frame.setToEnd();
+ return;
+ }
+
+#if !ENABLE(DFG_JIT)
+ readNonInlinedFrame(callFrame);
+
+#else // !ENABLE(DFG_JIT)
+ // If the frame doesn't have a code block, then it's not a DFG frame.
+ // Hence, we're not at an inlined frame.
+ CodeBlock* codeBlock = callFrame->codeBlock();
+ if (!codeBlock) {
+ readNonInlinedFrame(callFrame);
+ return;
+ }
+
+ // If the code block does not have any code origins, then there's no
+ // inlining. Hence, we're not at an inlined frame.
+ if (!codeBlock->hasCodeOrigins()) {
+ readNonInlinedFrame(callFrame);
+ return;
+ }
+
+ CallSiteIndex index = callFrame->callSiteIndex();
+ ASSERT(codeBlock->canGetCodeOrigin(index));
+ if (!codeBlock->canGetCodeOrigin(index)) {
+ // See assertion above. In release builds, we try to protect ourselves
+ // from crashing even though stack walking will be goofed up.
+ m_frame.setToEnd();
+ return;
+ }
+
+ CodeOrigin codeOrigin = codeBlock->codeOrigin(index);
+ if (!codeOrigin.inlineCallFrame) {
+ readNonInlinedFrame(callFrame, &codeOrigin);
+ return;
+ }
+
+ readInlinedFrame(callFrame, &codeOrigin);
+#endif // !ENABLE(DFG_JIT)
+}
+
+void StackVisitor::readNonInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin)
+{
+ 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_callee = callFrame->callee();
+ m_frame.m_codeBlock = callFrame->codeBlock();
+ m_frame.m_bytecodeOffset = !m_frame.codeBlock() ? 0
+ : codeOrigin ? codeOrigin->bytecodeIndex
+ : callFrame->bytecodeOffset();
+#if ENABLE(DFG_JIT)
+ m_frame.m_inlineCallFrame = 0;
+#endif
+}
+
+#if ENABLE(DFG_JIT)
+static int inlinedFrameOffset(CodeOrigin* codeOrigin)
+{
+ InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame;
+ int frameOffset = inlineCallFrame ? inlineCallFrame->stackOffset : 0;
+ return frameOffset;
+}
+
+void StackVisitor::readInlinedFrame(CallFrame* callFrame, CodeOrigin* codeOrigin)
+{
+ ASSERT(codeOrigin);
+
+ int frameOffset = inlinedFrameOffset(codeOrigin);
+ bool isInlined = !!frameOffset;
+ if (isInlined) {
+ InlineCallFrame* inlineCallFrame = codeOrigin->inlineCallFrame;
+
+ 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_codeBlock = inlineCallFrame->baselineCodeBlock.get();
+ m_frame.m_bytecodeOffset = codeOrigin->bytecodeIndex;
+
+ JSFunction* callee = inlineCallFrame->calleeForCallFrame(callFrame);
+ m_frame.m_callee = callee;
+ ASSERT(m_frame.callee());
+
+ // The callerFrame just needs to be non-null to indicate that we
+ // haven't reached the last frame yet. Setting it to the root
+ // frame (i.e. the callFrame that this inlined frame is called from)
+ // would work just fine.
+ m_frame.m_callerFrame = callFrame;
+ return;
+ }
+
+ readNonInlinedFrame(callFrame, codeOrigin);
+}
+#endif // ENABLE(DFG_JIT)
+
+StackVisitor::Frame::CodeType StackVisitor::Frame::codeType() const
+{
+ if (!isJSFrame())
+ return CodeType::Native;
+
+ switch (codeBlock()->codeType()) {
+ case EvalCode:
+ return CodeType::Eval;
+ case ModuleCode:
+ return CodeType::Module;
+ case FunctionCode:
+ return CodeType::Function;
+ case GlobalCode:
+ return CodeType::Global;
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+ return CodeType::Global;
+}
+
+String StackVisitor::Frame::functionName()
+{
+ String traceLine;
+ JSObject* callee = this->callee();
+
+ switch (codeType()) {
+ case CodeType::Eval:
+ traceLine = ASCIILiteral("eval code");
+ break;
+ case CodeType::Module:
+ traceLine = ASCIILiteral("module code");
+ break;
+ case CodeType::Native:
+ if (callee)
+ traceLine = getCalculatedDisplayName(callFrame(), callee).impl();
+ break;
+ case CodeType::Function:
+ traceLine = getCalculatedDisplayName(callFrame(), callee).impl();
+ break;
+ case CodeType::Global:
+ traceLine = ASCIILiteral("global code");
+ break;
+ }
+ return traceLine.isNull() ? emptyString() : traceLine;
+}
+
+String StackVisitor::Frame::sourceURL()
+{
+ String traceLine;
+
+ switch (codeType()) {
+ case CodeType::Eval:
+ case CodeType::Module:
+ case CodeType::Function:
+ case CodeType::Global: {
+ String sourceURL = codeBlock()->ownerScriptExecutable()->sourceURL();
+ if (!sourceURL.isEmpty())
+ traceLine = sourceURL.impl();
+ break;
+ }
+ case CodeType::Native:
+ traceLine = ASCIILiteral("[native code]");
+ break;
+ }
+ return traceLine.isNull() ? emptyString() : traceLine;
+}
+
+String StackVisitor::Frame::toString()
+{
+ StringBuilder traceBuild;
+ String functionName = this->functionName();
+ String sourceURL = this->sourceURL();
+ traceBuild.append(functionName);
+ if (!sourceURL.isEmpty()) {
+ if (!functionName.isEmpty())
+ traceBuild.append('@');
+ traceBuild.append(sourceURL);
+ if (isJSFrame()) {
+ unsigned line = 0;
+ unsigned column = 0;
+ computeLineAndColumn(line, column);
+ traceBuild.append(':');
+ traceBuild.appendNumber(line);
+ traceBuild.append(':');
+ traceBuild.appendNumber(column);
+ }
+ }
+ return traceBuild.toString().impl();
+}
+
+ClonedArguments* StackVisitor::Frame::createArguments()
+{
+ ASSERT(m_callFrame);
+ CallFrame* physicalFrame = m_callFrame;
+ ClonedArguments* arguments;
+ ArgumentsMode mode;
+ if (Options::useFunctionDotArguments())
+ mode = ArgumentsMode::Cloned;
+ else
+ mode = ArgumentsMode::FakeValues;
+#if ENABLE(DFG_JIT)
+ if (isInlinedFrame()) {
+ ASSERT(m_inlineCallFrame);
+ arguments = ClonedArguments::createWithInlineFrame(physicalFrame, physicalFrame, m_inlineCallFrame, mode);
+ } else
+#endif
+ arguments = ClonedArguments::createWithMachineFrame(physicalFrame, physicalFrame, mode);
+ return arguments;
+}
+
+void StackVisitor::Frame::computeLineAndColumn(unsigned& line, unsigned& column)
+{
+ CodeBlock* codeBlock = this->codeBlock();
+ if (!codeBlock) {
+ line = 0;
+ column = 0;
+ return;
+ }
+
+ int divot = 0;
+ int unusedStartOffset = 0;
+ int unusedEndOffset = 0;
+ unsigned divotLine = 0;
+ unsigned divotColumn = 0;
+ retrieveExpressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn);
+
+ line = divotLine + codeBlock->ownerScriptExecutable()->firstLine();
+ column = divotColumn + (divotLine ? 1 : codeBlock->firstLineColumnOffset());
+
+ if (codeBlock->ownerScriptExecutable()->hasOverrideLineNumber())
+ line = codeBlock->ownerScriptExecutable()->overrideLineNumber();
+}
+
+void StackVisitor::Frame::retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column)
+{
+ CodeBlock* codeBlock = this->codeBlock();
+ codeBlock->unlinkedCodeBlock()->expressionRangeForBytecodeOffset(bytecodeOffset(), divot, startOffset, endOffset, line, column);
+ divot += codeBlock->sourceOffset();
+}
+
+void StackVisitor::Frame::setToEnd()
+{
+ m_callFrame = 0;
+#if ENABLE(DFG_JIT)
+ m_inlineCallFrame = 0;
+#endif
+}
+
+static void printIndents(int levels)
+{
+ while (levels--)
+ dataLogFString(" ");
+}
+
+template<typename... Types>
+void log(unsigned indent, const Types&... values)
+{
+ printIndents(indent);
+ dataLog(values...);
+}
+
+template<typename... Types>
+void logF(unsigned indent, const char* format, const Types&... values)
+{
+ printIndents(indent);
+
+#if COMPILER(GCC_OR_CLANG)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+#pragma GCC diagnostic ignored "-Wmissing-format-attribute"
+#endif
+
+ dataLogF(format, values...);
+
+#if COMPILER(GCC_OR_CLANG)
+#pragma GCC diagnostic pop
+#endif
+}
+
+void StackVisitor::Frame::print(int indent)
+{
+ if (!this->callFrame()) {
+ log(indent, "frame 0x0\n");
+ return;
+ }
+
+ CodeBlock* codeBlock = this->codeBlock();
+ logF(indent, "frame %p {\n", this->callFrame());
+
+ {
+ indent++;
+
+ 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");
+
+ bool isInlined = false;
+#if ENABLE(DFG_JIT)
+ isInlined = isInlinedFrame();
+ log(indent, "isInlinedFrame: ", isInlinedFrame(), "\n");
+ if (isInlinedFrame())
+ logF(indent, "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->callSiteAsRawBits();
+ logF(indent, "rawLocationBits: %u 0x%x\n", locationRawBits, locationRawBits);
+ logF(indent, "codeBlock: %p ", codeBlock);
+ if (codeBlock)
+ dataLog(*codeBlock);
+ dataLog("\n");
+ if (codeBlock && !isInlined) {
+ indent++;
+
+ if (callFrame->callSiteBitsAreBytecodeOffset()) {
+ unsigned bytecodeOffset = callFrame->bytecodeOffset();
+ log(indent, "bytecodeOffset: ", bytecodeOffset, " of ", codeBlock->instructions().size(), "\n");
+#if ENABLE(DFG_JIT)
+ } else {
+ log(indent, "hasCodeOrigins: ", codeBlock->hasCodeOrigins(), "\n");
+ if (codeBlock->hasCodeOrigins()) {
+ CallSiteIndex callSiteIndex = callFrame->callSiteIndex();
+ log(indent, "callSiteIndex: ", callSiteIndex.bits(), " of ", codeBlock->codeOrigins().size(), "\n");
+
+ JITCode::JITType jitType = codeBlock->jitType();
+ if (jitType != JITCode::FTLJIT) {
+ JITCode* jitCode = codeBlock->jitCode().get();
+ logF(indent, "jitCode: %p start %p end %p\n", jitCode, jitCode->start(), jitCode->end());
+ }
+ }
+#endif
+ }
+ unsigned line = 0;
+ unsigned column = 0;
+ computeLineAndColumn(line, column);
+ log(indent, "line: ", line, "\n");
+ log(indent, "column: ", column, "\n");
+
+ indent--;
+ }
+ indent--;
+ }
+ log(indent, "}\n");
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/interpreter/StackVisitor.h b/Source/JavaScriptCore/interpreter/StackVisitor.h
new file mode 100644
index 000000000..bbf37fe9c
--- /dev/null
+++ b/Source/JavaScriptCore/interpreter/StackVisitor.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2013, 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * 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 StackVisitor_h
+#define StackVisitor_h
+
+#include "VMEntryRecord.h"
+#include <wtf/text/WTFString.h>
+
+namespace JSC {
+
+struct CodeOrigin;
+struct InlineCallFrame;
+
+class CodeBlock;
+class ExecState;
+class JSFunction;
+class JSObject;
+class JSScope;
+class ClonedArguments;
+class Register;
+
+typedef ExecState CallFrame;
+
+class StackVisitor {
+public:
+ class Frame {
+ public:
+ enum CodeType {
+ Global,
+ Eval,
+ Function,
+ Module,
+ Native
+ };
+
+ size_t index() const { return m_index; }
+ size_t argumentCountIncludingThis() const { return m_argumentCountIncludingThis; }
+ bool callerIsVMEntryFrame() const { return m_callerIsVMEntryFrame; }
+ CallFrame* callerFrame() const { return m_callerFrame; }
+ JSObject* callee() const { return m_callee; }
+ CodeBlock* codeBlock() const { return m_codeBlock; }
+ unsigned bytecodeOffset() const { return m_bytecodeOffset; }
+#if ENABLE(DFG_JIT)
+ InlineCallFrame* inlineCallFrame() const { return m_inlineCallFrame; }
+#endif
+
+ bool isJSFrame() const { return !!codeBlock(); }
+#if ENABLE(DFG_JIT)
+ bool isInlinedFrame() const { return !!m_inlineCallFrame; }
+#endif
+
+ JS_EXPORT_PRIVATE String functionName();
+ JS_EXPORT_PRIVATE String sourceURL();
+ JS_EXPORT_PRIVATE String toString();
+
+ CodeType codeType() const;
+ JS_EXPORT_PRIVATE void computeLineAndColumn(unsigned& line, unsigned& column);
+
+ ClonedArguments* createArguments();
+ VMEntryFrame* vmEntryFrame() const { return m_VMEntryFrame; }
+ CallFrame* callFrame() const { return m_callFrame; }
+
+ JS_EXPORT_PRIVATE void print(int indentLevel);
+
+ private:
+ Frame() { }
+ ~Frame() { }
+
+ void retrieveExpressionInfo(int& divot, int& startOffset, int& endOffset, unsigned& line, unsigned& column);
+ void setToEnd();
+
+ size_t m_index;
+ size_t m_argumentCountIncludingThis;
+ VMEntryFrame* m_VMEntryFrame;
+ VMEntryFrame* m_CallerVMEntryFrame;
+ CallFrame* m_callerFrame;
+ JSObject* m_callee;
+ CodeBlock* m_codeBlock;
+ unsigned m_bytecodeOffset;
+ bool m_callerIsVMEntryFrame;
+#if ENABLE(DFG_JIT)
+ InlineCallFrame* m_inlineCallFrame;
+#endif
+ CallFrame* m_callFrame;
+
+ friend class StackVisitor;
+ };
+
+ enum Status {
+ Continue = 0,
+ Done = 1
+ };
+
+ // StackVisitor::visit() expects a Functor that implements the following method:
+ // Status operator()(StackVisitor&);
+
+ template <typename Functor>
+ static void visit(CallFrame* startFrame, Functor& functor)
+ {
+ StackVisitor visitor(startFrame);
+ while (visitor->callFrame()) {
+ Status status = functor(visitor);
+ if (status != Continue)
+ break;
+ visitor.gotoNextFrame();
+ }
+ }
+
+ Frame& operator*() { return m_frame; }
+ ALWAYS_INLINE Frame* operator->() { return &m_frame; }
+ void unwindToMachineCodeBlockFrame();
+
+private:
+ JS_EXPORT_PRIVATE StackVisitor(CallFrame* startFrame);
+
+ JS_EXPORT_PRIVATE void gotoNextFrame();
+
+ void readFrame(CallFrame*);
+ void readNonInlinedFrame(CallFrame*, CodeOrigin* = 0);
+#if ENABLE(DFG_JIT)
+ void readInlinedFrame(CallFrame*, CodeOrigin*);
+#endif
+
+ Frame m_frame;
+};
+
+class CallerFunctor {
+public:
+ CallerFunctor()
+ : m_hasSkippedFirstFrame(false)
+ , m_callerFrame(0)
+ {
+ }
+
+ CallFrame* callerFrame() const { return m_callerFrame; }
+
+ StackVisitor::Status operator()(StackVisitor& visitor)
+ {
+ if (!m_hasSkippedFirstFrame) {
+ m_hasSkippedFirstFrame = true;
+ return StackVisitor::Continue;
+ }
+
+ m_callerFrame = visitor->callFrame();
+ return StackVisitor::Done;
+ }
+
+private:
+ bool m_hasSkippedFirstFrame;
+ CallFrame* m_callerFrame;
+};
+
+} // namespace JSC
+
+#endif // StackVisitor_h
+
diff --git a/Source/JavaScriptCore/interpreter/VMEntryRecord.h b/Source/JavaScriptCore/interpreter/VMEntryRecord.h
new file mode 100644
index 000000000..9726538fb
--- /dev/null
+++ b/Source/JavaScriptCore/interpreter/VMEntryRecord.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef VMEntryRecord_h
+#define VMEntryRecord_h
+
+namespace JSC {
+
+typedef void VMEntryFrame;
+
+class ExecState;
+class VM;
+
+struct VMEntryRecord {
+ /*
+ * This record stored in a vmEntryTo{JavaScript,Host} allocated frame. It is allocated on the stack
+ * after callee save registers where local variables would go.
+ */
+ VM* m_vm;
+ ExecState* m_prevTopCallFrame;
+ VMEntryFrame* m_prevTopVMEntryFrame;
+
+ ExecState* prevTopCallFrame() { return m_prevTopCallFrame; }
+ SUPPRESS_ASAN ExecState* unsafePrevTopCallFrame() { return m_prevTopCallFrame; }
+
+ VMEntryFrame* prevTopVMEntryFrame() { return m_prevTopVMEntryFrame; }
+ SUPPRESS_ASAN VMEntryFrame* unsafePrevTopVMEntryFrame() { return m_prevTopVMEntryFrame; }
+};
+
+extern "C" VMEntryRecord* vmEntryRecord(VMEntryFrame*);
+
+} // namespace JSC
+
+#endif // VMEntryRecord_h
diff --git a/Source/JavaScriptCore/interpreter/VMInspector.cpp b/Source/JavaScriptCore/interpreter/VMInspector.cpp
deleted file mode 100644
index 58bc15075..000000000
--- a/Source/JavaScriptCore/interpreter/VMInspector.cpp
+++ /dev/null
@@ -1,573 +0,0 @@
-/*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-#include "VMInspector.h"
-
-#if ENABLE(VMINSPECTOR)
-
-#include <stdio.h>
-#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->hasHostCallFrameFlag()) {
- count++;
- frame = frame->callerFrame();
- }
- return count;
-}
-
-
-//============================================================================
-// class FormatPrinter
-// - implements functionality to support fprintf.
-//
-// The FormatPrinter classes do the real formatting and printing.
-// By default, the superclass FormatPrinter will print to stdout (printf).
-// Each of the subclass will implement the other ...printf() options.
-// The subclasses are:
-//
-// FileFormatPrinter - fprintf
-// StringFormatPrinter - sprintf
-// StringNFormatPrinter - snprintf
-
-class FormatPrinter {
-public:
- virtual ~FormatPrinter() { }
-
- void print(const char* format, va_list args);
-
-protected:
- // Low level printers:
- bool printArg(const char* format, ...);
- virtual bool printArg(const char* format, va_list args);
-
- // JS type specific printers:
- void printWTFString(va_list args, bool verbose);
-};
-
-
-// The public print() function is the real workhorse behind the printf
-// family of functions. print() deciphers the % formatting, translate them
-// to primitive formats, and dispatches to underlying printArg() functions
-// to do the printing.
-//
-// The non-public internal printArg() function is virtual and is responsible
-// for handling the variations between printf, fprintf, sprintf, and snprintf.
-
-void FormatPrinter::print(const char* format, va_list args)
-{
- const char* p = format;
- const char* errorStr;
-
- // buffer is only used for 2 purposes:
- // 1. To temporarily hold a copy of normal chars (not needing formatting)
- // to be passed to printArg() and printed.
- //
- // The incoming format string may contain a string of normal chars much
- // longer than 128, but we handle this by breaking them out to 128 chars
- // fragments and printing each fragment before re-using the buffer to
- // load up the next fragment.
- //
- // 2. To hold a single "%..." format to be passed to printArg() to process
- // a single va_arg.
-
- char buffer[129]; // 128 chars + null terminator.
- char* end = &buffer[sizeof(buffer) - 1];
- const char* startOfFormatSpecifier = 0;
-
- while (true) {
- char c = *p++;
- char* curr = buffer;
-
- // Print leading normal chars:
- while (c != '\0' && c != '%') {
- *curr++ = c;
- if (curr == end) {
- // Out of buffer space. Flush the fragment, and start over.
- *curr = '\0';
- bool success = printArg("%s", buffer);
- if (!success) {
- errorStr = buffer;
- goto handleError;
- }
- curr = buffer;
- }
- c = *p++;
- }
- // If we have stuff in the buffer, flush the fragment:
- if (curr != buffer) {
- ASSERT(curr < end + 1);
- *curr = '\0';
- bool success = printArg("%s", buffer);
- if (!success) {
- errorStr = buffer;
- goto handleError;
- }
- }
-
- // End if there are not more chars to print:
- if (c == '\0')
- break;
-
- // If we get here, we've must have seen a '%':
- startOfFormatSpecifier = p - 1;
- ASSERT(*startOfFormatSpecifier == '%');
- c = *p++;
-
- // Check for "%%" case:
- if (c == '%') {
- bool success = printArg("%c", '%');
- if (!success) {
- errorStr = p - 2;
- goto handleError;
- }
- continue;
- }
-
- // Check for JS (%J<x>) formatting extensions:
- if (c == 'J') {
- bool verbose = false;
-
- c = *p++;
- if (UNLIKELY(c == '\0')) {
- errorStr = p - 2; // Rewind to % in "%J\0"
- goto handleError;
- }
-
- if (c == '+') {
- verbose = true;
- c= *p++;
- if (UNLIKELY(c == '\0')) {
- errorStr = p - 3; // Rewind to % in "%J+\0"
- goto handleError;
- }
- }
-
- switch (c) {
- // %Js - WTF::String*
- case 's': {
- printWTFString(args, verbose);
- continue;
- }
- } // END switch.
-
- // Check for non-JS extensions:
- } else if (c == 'b') {
- int value = va_arg(args, int);
- printArg("%s", value ? "TRUE" : "FALSE");
- continue;
- }
-
- // If we didn't handle the format in one of the above cases,
- // rewind p and let the standard formatting check handle it
- // if possible:
- p = startOfFormatSpecifier;
- ASSERT(*p == '%');
-
- // Check for standard formatting:
- // A format specifier always starts with a % and ends with some
- // alphabet. We'll do the simple thing and scan until the next
- // alphabet, or the end of string.
-
- // In the following, we're going to use buffer as storage for a copy
- // of a single format specifier. Hence, conceptually, we can think of
- // 'buffer' as synonymous with 'argFormat' here:
-
-#define ABORT_IF_FORMAT_TOO_LONG(curr) \
- do { \
- if (UNLIKELY(curr >= end)) \
- goto formatTooLong; \
- } while (false)
-
- curr = buffer;
- *curr++ = *p++; // Output the first % in the format specifier.
- c = *p++; // Grab the next char in the format specifier.
-
- // Checks for leading modifiers e.g. "%-d":
- // 0, -, ' ', +, '\''
- if (c == '0' || c == '-' || c == ' ' || c == '+' || c == '\'' || c == '#') {
- ABORT_IF_FORMAT_TOO_LONG(curr);
- *curr++ = c;
- c = *p++;
- }
-
- // Checks for decimal digit field width modifiers e.g. "%2f":
- while (c >= '0' && c <= '9') {
- ABORT_IF_FORMAT_TOO_LONG(curr);
- *curr++ = c;
- c = *p++;
- }
-
- // Checks for '.' e.g. "%2.f":
- if (c == '.') {
- ABORT_IF_FORMAT_TOO_LONG(curr);
- *curr++ = c;
- c = *p++;
-
- // Checks for decimal digit precision modifiers e.g. "%.2f":
- while (c >= '0' && c <= '9') {
- ABORT_IF_FORMAT_TOO_LONG(curr);
- *curr++ = c;
- c = *p++;
- }
- }
-
- // Checks for the modifier <m> where <m> can be:
- // l, h, j, t, z
- // e.g. "%ld"
- if (c == 'l' || c == 'h' || c == 'j' || c == 't' || c == 'z' || c == 'L') {
- ABORT_IF_FORMAT_TOO_LONG(curr);
- *curr++ = c;
- char prevChar = c;
- c = *p++;
-
- // Checks for the modifier ll or hh in %<x><m>:
- if ((prevChar == 'l' || prevChar == 'h') && c == prevChar) {
- ABORT_IF_FORMAT_TOO_LONG(curr);
- *curr++ = c;
- c = *p++;
- }
- }
-
- // Checks for %<x> where <x> can be:
- // d, i, n, o, u, x, X
- // But hey, we're just going to do the simple thing and allow any
- // alphabet. The user is expected to pass correct format specifiers.
- // We won't do any format checking here. We'll just pass it on, and the
- // underlying ...printf() implementation may do the needed checking
- // at its discretion.
- while (c != '\0' && !isASCIIAlpha(c)) {
- ABORT_IF_FORMAT_TOO_LONG(curr);
- *curr++ = c;
- c = *p++;
- }
-
- ABORT_IF_FORMAT_TOO_LONG(curr);
- *curr++ = c;
- if (c == '\0') {
- // Uh oh. Bad format. We should have gotten an alphabet instead.
- // Print the supposed format as a string instead:
- errorStr = buffer;
- goto handleError;
- }
-
- // Otherwise, we have the alpha that terminates the format.
- // Terminate the buffer (i.e. argFormat) string:
- ASSERT(isASCIIAlpha(c));
- ABORT_IF_FORMAT_TOO_LONG(curr);
- *curr = '\0';
-
- bool success = printArg(buffer, args);
- if (!success) {
- errorStr = buffer;
- goto handleError;
- }
- }
-#undef ABORT_IF_FORMAT_TOO_LONG
-
- return;
-
-formatTooLong:
- // Print the error string:
- ASSERT(!!startOfFormatSpecifier);
- p = startOfFormatSpecifier;
- ASSERT(p >= format);
- printArg("ERROR @ Format too long at \"%s\"\n", p);
- return;
-
-handleError:
- // We've got an error. Can't do any more work. Print an error message if
- // possible and then just return.
-
- // The errorStr may be pointing into the middle of buffer, or the original
- // format string. Move the string to buffer for consistency, and also so
- // that we can strip it of newlines below.
- if (errorStr != buffer) {
- size_t length = strlen(errorStr);
- if (length > sizeof(buffer) - 1)
- length = sizeof(buffer) - 1;
- memmove(buffer, errorStr, length);
- buffer[length] = '\0'; // Terminate the moved error string.
- }
- // Strip the newlines:
- char* cp = buffer;
- while (*cp) {
- if (*cp == '\n' || *cp == '\r')
- *cp = ' ';
- cp++;
- }
- // Print the error string:
- printArg("ERROR @ \"%s\"\n", buffer);
-}
-
-
-bool FormatPrinter::printArg(const char* format, ...)
-{
- va_list args;
- va_start(args, format);
- bool success = printArg(format, args);
- va_end(args);
- return success;
-}
-
-bool FormatPrinter::printArg(const char* format, va_list args)
-{
- int count = ::vprintf(format, args);
- return (count >= 0); // Fail if less than 0 chars printed.
-}
-
-
-// %Js - WTF::String*
-// verbose mode prints: WTF::String "<your string>"
-void FormatPrinter::printWTFString(va_list args, bool verbose)
-{
- const String* str = va_arg(args, const String*);
-
- // Print verbose header if appropriate:
- if (verbose)
- printArg("WTF::String \"");
-
- // Print the string itself:
- if (!str->isEmpty()) {
- if (str->is8Bit()) {
- const LChar* chars = str->characters8();
- printArg("%s", reinterpret_cast<const char*>(chars));
- } else {
- const UChar* chars = str->characters16();
- printArg("%S", reinterpret_cast<const wchar_t*>(chars));
- }
- }
-
- // Print verbose footer if appropriate:
- if (verbose)
- printArg("\"");
-}
-
-
-//============================================================================
-// class FileFormatPrinter
-// - implements functionality to support fprintf.
-
-class FileFormatPrinter: public FormatPrinter {
-public:
- FileFormatPrinter(FILE*);
-private:
- virtual bool printArg(const char* format, va_list args);
-
- FILE* m_file;
-};
-
-FileFormatPrinter::FileFormatPrinter(FILE* file)
- : m_file(file)
-{
-}
-
-bool FileFormatPrinter::printArg(const char* format, va_list args)
-{
- int count = ::vfprintf(m_file, format, args);
- return (count >= 0); // Fail if less than 0 chars printed.
-}
-
-
-//============================================================================
-// class StringFormatPrinter
-// - implements functionality to support sprintf.
-
-class StringFormatPrinter: public FormatPrinter {
-public:
- StringFormatPrinter(char* buffer);
-private:
- virtual bool printArg(const char* format, va_list args);
-
- char* m_buffer;
-};
-
-StringFormatPrinter::StringFormatPrinter(char* buffer)
- : m_buffer(buffer)
-{
-}
-
-bool StringFormatPrinter::printArg(const char* format, va_list args)
-{
- int count = ::vsprintf(m_buffer, format, args);
- m_buffer += count;
- return (count >= 0); // Fail if less than 0 chars printed.
-}
-
-
-//============================================================================
-// class StringNFormatPrinter
-// - implements functionality to support snprintf.
-
-class StringNFormatPrinter: public FormatPrinter {
-public:
- StringNFormatPrinter(char* buffer, size_t);
-private:
- virtual bool printArg(const char* format, va_list args);
-
- char* m_buffer;
- size_t m_size;
-};
-
-
-StringNFormatPrinter::StringNFormatPrinter(char* buffer, size_t size)
- : m_buffer(buffer)
- , m_size(size)
-{
-}
-
-bool StringNFormatPrinter::printArg(const char* format, va_list args)
-{
- if (m_size > 0) {
- int count = ::vsnprintf(m_buffer, m_size, format, args);
-
- // According to vsnprintf specs, ...
- bool success = (count >= 0);
- if (static_cast<size_t>(count) >= m_size) {
- // If count > size, then we didn't have enough buffer space.
- count = m_size;
- }
-
- // Adjust the buffer to what's left if appropriate:
- if (success) {
- m_buffer += count;
- m_size -= count;
- }
- return success;
- }
- // No more room to print. Declare it a fail:
- return false;
-}
-
-
-//============================================================================
-// VMInspector printf family of methods:
-
-void VMInspector::fprintf(FILE* file, const char* format, ...)
-{
- va_list args;
- va_start(args, format);
- FileFormatPrinter(file).print(format, args);
- va_end(args);
-}
-
-void VMInspector::printf(const char* format, ...)
-{
- va_list args;
- va_start(args, format);
- FormatPrinter().print(format, args);
- va_end(args);
-}
-
-void VMInspector::sprintf(char* buffer, const char* format, ...)
-{
- va_list args;
- va_start(args, format);
- StringFormatPrinter(buffer).print(format, args);
- va_end(args);
-}
-
-void VMInspector::snprintf(char* buffer, size_t size, const char* format, ...)
-{
- va_list args;
- va_start(args, format);
- StringNFormatPrinter(buffer, size).print(format, args);
- va_end(args);
-}
-
-} // namespace JSC
-
-#endif // ENABLE(VMINSPECTOR)
diff --git a/Source/JavaScriptCore/interpreter/VMInspector.h b/Source/JavaScriptCore/interpreter/VMInspector.h
deleted file mode 100644
index 6623068dc..000000000
--- a/Source/JavaScriptCore/interpreter/VMInspector.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef VMInspector_h
-#define VMInspector_h
-
-#define ENABLE_VMINSPECTOR 0
-
-#include "CallFrame.h"
-#include "JSCJSValue.h"
-#include <stdarg.h>
-#include <stdio.h>
-#include <wtf/text/WTFString.h>
-
-namespace JSC {
-
-#if ENABLE(VMINSPECTOR)
-
-class VMInspector {
-public:
- static JS_EXPORT_PRIVATE const char* getTypeName(JSValue);
- static JS_EXPORT_PRIVATE void dumpFrame0(CallFrame*);
- static JS_EXPORT_PRIVATE void dumpFrame(CallFrame*, const char* prefix = 0, const char* funcName = 0, const char* file = 0, int line = -1);
- static JS_EXPORT_PRIVATE int countFrames(CallFrame*);
-
- // Special family of ...printf() functions that support, in addition to the
- // standard % formats (e.g. %d, %s, etc), the following extra JSC formatting
- // options, %J<x>, where <x> consists of:
- //
- // + - verbose mode modifier.
- // Used in combination with other options. Must come after the %J.
- // s - WTF::String*
- //
- // Examples of usage:
- //
- // WTF::String str("My WTF String");
- //
- // // Printing the string. Will print:
- // // The wtf string says: "My WTF String" and is NOT EMPTY.
- //
- // VMInspector::printf("The wtf string says: \"%Js\" and is %s\n",
- // &str, str.isEmpty()?"EMPTY":"NOT EMPTY");
- //
- // // Printing the string with verbose mode. Will print:
- // // <WTF::String "My WTF String">
- //
- // VMInspector::printf("<%J+s>\n", &str);
- //
- // Also added some convenience non-JS formats:
- //
- // %b - boolean (va_args will look for an int).
- // Prints TRUE if non-zero, else prints FALSE.
- //
- // Caution: the user is expected to pass the correctly matched arguments
- // to pair with the corresponding % fomatting.
-
- static JS_EXPORT_PRIVATE void fprintf(FILE*, const char* format, ...);
- static JS_EXPORT_PRIVATE void printf(const char* format, ...);
- static JS_EXPORT_PRIVATE void sprintf(char*, const char* format, ...);
- static JS_EXPORT_PRIVATE void snprintf(char*, size_t, const char* format, ...);
-};
-
-#endif // ENABLE(VMINSPECTOR)
-
-} // namespace JSC
-
-#endif // VMInspector.h