diff options
Diffstat (limited to 'Source/JavaScriptCore/interpreter/Interpreter.cpp')
-rw-r--r-- | Source/JavaScriptCore/interpreter/Interpreter.cpp | 1414 |
1 files changed, 686 insertions, 728 deletions
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 |