summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/interpreter/Interpreter.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2015-10-15 09:45:50 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2015-10-15 09:45:50 +0000
commite15dd966d523731101f70ccf768bba12435a0208 (patch)
treeae9cb828a24ded2585a41af3f21411523b47897d /Source/JavaScriptCore/interpreter/Interpreter.cpp
downloadWebKitGtk-tarball-e15dd966d523731101f70ccf768bba12435a0208.tar.gz
webkitgtk-2.10.2webkitgtk-2.10.2
Diffstat (limited to 'Source/JavaScriptCore/interpreter/Interpreter.cpp')
-rw-r--r--Source/JavaScriptCore/interpreter/Interpreter.cpp1248
1 files changed, 1248 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp
new file mode 100644
index 000000000..518fc0b76
--- /dev/null
+++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp
@@ -0,0 +1,1248 @@
+/*
+ * 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
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "Interpreter.h"
+
+#include "BatchedTransitionOptimizer.h"
+#include "CallFrameClosure.h"
+#include "CallFrameInlines.h"
+#include "ClonedArguments.h"
+#include "CodeBlock.h"
+#include "DirectArguments.h"
+#include "Heap.h"
+#include "Debugger.h"
+#include "DebuggerCallFrame.h"
+#include "ErrorInstance.h"
+#include "EvalCodeCache.h"
+#include "Exception.h"
+#include "ExceptionHelpers.h"
+#include "GetterSetter.h"
+#include "JSArray.h"
+#include "JSBoundFunction.h"
+#include "JSCInlines.h"
+#include "JSLexicalEnvironment.h"
+#include "JSNotAnObject.h"
+#include "JSStackInlines.h"
+#include "JSString.h"
+#include "JSWithScope.h"
+#include "LLIntCLoop.h"
+#include "LLIntThunks.h"
+#include "LegacyProfiler.h"
+#include "LiteralParser.h"
+#include "ObjectPrototype.h"
+#include "Parser.h"
+#include "ProtoCallFrame.h"
+#include "RegExpObject.h"
+#include "RegExpPrototype.h"
+#include "Register.h"
+#include "SamplingTool.h"
+#include "ScopedArguments.h"
+#include "StackAlignment.h"
+#include "StackVisitor.h"
+#include "StrictEvalActivation.h"
+#include "StrongInlines.h"
+#include "Symbol.h"
+#include "VMEntryScope.h"
+#include "VMInlines.h"
+#include "VirtualRegister.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <wtf/StackStats.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/StringPrintStream.h>
+#include <wtf/Threading.h>
+#include <wtf/WTFThreadData.h>
+#include <wtf/text/StringBuilder.h>
+
+#if ENABLE(JIT)
+#include "JIT.h"
+#endif
+
+using namespace std;
+
+namespace JSC {
+
+String StackFrame::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 StackFrame::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;
+}
+
+JSValue eval(CallFrame* callFrame)
+{
+ if (!callFrame->argumentCount())
+ return jsUndefined();
+
+ JSValue program = callFrame->argument(0);
+ if (!program.isString())
+ return program;
+
+ TopCallFrameSetter topCallFrame(callFrame->vm(), callFrame);
+ String programSource = asString(program)->value(callFrame);
+ if (callFrame->hadException())
+ return JSValue();
+
+ CallFrame* callerFrame = callFrame->callerFrame();
+ CodeBlock* callerCodeBlock = callerFrame->codeBlock();
+ JSScope* callerScopeChain = callerFrame->uncheckedR(callerCodeBlock->scopeRegister().offset()).Register::scope();
+ EvalExecutable* eval = callerCodeBlock->evalCodeCache().tryGet(callerCodeBlock->isStrictMode(), programSource, callerScopeChain);
+
+ if (!eval) {
+ if (!callerCodeBlock->isStrictMode()) {
+ if (programSource.is8Bit()) {
+ LiteralParser<LChar> preparser(callFrame, programSource.characters8(), programSource.length(), NonStrictJSON);
+ if (JSValue parsedObject = preparser.tryLiteralParse())
+ return parsedObject;
+ } else {
+ LiteralParser<UChar> preparser(callFrame, programSource.characters16(), programSource.length(), NonStrictJSON);
+ if (JSValue parsedObject = preparser.tryLiteralParse())
+ return parsedObject;
+ }
+ }
+
+ // If the literal parser bailed, it should not have thrown exceptions.
+ ASSERT(!callFrame->vm().exception());
+
+ ThisTDZMode thisTDZMode = callerCodeBlock->unlinkedCodeBlock()->constructorKind() == ConstructorKind::Derived ? ThisTDZMode::AlwaysCheck : ThisTDZMode::CheckIfNeeded;
+ eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock->ownerExecutable(), callerCodeBlock->isStrictMode(), thisTDZMode, programSource, callerScopeChain);
+ if (!eval)
+ return jsUndefined();
+ }
+
+ JSValue thisValue = callerFrame->thisValue();
+ Interpreter* interpreter = callFrame->vm().interpreter;
+ return interpreter->execute(eval, callFrame, thisValue, callerScopeChain);
+}
+
+unsigned sizeOfVarargs(CallFrame* callFrame, JSValue arguments, uint32_t firstVarArgOffset)
+{
+ if (UNLIKELY(!arguments.isCell())) {
+ if (arguments.isUndefinedOrNull())
+ return 0;
+
+ callFrame->vm().throwException(callFrame, createInvalidFunctionApplyParameterError(callFrame, arguments));
+ return 0;
+ }
+
+ 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;
+}
+
+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;
+}
+
+void loadVarargs(CallFrame* callFrame, VirtualRegister firstElementDest, JSValue arguments, uint32_t offset, uint32_t length)
+{
+ if (UNLIKELY(!arguments.isCell()))
+ 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;
+ }
+ unsigned i;
+ for (i = 0; i < length && object->canGetIndexQuickly(i + offset); ++i)
+ callFrame->r(firstElementDest + i) = object->getIndexQuickly(i + offset);
+ for (; i < length; ++i)
+ callFrame->r(firstElementDest + i) = object->get(callFrame, i + offset);
+ return;
+ } }
+}
+
+void setupVarargsFrame(CallFrame* callFrame, CallFrame* newCallFrame, JSValue arguments, uint32_t offset, uint32_t length)
+{
+ VirtualRegister calleeFrameOffset(newCallFrame - callFrame);
+
+ loadVarargs(
+ callFrame,
+ calleeFrameOffset + CallFrame::argumentOffset(0),
+ arguments, offset, length);
+
+ newCallFrame->setArgumentCountIncludingThis(length + 1);
+}
+
+void setupVarargsFrameAndSetThis(CallFrame* callFrame, CallFrame* newCallFrame, JSValue thisValue, JSValue arguments, uint32_t firstVarArgOffset, uint32_t length)
+{
+ setupVarargsFrame(callFrame, newCallFrame, arguments, firstVarArgOffset, length);
+ newCallFrame->setThisValue(thisValue);
+}
+
+Interpreter::Interpreter(VM& vm)
+ : m_sampleEntryDepth(0)
+ , m_vm(vm)
+ , m_stack(vm)
+ , m_errorHandlingModeReentry(0)
+#if !ASSERT_DISABLED
+ , m_initialized(false)
+#endif
+{
+}
+
+Interpreter::~Interpreter()
+{
+}
+
+void Interpreter::initialize(bool canUseJIT)
+{
+ UNUSED_PARAM(canUseJIT);
+
+#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));
+#endif
+
+#if !ASSERT_DISABLED
+ m_initialized = true;
+#endif
+
+#if ENABLE(OPCODE_SAMPLING)
+ enableSampler();
+#endif
+}
+
+#ifdef NDEBUG
+
+void Interpreter::dumpCallFrame(CallFrame*)
+{
+}
+
+#else
+
+void Interpreter::dumpCallFrame(CallFrame* callFrame)
+{
+ callFrame->codeBlock()->dumpBytecode();
+ 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");
+ dataLogF("-----------------------------------------------------------------------------\n");
+ dataLogF(" use | address | value \n");
+ dataLogF("-----------------------------------------------------------------------------\n");
+
+ CodeBlock* codeBlock = callFrame->codeBlock();
+ const Register* it;
+ const Register* 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(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;
+ }
+
+ dataLogF("-----------------------------------------------------------------------------\n");
+ dataLogF("[ArgumentCount] | %10p | %lu \n", it, (unsigned long) callFrame->argumentCount());
+ --it;
+ dataLogF("[CallerFrame] | %10p | %p \n", it, callFrame->callerFrame());
+ --it;
+ dataLogF("[Callee] | %10p | %p \n", it, callFrame->callee());
+ --it;
+ // FIXME: Remove the next decrement when the ScopeChain slot is removed from the call header
+ --it;
+#if ENABLE(JIT)
+ AbstractPC pc = callFrame->abstractReturnPC(callFrame->vm());
+ if (pc.hasJITReturnAddress())
+ dataLogF("[ReturnJITPC] | %10p | %p \n", it, pc.jitReturnAddress().value());
+#endif
+
+ DumpRegisterFunctor functor(it);
+ callFrame->iterate(functor);
+
+ dataLogF("[CodeBlock] | %10p | %p \n", it, callFrame->codeBlock());
+ --it;
+ dataLogF("-----------------------------------------------------------------------------\n");
+
+ end = it - codeBlock->m_numVars;
+ if (it != end) {
+ do {
+ JSValue v = it->jsValue();
+ int registerNumber = it - callFrame->registers();
+ 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;
+ } while (it != end);
+ }
+ dataLogF("-----------------------------------------------------------------------------\n");
+
+ end = it - codeBlock->m_numCalleeRegisters + codeBlock->m_numVars;
+ if (it != end) {
+ do {
+ JSValue v = (*it).jsValue();
+ 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");
+}
+
+#endif
+
+bool Interpreter::isOpcode(Opcode opcode)
+{
+#if ENABLE(COMPUTED_GOTO_OPCODES)
+ return opcode != HashTraits<Opcode>::emptyValue()
+ && !HashTraits<Opcode>::isDeletedValue(opcode)
+ && m_opcodeIDTable.contains(opcode);
+#else
+ return opcode >= 0 && opcode <= op_end;
+#endif
+}
+
+static bool unwindCallFrame(StackVisitor& visitor)
+{
+ CallFrame* callFrame = visitor->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());
+ }
+
+ return !visitor->callerIsVMEntryFrame();
+}
+
+static StackFrameCodeType getStackFrameCodeType(StackVisitor& visitor)
+{
+ switch (visitor->codeType()) {
+ case StackVisitor::Frame::Eval:
+ return StackFrameEvalCode;
+ case StackVisitor::Frame::Function:
+ return StackFrameFunctionCode;
+ case StackVisitor::Frame::Global:
+ return StackFrameGlobalCode;
+ case StackVisitor::Frame::Native:
+ ASSERT_NOT_REACHED();
+ return StackFrameNativeCode;
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+ return StackFrameGlobalCode;
+}
+
+void StackFrame::computeLineAndColumn(unsigned& line, unsigned& column)
+{
+ if (!codeBlock) {
+ line = 0;
+ column = 0;
+ return;
+ }
+
+ int divot = 0;
+ int unusedStartOffset = 0;
+ int unusedEndOffset = 0;
+ unsigned divotLine = 0;
+ unsigned divotColumn = 0;
+ expressionInfo(divot, unusedStartOffset, unusedEndOffset, divotLine, divotColumn);
+
+ 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)
+{
+ codeBlock->expressionRangeForBytecodeOffset(bytecodeOffset, divot, startOffset, endOffset, line, column);
+ divot += characterOffset;
+}
+
+String StackFrame::toString(CallFrame* callFrame)
+{
+ StringBuilder traceBuild;
+ String functionName = friendlyFunctionName(callFrame);
+ String sourceURL = friendlySourceURL();
+ traceBuild.append(functionName);
+ if (!sourceURL.isEmpty()) {
+ if (!functionName.isEmpty())
+ traceBuild.append('@');
+ traceBuild.append(sourceURL);
+ if (codeType != StackFrameNativeCode) {
+ unsigned line;
+ unsigned column;
+ computeLineAndColumn(line, column);
+
+ traceBuild.append(':');
+ traceBuild.appendNumber(line);
+ traceBuild.append(':');
+ traceBuild.appendNumber(column);
+ }
+ }
+ return traceBuild.toString().impl();
+}
+
+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() && !visitor->codeBlock()->unlinkedCodeBlock()->isBuiltinFunction()) {
+ CodeBlock* codeBlock = visitor->codeBlock();
+ StackFrame s = {
+ Strong<JSObject>(vm, visitor->callee()),
+ getStackFrameCodeType(visitor),
+ Strong<ScriptExecutable>(vm, codeBlock->ownerExecutable()),
+ Strong<UnlinkedCodeBlock>(vm, codeBlock->unlinkedCodeBlock()),
+ codeBlock->source(),
+ codeBlock->ownerExecutable()->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);
+ }
+
+ 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;
+
+ 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(exec)));
+ if (i != stackTrace.size() - 1)
+ builder.append('\n');
+ }
+ return jsString(&exec->vm(), builder.toString());
+}
+
+class GetCatchHandlerFunctor {
+public:
+ GetCatchHandlerFunctor()
+ : m_handler(0)
+ {
+ }
+
+ HandlerInfo* handler() { return m_handler; }
+
+ StackVisitor::Status operator()(StackVisitor& visitor)
+ {
+ CodeBlock* codeBlock = visitor->codeBlock();
+ if (!codeBlock)
+ return StackVisitor::Continue;
+
+ unsigned bytecodeOffset = visitor->bytecodeOffset();
+ m_handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset, CodeBlock::RequiredHandler::CatchHandler);
+ if (m_handler)
+ return StackVisitor::Done;
+
+ return StackVisitor::Continue;
+ }
+
+private:
+ HandlerInfo* m_handler;
+};
+
+class UnwindFunctor {
+public:
+ UnwindFunctor(VMEntryFrame*& vmEntryFrame, CallFrame*& callFrame, bool isTermination, CodeBlock*& codeBlock, HandlerInfo*& handler)
+ : m_vmEntryFrame(vmEntryFrame)
+ , m_callFrame(callFrame)
+ , m_isTermination(isTermination)
+ , m_codeBlock(codeBlock)
+ , m_handler(handler)
+ {
+ }
+
+ StackVisitor::Status operator()(StackVisitor& visitor)
+ {
+ VM& vm = m_callFrame->vm();
+ m_vmEntryFrame = visitor->vmEntryFrame();
+ m_callFrame = visitor->callFrame();
+ m_codeBlock = visitor->codeBlock();
+ unsigned bytecodeOffset = visitor->bytecodeOffset();
+
+ if (m_isTermination || !(m_handler = m_codeBlock ? m_codeBlock->handlerForBytecodeOffset(bytecodeOffset) : nullptr)) {
+ if (!unwindCallFrame(visitor)) {
+ if (LegacyProfiler* profiler = vm.enabledProfiler())
+ profiler->exceptionUnwind(m_callFrame);
+ return StackVisitor::Done;
+ }
+ } else
+ return StackVisitor::Done;
+
+ return StackVisitor::Continue;
+ }
+
+private:
+ VMEntryFrame*& m_vmEntryFrame;
+ CallFrame*& m_callFrame;
+ bool m_isTermination;
+ CodeBlock*& m_codeBlock;
+ HandlerInfo*& m_handler;
+};
+
+NEVER_INLINE HandlerInfo* Interpreter::unwind(VMEntryFrame*& vmEntryFrame, CallFrame*& callFrame, Exception* exception)
+{
+ CodeBlock* codeBlock = callFrame->codeBlock();
+ bool isTermination = false;
+
+ JSValue exceptionValue = exception->value();
+ ASSERT(!exceptionValue.isEmpty());
+ ASSERT(!exceptionValue.isCell() || exceptionValue.asCell());
+ // This shouldn't be possible (hence the assertions), but we're already in the slowest of
+ // slow cases, so let's harden against it anyway to be safe.
+ if (exceptionValue.isEmpty() || (exceptionValue.isCell() && !exceptionValue.asCell()))
+ exceptionValue = jsNull();
+
+ if (exceptionValue.isObject())
+ isTermination = isTerminatedExecutionException(exception);
+
+ ASSERT(callFrame->vm().exception() && callFrame->vm().exception()->stack().size());
+
+ Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger();
+ if (debugger && debugger->needsExceptionCallbacks() && !exception->didNotifyInspectorOfThrow()) {
+ // We need to clear the exception here in order to see if a new exception happens.
+ // Afterwards, the values are put back to continue processing this error.
+ SuspendExceptionScope scope(&callFrame->vm());
+ // This code assumes that if the debugger is enabled then there is no inlining.
+ // If that assumption turns out to be false then we'll ignore the inlined call
+ // frames.
+ // https://bugs.webkit.org/show_bug.cgi?id=121754
+
+ bool hasCatchHandler;
+ if (isTermination)
+ hasCatchHandler = false;
+ else {
+ GetCatchHandlerFunctor functor;
+ callFrame->iterate(functor);
+ HandlerInfo* handler = functor.handler();
+ ASSERT(!handler || handler->isCatchHandler());
+ hasCatchHandler = !!handler;
+ }
+
+ debugger->exception(callFrame, exceptionValue, hasCatchHandler);
+ ASSERT(!callFrame->hadException());
+ }
+ exception->setDidNotifyInspectorOfThrow();
+
+ // Calculate an exception handler vPC, unwinding call frames as necessary.
+ HandlerInfo* handler = 0;
+ VM& vm = callFrame->vm();
+ ASSERT(callFrame == vm.topCallFrame);
+ UnwindFunctor functor(vmEntryFrame, callFrame, isTermination, codeBlock, handler);
+ callFrame->iterate(functor);
+ if (!handler)
+ return 0;
+
+ if (LegacyProfiler* profiler = vm.enabledProfiler())
+ profiler->exceptionUnwind(callFrame);
+
+ return handler;
+}
+
+static inline JSValue checkedReturn(JSValue returnValue)
+{
+ ASSERT(returnValue);
+ return returnValue;
+}
+
+static inline JSObject* checkedReturn(JSObject* returnValue)
+{
+ ASSERT(returnValue);
+ return returnValue;
+}
+
+class SamplingScope {
+public:
+ SamplingScope(Interpreter* interpreter)
+ : m_interpreter(interpreter)
+ {
+ interpreter->startSampling();
+ }
+ ~SamplingScope()
+ {
+ m_interpreter->stopSampling();
+ }
+private:
+ Interpreter* m_interpreter;
+};
+
+JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, JSObject* thisObj)
+{
+ SamplingScope samplingScope(this);
+
+ JSScope* scope = thisObj->globalObject();
+ VM& vm = *scope->vm();
+
+ ASSERT(!vm.exception());
+ ASSERT(!vm.isCollectorBusy());
+ RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock());
+ if (vm.isCollectorBusy())
+ return jsNull();
+
+ 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.
+
+ Vector<JSONPData> JSONPData;
+ bool parseResult;
+ const String programSource = program->source().toString();
+ if (programSource.isNull())
+ return jsUndefined();
+ if (programSource.is8Bit()) {
+ LiteralParser<LChar> literalParser(callFrame, programSource.characters8(), programSource.length(), JSONP);
+ parseResult = literalParser.tryJSONPParse(JSONPData, scope->globalObject()->globalObjectMethodTable()->supportsRichSourceInfo(scope->globalObject()));
+ } else {
+ LiteralParser<UChar> literalParser(callFrame, programSource.characters16(), programSource.length(), JSONP);
+ parseResult = literalParser.tryJSONPParse(JSONPData, scope->globalObject()->globalObjectMethodTable()->supportsRichSourceInfo(scope->globalObject()));
+ }
+
+ if (parseResult) {
+ JSGlobalObject* globalObject = scope->globalObject();
+ JSValue result;
+ for (unsigned entry = 0; entry < JSONPData.size(); entry++) {
+ Vector<JSONPPathEntry> JSONPPath;
+ JSONPPath.swap(JSONPData[entry].m_path);
+ JSValue JSONPValue = JSONPData[entry].m_value.get();
+ if (JSONPPath.size() == 1 && JSONPPath[0].m_type == JSONPPathEntryTypeDeclare) {
+ globalObject->addVar(callFrame, JSONPPath[0].m_pathEntryName);
+ PutPropertySlot slot(globalObject);
+ globalObject->methodTable()->put(globalObject, callFrame, JSONPPath[0].m_pathEntryName, JSONPValue, slot);
+ result = jsUndefined();
+ continue;
+ }
+ JSValue baseObject(globalObject);
+ for (unsigned i = 0; i < JSONPPath.size() - 1; i++) {
+ ASSERT(JSONPPath[i].m_type != JSONPPathEntryTypeDeclare);
+ switch (JSONPPath[i].m_type) {
+ case JSONPPathEntryTypeDot: {
+ if (i == 0) {
+ PropertySlot slot(globalObject);
+ if (!globalObject->getPropertySlot(callFrame, JSONPPath[i].m_pathEntryName, slot)) {
+ if (entry)
+ return callFrame->vm().throwException(callFrame, createUndefinedVariableError(callFrame, JSONPPath[i].m_pathEntryName));
+ goto failedJSONP;
+ }
+ baseObject = slot.getValue(callFrame, JSONPPath[i].m_pathEntryName);
+ } else
+ baseObject = baseObject.get(callFrame, JSONPPath[i].m_pathEntryName);
+ if (callFrame->hadException())
+ return jsUndefined();
+ continue;
+ }
+ case JSONPPathEntryTypeLookup: {
+ baseObject = baseObject.get(callFrame, JSONPPath[i].m_pathIndex);
+ if (callFrame->hadException())
+ return jsUndefined();
+ continue;
+ }
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ return jsUndefined();
+ }
+ }
+ PutPropertySlot slot(baseObject);
+ switch (JSONPPath.last().m_type) {
+ case JSONPPathEntryTypeCall: {
+ JSValue function = baseObject.get(callFrame, JSONPPath.last().m_pathEntryName);
+ if (callFrame->hadException())
+ return jsUndefined();
+ CallData callData;
+ CallType callType = getCallData(function, callData);
+ if (callType == CallTypeNone)
+ return callFrame->vm().throwException(callFrame, createNotAFunctionError(callFrame, function));
+ MarkedArgumentBuffer jsonArg;
+ jsonArg.append(JSONPValue);
+ JSValue thisValue = JSONPPath.size() == 1 ? jsUndefined(): baseObject;
+ JSONPValue = JSC::call(callFrame, function, callType, callData, thisValue, jsonArg);
+ if (callFrame->hadException())
+ return jsUndefined();
+ break;
+ }
+ case JSONPPathEntryTypeDot: {
+ baseObject.put(callFrame, JSONPPath.last().m_pathEntryName, JSONPValue, slot);
+ if (callFrame->hadException())
+ return jsUndefined();
+ break;
+ }
+ case JSONPPathEntryTypeLookup: {
+ baseObject.putByIndex(callFrame, JSONPPath.last().m_pathIndex, JSONPValue, slot.isStrictMode());
+ if (callFrame->hadException())
+ return jsUndefined();
+ break;
+ }
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ return jsUndefined();
+ }
+ result = JSONPValue;
+ }
+ return result;
+ }
+failedJSONP:
+ // If we get here, then we have already proven that the script is not a JSON
+ // object.
+
+ VMEntryScope entryScope(vm, scope->globalObject());
+
+ // Compile source to bytecode if necessary:
+ if (JSObject* error = program->initializeGlobalProperties(vm, callFrame, scope))
+ return checkedReturn(callFrame->vm().throwException(callFrame, error));
+
+ if (JSObject* error = program->prepareForExecution(callFrame, nullptr, scope, CodeForCall))
+ return checkedReturn(callFrame->vm().throwException(callFrame, error));
+
+ ProgramCodeBlock* codeBlock = program->codeBlock();
+
+ if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
+ return throwTerminatedExecutionException(callFrame);
+
+ ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'.
+
+ ProtoCallFrame protoCallFrame;
+ protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), thisObj, 1);
+
+ if (LegacyProfiler* profiler = vm.enabledProfiler())
+ profiler->willExecute(callFrame, program->sourceURL(), program->firstLine(), program->startColumn());
+
+ // Execute the code:
+ JSValue result;
+ {
+ SamplingTool::CallRecord callRecord(m_sampler.get());
+ result = program->generatedJITCode()->execute(&vm, &protoCallFrame);
+ }
+
+ if (LegacyProfiler* profiler = vm.enabledProfiler())
+ profiler->didExecute(callFrame, program->sourceURL(), program->firstLine(), program->startColumn());
+
+ return checkedReturn(result);
+}
+
+JSValue Interpreter::executeCall(CallFrame* callFrame, JSObject* function, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args)
+{
+ VM& vm = callFrame->vm();
+ ASSERT(!callFrame->hadException());
+ ASSERT(!vm.isCollectorBusy());
+ if (vm.isCollectorBusy())
+ return jsNull();
+
+ bool isJSCall = (callType == CallTypeJS);
+ JSScope* scope = nullptr;
+ CodeBlock* newCodeBlock;
+ size_t argsCount = 1 + args.size(); // implicit "this" parameter
+
+ JSGlobalObject* globalObject;
+
+ if (isJSCall) {
+ scope = callData.js.scope;
+ globalObject = scope->globalObject();
+ } else {
+ ASSERT(callType == CallTypeHost);
+ globalObject = function->globalObject();
+ }
+
+ VMEntryScope entryScope(vm, globalObject);
+ if (!vm.isSafeToRecurse())
+ return checkedReturn(throwStackOverflowError(callFrame));
+
+ if (isJSCall) {
+ // Compile the callee:
+ JSObject* compileError = callData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(function), scope, CodeForCall);
+ if (UNLIKELY(!!compileError)) {
+ return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
+ }
+ newCodeBlock = callData.js.functionExecutable->codeBlockForCall();
+ ASSERT(!!newCodeBlock);
+ newCodeBlock->m_shouldAlwaysBeInlined = false;
+ } else
+ newCodeBlock = 0;
+
+ if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
+ return throwTerminatedExecutionException(callFrame);
+
+ ProtoCallFrame protoCallFrame;
+ protoCallFrame.init(newCodeBlock, function, thisValue, argsCount, args.data());
+
+ if (LegacyProfiler* profiler = vm.enabledProfiler())
+ profiler->willExecute(callFrame, function);
+
+ JSValue result;
+ {
+ SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSCall);
+
+ // Execute the code:
+ if (isJSCall)
+ result = callData.js.functionExecutable->generatedJITCodeForCall()->execute(&vm, &protoCallFrame);
+ else {
+ result = JSValue::decode(vmEntryToNative(reinterpret_cast<void*>(callData.native.function), &vm, &protoCallFrame));
+ if (callFrame->hadException())
+ result = jsNull();
+ }
+ }
+
+ if (LegacyProfiler* profiler = vm.enabledProfiler())
+ profiler->didExecute(callFrame, function);
+
+ return checkedReturn(result);
+}
+
+JSObject* Interpreter::executeConstruct(CallFrame* callFrame, JSObject* constructor, ConstructType constructType, const ConstructData& constructData, const ArgList& args, JSValue newTarget)
+{
+ VM& vm = callFrame->vm();
+ ASSERT(!callFrame->hadException());
+ ASSERT(!vm.isCollectorBusy());
+ // We throw in this case because we have to return something "valid" but we're
+ // already in an invalid state.
+ if (vm.isCollectorBusy())
+ return checkedReturn(throwStackOverflowError(callFrame));
+
+ bool isJSConstruct = (constructType == ConstructTypeJS);
+ JSScope* scope = nullptr;
+ CodeBlock* newCodeBlock;
+ size_t argsCount = 1 + args.size(); // implicit "this" parameter
+
+ JSGlobalObject* globalObject;
+
+ if (isJSConstruct) {
+ scope = constructData.js.scope;
+ globalObject = scope->globalObject();
+ } else {
+ ASSERT(constructType == ConstructTypeHost);
+ globalObject = constructor->globalObject();
+ }
+
+ VMEntryScope entryScope(vm, globalObject);
+ if (!vm.isSafeToRecurse())
+ return checkedReturn(throwStackOverflowError(callFrame));
+
+ if (isJSConstruct) {
+ // Compile the callee:
+ JSObject* compileError = constructData.js.functionExecutable->prepareForExecution(callFrame, jsCast<JSFunction*>(constructor), scope, CodeForConstruct);
+ if (UNLIKELY(!!compileError)) {
+ return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
+ }
+ newCodeBlock = constructData.js.functionExecutable->codeBlockForConstruct();
+ ASSERT(!!newCodeBlock);
+ newCodeBlock->m_shouldAlwaysBeInlined = false;
+ } else
+ newCodeBlock = 0;
+
+ if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
+ return throwTerminatedExecutionException(callFrame);
+
+ ProtoCallFrame protoCallFrame;
+ protoCallFrame.init(newCodeBlock, constructor, newTarget, argsCount, args.data());
+
+ if (LegacyProfiler* profiler = vm.enabledProfiler())
+ profiler->willExecute(callFrame, constructor);
+
+ JSValue result;
+ {
+ SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSConstruct);
+
+ // Execute the code.
+ 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);
+
+ if (callFrame->hadException())
+ return 0;
+ ASSERT(result.isObject());
+ return checkedReturn(asObject(result));
+}
+
+CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionExecutable, CallFrame* callFrame, ProtoCallFrame* protoCallFrame, JSFunction* function, int argumentCountIncludingThis, JSScope* scope, JSValue* args)
+{
+ VM& vm = *scope->vm();
+ ASSERT(!vm.exception());
+
+ if (vm.isCollectorBusy())
+ return CallFrameClosure();
+
+ // Compile the callee:
+ JSObject* error = functionExecutable->prepareForExecution(callFrame, function, scope, CodeForCall);
+ if (error) {
+ callFrame->vm().throwException(callFrame, error);
+ return CallFrameClosure();
+ }
+ CodeBlock* newCodeBlock = functionExecutable->codeBlockForCall();
+ newCodeBlock->m_shouldAlwaysBeInlined = false;
+
+ size_t argsCount = argumentCountIncludingThis;
+
+ protoCallFrame->init(newCodeBlock, function, jsUndefined(), argsCount, args);
+ // Return the successful closure:
+ CallFrameClosure result = { callFrame, protoCallFrame, function, functionExecutable, &vm, scope, newCodeBlock->numParameters(), argumentCountIncludingThis };
+ return result;
+}
+
+JSValue Interpreter::execute(CallFrameClosure& closure)
+{
+ VM& vm = *closure.vm;
+ SamplingScope samplingScope(this);
+
+ ASSERT(!vm.isCollectorBusy());
+ RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock());
+ if (vm.isCollectorBusy())
+ return jsNull();
+
+ StackStats::CheckPoint stackCheckPoint;
+
+ if (LegacyProfiler* profiler = vm.enabledProfiler())
+ profiler->willExecute(closure.oldCallFrame, closure.function);
+
+ if (UNLIKELY(vm.shouldTriggerTermination(closure.oldCallFrame)))
+ return throwTerminatedExecutionException(closure.oldCallFrame);
+
+ // Execute the code:
+ JSValue result;
+ {
+ SamplingTool::CallRecord callRecord(m_sampler.get());
+ result = closure.functionExecutable->generatedJITCodeForCall()->execute(&vm, closure.protoCallFrame);
+ }
+
+ if (LegacyProfiler* profiler = vm.enabledProfiler())
+ profiler->didExecute(closure.oldCallFrame, closure.function);
+
+ return checkedReturn(result);
+}
+
+JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue thisValue, JSScope* scope)
+{
+ VM& vm = *scope->vm();
+ SamplingScope samplingScope(this);
+
+ ASSERT(scope->vm() == &callFrame->vm());
+ ASSERT(!vm.exception());
+ ASSERT(!vm.isCollectorBusy());
+ RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock());
+ if (vm.isCollectorBusy())
+ return jsNull();
+
+ VMEntryScope entryScope(vm, scope->globalObject());
+ if (!vm.isSafeToRecurse())
+ return checkedReturn(throwStackOverflowError(callFrame));
+
+ unsigned numVariables = eval->numVariables();
+ int numFunctions = eval->numberOfFunctionDecls();
+
+ 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;
+ }
+ }
+ }
+ ASSERT(!variableObject->isNameScopeObject());
+ }
+
+ JSObject* compileError = eval->prepareForExecution(callFrame, nullptr, scope, CodeForCall);
+ if (UNLIKELY(!!compileError))
+ return checkedReturn(callFrame->vm().throwException(callFrame, compileError));
+ EvalCodeBlock* codeBlock = eval->codeBlock();
+
+ if (numVariables || numFunctions) {
+ 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(variableObject);
+ variableObject->methodTable()->put(variableObject, callFrame, ident, jsUndefined(), slot);
+ }
+ }
+
+ for (int i = 0; i < numFunctions; ++i) {
+ FunctionExecutable* function = codeBlock->functionDecl(i);
+ PutPropertySlot slot(variableObject);
+ variableObject->methodTable()->put(variableObject, callFrame, function->name(), JSFunction::create(vm, function, scope), slot);
+ }
+ }
+
+ if (UNLIKELY(vm.shouldTriggerTermination(callFrame)))
+ return throwTerminatedExecutionException(callFrame);
+
+ ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'.
+
+ ProtoCallFrame protoCallFrame;
+ protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), thisValue, 1);
+
+ if (LegacyProfiler* profiler = vm.enabledProfiler())
+ profiler->willExecute(callFrame, eval->sourceURL(), eval->firstLine(), eval->startColumn());
+
+ // Execute the code:
+ JSValue result;
+ {
+ SamplingTool::CallRecord callRecord(m_sampler.get());
+ result = eval->generatedJITCode()->execute(&vm, &protoCallFrame);
+ }
+
+ if (LegacyProfiler* profiler = vm.enabledProfiler())
+ profiler->didExecute(callFrame, eval->sourceURL(), eval->firstLine(), eval->startColumn());
+
+ return checkedReturn(result);
+}
+
+NEVER_INLINE void Interpreter::debug(CallFrame* callFrame, DebugHookID debugHookID)
+{
+ Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger();
+ if (!debugger)
+ return;
+
+ ASSERT(callFrame->codeBlock()->hasDebuggerRequests());
+ ASSERT(!callFrame->hadException());
+
+ 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;
+ }
+ ASSERT(!callFrame->hadException());
+}
+
+void Interpreter::enableSampler()
+{
+#if ENABLE(OPCODE_SAMPLING)
+ if (!m_sampler) {
+ m_sampler = std::make_unique<SamplingTool>(this);
+ m_sampler->setup();
+ }
+#endif
+}
+void Interpreter::dumpSampleData(ExecState* exec)
+{
+#if ENABLE(OPCODE_SAMPLING)
+ if (m_sampler)
+ m_sampler->dump(exec);
+#else
+ UNUSED_PARAM(exec);
+#endif
+}
+void Interpreter::startSampling()
+{
+#if ENABLE(SAMPLING_THREAD)
+ if (!m_sampleEntryDepth)
+ SamplingThread::start();
+
+ m_sampleEntryDepth++;
+#endif
+}
+void Interpreter::stopSampling()
+{
+#if ENABLE(SAMPLING_THREAD)
+ m_sampleEntryDepth--;
+ if (!m_sampleEntryDepth)
+ SamplingThread::stop();
+#endif
+}
+
+} // namespace JSC