diff options
Diffstat (limited to 'Source/JavaScriptCore/interpreter/Interpreter.cpp')
-rw-r--r-- | Source/JavaScriptCore/interpreter/Interpreter.cpp | 1203 |
1 files changed, 597 insertions, 606 deletions
diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp index 4fbc8229a..b1243f290 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-2017 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,238 +30,284 @@ #include "config.h" #include "Interpreter.h" -#include "Arguments.h" #include "BatchedTransitionOptimizer.h" -#include "CallFrame.h" #include "CallFrameClosure.h" -#include "CallFrameInlines.h" #include "CodeBlock.h" +#include "DirectArguments.h" #include "Heap.h" #include "Debugger.h" #include "DebuggerCallFrame.h" +#include "DirectEvalCodeCache.h" #include "ErrorInstance.h" -#include "EvalCodeCache.h" +#include "EvalCodeBlock.h" +#include "Exception.h" #include "ExceptionHelpers.h" -#include "GetterSetter.h" -#include "JSActivation.h" -#include "JSArray.h" +#include "FunctionCodeBlock.h" +#include "JSArrayInlines.h" #include "JSBoundFunction.h" -#include "JSNameScope.h" -#include "JSNotAnObject.h" -#include "JSPropertyNameIterator.h" -#include "JSStackInlines.h" +#include "JSCInlines.h" +#include "JSLexicalEnvironment.h" +#include "JSModuleEnvironment.h" #include "JSString.h" #include "JSWithScope.h" #include "LLIntCLoop.h" +#include "LLIntData.h" #include "LLIntThunks.h" -#include "LegacyProfiler.h" #include "LiteralParser.h" -#include "NameInstance.h" +#include "ModuleProgramCodeBlock.h" #include "ObjectPrototype.h" -#include "Operations.h" #include "Parser.h" +#include "ProgramCodeBlock.h" #include "ProtoCallFrame.h" #include "RegExpObject.h" -#include "RegExpPrototype.h" #include "Register.h" -#include "SamplingTool.h" +#include "ScopedArguments.h" +#include "StackAlignment.h" +#include "StackFrame.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 -#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()) -{ - if (!m_interpreter.m_errorHandlingModeReentry) - m_interpreter.stack().enableErrorStackReserve(); - m_interpreter.m_errorHandlingModeReentry++; -} - -Interpreter::ErrorHandlingMode::~ErrorHandlingMode() -{ - m_interpreter.m_errorHandlingModeReentry--; - ASSERT(m_interpreter.m_errorHandlingModeReentry >= 0); - if (!m_interpreter.m_errorHandlingModeReentry) - m_interpreter.stack().disableErrorStackReserve(); -} - JSValue eval(CallFrame* callFrame) { + VM& vm = callFrame->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + if (!callFrame->argumentCount()) return jsUndefined(); JSValue program = callFrame->argument(0); if (!program.isString()) return program; - - TopCallFrameSetter topCallFrame(callFrame->vm(), callFrame); + + TopCallFrameSetter topCallFrame(vm, callFrame); + JSGlobalObject* globalObject = callFrame->lexicalGlobalObject(); + if (!globalObject->evalEnabled()) { + throwException(callFrame, scope, createEvalError(callFrame, globalObject->evalDisabledErrorMessage())); + return jsUndefined(); + } String programSource = asString(program)->value(callFrame); - if (callFrame->hadException()) - return JSValue(); + RETURN_IF_EXCEPTION(scope, JSValue()); CallFrame* callerFrame = callFrame->callerFrame(); + CallSiteIndex callerCallSiteIndex = callerFrame->callSiteIndex(); 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(); + + DerivedContextType derivedContextType = callerUnlinkedCodeBlock->derivedContextType(); + if (!isArrowFunctionContext && callerUnlinkedCodeBlock->isClassContext()) { + derivedContextType = callerUnlinkedCodeBlock->isConstructor() + ? DerivedContextType::DerivedConstructorContext + : DerivedContextType::DerivedMethodContext; + } + + EvalContextType evalContextType; + if (isFunctionParseMode(callerUnlinkedCodeBlock->parseMode())) + evalContextType = EvalContextType::FunctionEvalContext; + else if (callerUnlinkedCodeBlock->codeType() == EvalCode) + evalContextType = callerUnlinkedCodeBlock->evalContextType(); + else + evalContextType = EvalContextType::None; + DirectEvalExecutable* eval = callerCodeBlock->directEvalCodeCache().tryGet(programSource, callerCallSiteIndex); 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()) + if (JSValue parsedObject = preparser.tryLiteralParse()) { + scope.release(); return parsedObject; + } } else { LiteralParser<UChar> preparser(callFrame, programSource.characters16(), programSource.length(), NonStrictJSON); - if (JSValue parsedObject = preparser.tryLiteralParse()) - return parsedObject; + if (JSValue parsedObject = preparser.tryLiteralParse()) { + scope.release(); + return parsedObject; + } } } // If the literal parser bailed, it should not have thrown exceptions. - ASSERT(!callFrame->vm().exception()); + ASSERT(!scope.exception()); - eval = callerCodeBlock->evalCodeCache().getSlow(callFrame, callerCodeBlock->ownerExecutable(), callerCodeBlock->isStrictMode(), programSource, callerScopeChain); + VariableEnvironment variablesUnderTDZ; + JSScope::collectClosureVariablesUnderTDZ(callerScopeChain, variablesUnderTDZ); + eval = DirectEvalExecutable::create(callFrame, makeSource(programSource, callerCodeBlock->source()->sourceOrigin()), callerCodeBlock->isStrictMode(), derivedContextType, isArrowFunctionContext, evalContextType, &variablesUnderTDZ); + ASSERT(!!scope.exception() == !eval); if (!eval) return jsUndefined(); + + callerCodeBlock->directEvalCodeCache().set(callFrame, callerCodeBlock, programSource, callerCallSiteIndex, eval); } JSValue thisValue = callerFrame->thisValue(); - Interpreter* interpreter = callFrame->vm().interpreter; + Interpreter* interpreter = vm.interpreter; + scope.release(); return interpreter->execute(eval, callFrame, thisValue, callerScopeChain); } -CallFrame* sizeAndAllocFrameForVarargs(CallFrame* callFrame, JSStack* stack, JSValue arguments, int firstFreeRegister) +unsigned sizeOfVarargs(CallFrame* callFrame, JSValue arguments, uint32_t firstVarArgOffset) { - if (!arguments) { // f.apply(x, arguments), with arguments unmodified. - unsigned argumentCountIncludingThis = callFrame->argumentCountIncludingThis(); - CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister - argumentCountIncludingThis - JSStack::CallFrameHeaderSize - 1); - if (argumentCountIncludingThis > Arguments::MaxArguments + 1 || !stack->grow(newCallFrame->registers())) { - callFrame->vm().throwException(callFrame, createStackOverflowError(callFrame)); - return 0; - } - return newCallFrame; - } + VM& vm = callFrame->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); - if (arguments.isUndefinedOrNull()) { - CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister - 1 - JSStack::CallFrameHeaderSize - 1); - if (!stack->grow(newCallFrame->registers())) { - callFrame->vm().throwException(callFrame, createStackOverflowError(callFrame)); + if (UNLIKELY(!arguments.isCell())) { + if (arguments.isUndefinedOrNull()) return 0; - } - return newCallFrame; + + throwException(callFrame, scope, createInvalidFunctionApplyParameterError(callFrame, arguments)); + return 0; } - - if (!arguments.isObject()) { - callFrame->vm().throwException(callFrame, createInvalidParameterError(callFrame, "Function.prototype.apply", arguments)); + + 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: + case SymbolType: + throwException(callFrame, scope, createInvalidFunctionApplyParameterError(callFrame, arguments)); return 0; + + default: + RELEASE_ASSERT(arguments.isObject()); + length = getLength(callFrame, jsCast<JSObject*>(cell)); + break; } + RETURN_IF_EXCEPTION(scope, 0); + + if (length >= firstVarArgOffset) + length -= firstVarArgOffset; + else + length = 0; + + return length; +} - if (asObject(arguments)->classInfo() == Arguments::info()) { - Arguments* argsObject = asArguments(arguments); - unsigned argCount = argsObject->length(callFrame); - CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister - CallFrame::offsetFor(argCount + 1)); - if (argCount > Arguments::MaxArguments || !stack->grow(newCallFrame->registers())) { - callFrame->vm().throwException(callFrame, createStackOverflowError(callFrame)); - return 0; - } - return newCallFrame; - } +unsigned sizeFrameForForwardArguments(CallFrame* callFrame, VM& vm, unsigned numUsedStackSlots) +{ + auto scope = DECLARE_THROW_SCOPE(vm); - if (isJSArray(arguments)) { - JSArray* array = asArray(arguments); - unsigned argCount = array->length(); - CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister - CallFrame::offsetFor(argCount + 1)); - if (argCount > Arguments::MaxArguments || !stack->grow(newCallFrame->registers())) { - callFrame->vm().throwException(callFrame, createStackOverflowError(callFrame)); - return 0; - } - return newCallFrame; - } + unsigned length = callFrame->argumentCount(); + CallFrame* calleeFrame = calleeFrameForVarargs(callFrame, numUsedStackSlots, length + 1); + if (UNLIKELY(!vm.ensureStackCapacityFor(calleeFrame->registers()))) + throwStackOverflowError(callFrame, scope); - JSObject* argObject = asObject(arguments); - unsigned argCount = argObject->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame); - CallFrame* newCallFrame = CallFrame::create(callFrame->registers() + firstFreeRegister - CallFrame::offsetFor(argCount + 1)); - if (argCount > Arguments::MaxArguments || !stack->grow(newCallFrame->registers())) { - callFrame->vm().throwException(callFrame, createStackOverflowError(callFrame)); - return 0; - } - return newCallFrame; + return length; } -void loadVarargs(CallFrame* callFrame, CallFrame* newCallFrame, JSValue thisValue, JSValue arguments) +unsigned sizeFrameForVarargs(CallFrame* callFrame, VM& vm, JSValue arguments, unsigned numUsedStackSlots, uint32_t firstVarArgOffset) { - if (!arguments) { // f.apply(x, arguments), with arguments unmodified. - unsigned argumentCountIncludingThis = callFrame->argumentCountIncludingThis(); + auto scope = DECLARE_THROW_SCOPE(vm); - newCallFrame->setArgumentCountIncludingThis(argumentCountIncludingThis); - newCallFrame->setThisValue(thisValue); - for (size_t i = 0; i < callFrame->argumentCount(); ++i) - newCallFrame->setArgument(i, callFrame->argumentAfterCapture(i)); - return; + unsigned length = sizeOfVarargs(callFrame, arguments, firstVarArgOffset); + RETURN_IF_EXCEPTION(scope, 0); + + CallFrame* calleeFrame = calleeFrameForVarargs(callFrame, numUsedStackSlots, length + 1); + if (UNLIKELY(length > maxArguments || !vm.ensureStackCapacityFor(calleeFrame->registers()))) { + throwStackOverflowError(callFrame, scope); + return 0; } - if (arguments.isUndefinedOrNull()) { - newCallFrame->setArgumentCountIncludingThis(1); - newCallFrame->setThisValue(thisValue); + return length; +} + +void loadVarargs(CallFrame* callFrame, VirtualRegister firstElementDest, JSValue arguments, uint32_t offset, uint32_t length) +{ + if (UNLIKELY(!arguments.isCell()) || !length) return; - } - if (asObject(arguments)->classInfo() == Arguments::info()) { - Arguments* argsObject = asArguments(arguments); - unsigned argCount = argsObject->length(callFrame); - newCallFrame->setArgumentCountIncludingThis(argCount + 1); - newCallFrame->setThisValue(thisValue); - argsObject->copyToArguments(callFrame, newCallFrame, argCount); + JSCell* cell = arguments.asCell(); + + switch (cell->type()) { + case DirectArgumentsType: + jsCast<DirectArguments*>(cell)->copyToArguments(callFrame, firstElementDest, offset, length); return; - } - - if (isJSArray(arguments)) { - JSArray* array = asArray(arguments); - unsigned argCount = array->length(); - newCallFrame->setArgumentCountIncludingThis(argCount + 1); - newCallFrame->setThisValue(thisValue); - array->copyToArguments(callFrame, newCallFrame, argCount); + 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); - JSObject* argObject = asObject(arguments); - unsigned argCount = argObject->get(callFrame, callFrame->propertyNames().length).toUInt32(callFrame); - newCallFrame->setArgumentCountIncludingThis(argCount + 1); + loadVarargs( + callFrame, + calleeFrameOffset + CallFrame::argumentOffset(0), + arguments, offset, length); + + newCallFrame->setArgumentCountIncludingThis(length + 1); +} + +void setupVarargsFrameAndSetThis(CallFrame* callFrame, CallFrame* newCallFrame, JSValue thisValue, JSValue arguments, uint32_t firstVarArgOffset, uint32_t length) +{ + setupVarargsFrame(callFrame, newCallFrame, arguments, firstVarArgOffset, length); newCallFrame->setThisValue(thisValue); - for (size_t i = 0; i < argCount; ++i) { - newCallFrame->setArgument(i, asObject(arguments)->get(callFrame, i)); - if (UNLIKELY(callFrame->vm().exception())) - return; - } } +void setupForwardArgumentsFrame(CallFrame* execCaller, CallFrame* execCallee, uint32_t length) +{ + ASSERT(length == execCaller->argumentCount()); + unsigned offset = execCaller->argumentOffset(0) * sizeof(Register); + memcpy(reinterpret_cast<char*>(execCallee) + offset, reinterpret_cast<char*>(execCaller) + offset, length * sizeof(Register)); + execCallee->setArgumentCountIncludingThis(length + 1); +} + +void setupForwardArgumentsFrameAndSetThis(CallFrame* execCaller, CallFrame* execCallee, JSValue thisValue, uint32_t length) +{ + setupForwardArgumentsFrame(execCaller, execCallee, length); + execCallee->setThisValue(thisValue); +} + + + Interpreter::Interpreter(VM& vm) - : m_sampleEntryDepth(0) - , m_vm(vm) - , m_stack(vm) - , m_errorHandlingModeReentry(0) + : m_vm(vm) +#if !ENABLE(JIT) + , m_cloopStack(vm) +#endif #if !ASSERT_DISABLED , m_initialized(false) #endif @@ -272,11 +318,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,10 +329,6 @@ void Interpreter::initialize(bool canUseJIT) #if !ASSERT_DISABLED m_initialized = true; #endif - -#if ENABLE(OPCODE_SAMPLING) - enableSampler(); -#endif } #ifdef NDEBUG @@ -313,7 +353,7 @@ public: { } - StackVisitor::Status operator()(StackVisitor& visitor) + StackVisitor::Status operator()(StackVisitor& visitor) const { if (!m_hasSkippedFirstFrame) { m_hasSkippedFirstFrame = true; @@ -329,7 +369,7 @@ public: } private: - bool m_hasSkippedFirstFrame; + mutable bool m_hasSkippedFirstFrame; const Register*& m_it; }; @@ -344,8 +384,8 @@ void Interpreter::dumpRegisters(CallFrame* callFrame) const Register* it; const Register* end; - it = callFrame->registers() + JSStack::ThisArgument + callFrame->argumentCount(); - end = callFrame->registers() + JSStack::ThisArgument - 1; + it = callFrame->registers() + CallFrameSlot::thisArgument + callFrame->argumentCount(); + end = callFrame->registers() + CallFrameSlot::thisArgument - 1; while (it > end) { JSValue v = it->jsValue(); int registerNumber = it - callFrame->registers(); @@ -359,9 +399,9 @@ void Interpreter::dumpRegisters(CallFrame* callFrame) --it; dataLogF("[CallerFrame] | %10p | %p \n", it, callFrame->callerFrame()); --it; - dataLogF("[Callee] | %10p | %p \n", it, callFrame->callee()); + dataLogF("[Callee] | %10p | %p \n", it, callFrame->jsCallee()); --it; - dataLogF("[ScopeChain] | %10p | %p \n", it, callFrame->scope()); + // FIXME: Remove the next decrement when the ScopeChain slot is removed from the call header --it; #if ENABLE(JIT) AbstractPC pc = callFrame->abstractReturnPC(callFrame->vm()); @@ -388,7 +428,7 @@ void Interpreter::dumpRegisters(CallFrame* callFrame) } dataLogF("-----------------------------------------------------------------------------\n"); - end = it - codeBlock->m_numCalleeRegisters + codeBlock->m_numVars; + end = it - codeBlock->m_numCalleeLocals + codeBlock->m_numVars; if (it != end) { do { JSValue v = (*it).jsValue(); @@ -405,160 +445,41 @@ void Interpreter::dumpRegisters(CallFrame* callFrame) bool Interpreter::isOpcode(Opcode opcode) { #if ENABLE(COMPUTED_GOTO_OPCODES) -#if !ENABLE(LLINT) - return static_cast<OpcodeID>(bitwise_cast<uintptr_t>(opcode)) <= op_end; -#else return opcode != HashTraits<Opcode>::emptyValue() && !HashTraits<Opcode>::isDeletedValue(opcode) && m_opcodeIDTable.contains(opcode); -#endif #else return opcode >= 0 && opcode <= op_end; #endif } -static bool unwindCallFrame(StackVisitor& visitor) -{ - CallFrame* callFrame = visitor->callFrame(); - CodeBlock* codeBlock = visitor->codeBlock(); - CodeBlock* oldCodeBlock = codeBlock; - JSScope* scope = callFrame->scope(); - - if (Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger()) { - if (callFrame->callee()) - debugger->returnEvent(callFrame); - else - debugger->didExecuteProgram(callFrame); - } - - JSValue activation; - if (oldCodeBlock->codeType() == FunctionCode && oldCodeBlock->needsActivation()) { -#if ENABLE(DFG_JIT) - RELEASE_ASSERT(!visitor->isInlinedFrame()); -#endif - activation = callFrame->uncheckedR(oldCodeBlock->activationRegister().offset()).jsValue(); - if (activation) - jsCast<JSActivation*>(activation)->tearOff(*scope->vm()); - } - - if (oldCodeBlock->codeType() == FunctionCode && oldCodeBlock->usesArguments()) { - if (Arguments* arguments = visitor->existingArguments()) { - if (activation) - arguments->didTearOffActivation(callFrame, jsCast<JSActivation*>(activation)); -#if ENABLE(DFG_JIT) - else if (visitor->isInlinedFrame()) - arguments->tearOff(callFrame, visitor->inlineCallFrame()); -#endif - else - arguments->tearOff(callFrame); - } - } - - CallFrame* callerFrame = callFrame->callerFrame(); - if (callerFrame->isVMEntrySentinel()) { - callFrame->vm().topCallFrame = callerFrame->vmEntrySentinelCallerFrame(); - return false; - } - return true; -} - -static StackFrameCodeType getStackFrameCodeType(StackVisitor& visitor) -{ - switch (visitor->codeType()) { - case StackVisitor::Frame::Eval: - return StackFrameEvalCode; - case StackVisitor::Frame::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); -} - -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) + GetStackTraceFunctor(VM& vm, Vector<StackFrame>& results, size_t framesToSkip, size_t capacity) : m_vm(vm) , m_results(results) - , m_remainingCapacityForFrameCapture(remainingCapacity) + , m_framesToSkip(framesToSkip) + , m_remainingCapacityForFrameCapture(capacity) { + m_results.reserveInitialCapacity(capacity); } - StackVisitor::Status operator()(StackVisitor& visitor) + StackVisitor::Status operator()(StackVisitor& visitor) const { - VM& vm = m_vm; + if (m_framesToSkip > 0) { + m_framesToSkip--; + return StackVisitor::Continue; + } + if (m_remainingCapacityForFrameCapture) { - if (visitor->isJSFrame()) { - CodeBlock* codeBlock = visitor->codeBlock(); - StackFrame s = { - Strong<JSObject>(vm, visitor->callee()), - getStackFrameCodeType(visitor), - Strong<ExecutableBase>(vm, codeBlock->ownerExecutable()), - Strong<UnlinkedCodeBlock>(vm, codeBlock->unlinkedCodeBlock()), - codeBlock->source(), - codeBlock->ownerExecutable()->lineNo(), - codeBlock->firstLineColumnOffset(), - codeBlock->sourceOffset(), - visitor->bytecodeOffset(), - visitor->sourceURL() - }; - m_results.append(s); + if (!visitor->isWasmFrame() + && !!visitor->codeBlock() + && !visitor->codeBlock()->unlinkedCodeBlock()->isBuiltinFunction()) { + m_results.append( + StackFrame(m_vm, visitor->callee(), visitor->codeBlock(), visitor->bytecodeOffset())); } else { - StackFrame s = { Strong<JSObject>(vm, visitor->callee()), StackFrameNativeCode, Strong<ExecutableBase>(), Strong<UnlinkedCodeBlock>(), 0, 0, 0, 0, 0, String()}; - m_results.append(s); + m_results.append( + StackFrame(m_vm, visitor->callee())); } m_remainingCapacityForFrameCapture--; @@ -570,50 +491,80 @@ public: private: VM& m_vm; Vector<StackFrame>& m_results; - size_t m_remainingCapacityForFrameCapture; + mutable size_t m_framesToSkip; + mutable size_t m_remainingCapacityForFrameCapture; }; -void Interpreter::getStackTrace(Vector<StackFrame>& results, size_t maxStackSize) +void Interpreter::getStackTrace(Vector<StackFrame>& results, size_t framesToSkip, size_t maxStackSize) { VM& vm = m_vm; - ASSERT(!vm.topCallFrame->isVMEntrySentinel()); CallFrame* callFrame = vm.topCallFrame; if (!callFrame) return; - GetStackTraceFunctor functor(vm, results, maxStackSize); + size_t framesCount = 0; + callFrame->iterate([&] (StackVisitor&) -> StackVisitor::Status { + framesCount++; + return StackVisitor::Continue; + }); + if (framesCount <= framesToSkip) + return; + + framesCount -= framesToSkip; + framesCount = std::min(maxStackSize, framesCount); + + GetStackTraceFunctor functor(vm, results, framesToSkip, framesCount); callFrame->iterate(functor); + ASSERT(results.size() == results.capacity()); } -JSString* Interpreter::stackTraceAsString(ExecState* exec, Vector<StackFrame> stackTrace) +JSString* Interpreter::stackTraceAsString(VM& vm, const 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))); + builder.append(String(stackTrace[i].toString(vm))); if (i != stackTrace.size() - 1) builder.append('\n'); } - return jsString(&exec->vm(), builder.toString()); + return jsString(&vm, builder.toString()); } -class GetExceptionHandlerFunctor { +ALWAYS_INLINE static HandlerInfo* findExceptionHandler(StackVisitor& visitor, CodeBlock* codeBlock, RequiredHandler requiredHandler) +{ + ASSERT(codeBlock); +#if ENABLE(DFG_JIT) + ASSERT(!visitor->isInlinedFrame()); +#endif + + CallFrame* callFrame = visitor->callFrame(); + unsigned exceptionHandlerIndex; + if (JITCode::isOptimizingJIT(codeBlock->jitType())) + exceptionHandlerIndex = callFrame->callSiteIndex().bits(); + else + exceptionHandlerIndex = callFrame->bytecodeOffset(); + + return codeBlock->handlerForIndex(exceptionHandlerIndex, requiredHandler); +} + +class GetCatchHandlerFunctor { public: - GetExceptionHandlerFunctor() + GetCatchHandlerFunctor() : m_handler(0) { } HandlerInfo* handler() { return m_handler; } - StackVisitor::Status operator()(StackVisitor& visitor) + StackVisitor::Status operator()(StackVisitor& visitor) const { + visitor.unwindToMachineCodeBlockFrame(); + CodeBlock* codeBlock = visitor->codeBlock(); if (!codeBlock) return StackVisitor::Continue; - unsigned bytecodeOffset = visitor->bytecodeOffset(); - m_handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset); + m_handler = findExceptionHandler(visitor, codeBlock, RequiredHandler::CatchHandler); if (m_handler) return StackVisitor::Done; @@ -621,9 +572,23 @@ public: } private: - HandlerInfo* m_handler; + mutable HandlerInfo* m_handler; }; +ALWAYS_INLINE static void notifyDebuggerOfUnwinding(CallFrame* callFrame) +{ + VM& vm = callFrame->vm(); + auto catchScope = DECLARE_CATCH_SCOPE(vm); + if (Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger()) { + SuspendExceptionScope scope(&vm); + if (jsDynamicCast<JSFunction*>(vm, callFrame->jsCallee())) + debugger->unwindEvent(callFrame); + else + debugger->didExecuteProgram(callFrame); + ASSERT_UNUSED(catchScope, !catchScope.exception()); + } +} + class UnwindFunctor { public: UnwindFunctor(CallFrame*& callFrame, bool isTermination, CodeBlock*& codeBlock, HandlerInfo*& handler) @@ -634,37 +599,82 @@ public: { } - StackVisitor::Status operator()(StackVisitor& visitor) + StackVisitor::Status operator()(StackVisitor& visitor) const { - VM& vm = m_callFrame->vm(); + visitor.unwindToMachineCodeBlockFrame(); m_callFrame = visitor->callFrame(); m_codeBlock = visitor->codeBlock(); - unsigned bytecodeOffset = visitor->bytecodeOffset(); - if (m_isTermination || !(m_handler = m_codeBlock->handlerForBytecodeOffset(bytecodeOffset))) { - if (!unwindCallFrame(visitor)) { - if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->exceptionUnwind(m_callFrame); - return StackVisitor::Done; + m_handler = nullptr; + if (!m_isTermination) { + if (m_codeBlock) { + m_handler = findExceptionHandler(visitor, m_codeBlock, RequiredHandler::AnyHandler); + if (m_handler) + return StackVisitor::Done; } - } else + } + + notifyDebuggerOfUnwinding(m_callFrame); + + copyCalleeSavesToVMEntryFrameCalleeSavesBuffer(visitor); + + bool shouldStopUnwinding = visitor->callerIsVMEntryFrame(); + if (shouldStopUnwinding) return StackVisitor::Done; return StackVisitor::Continue; } private: + void copyCalleeSavesToVMEntryFrameCalleeSavesBuffer(StackVisitor& visitor) const + { +#if ENABLE(JIT) && NUMBER_OF_CALLEE_SAVES_REGISTERS > 0 + RegisterAtOffsetList* currentCalleeSaves = visitor->calleeSaveRegisters(); + + 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(); + VMEntryRecord* record = vmEntryRecord(vm.topVMEntryFrame); + for (unsigned i = 0; i < registerCount; i++) { + RegisterAtOffset currentEntry = currentCalleeSaves->at(i); + if (dontCopyRegisters.get(currentEntry.reg())) + continue; + RegisterAtOffset* calleeSavesEntry = allCalleeSaves->find(currentEntry.reg()); + + record->calleeSaveRegistersBuffer[calleeSavesEntry->offsetAsIndex()] = *(frame + currentEntry.offsetAsIndex()); + } +#else + UNUSED_PARAM(visitor); +#endif + } + CallFrame*& m_callFrame; bool m_isTermination; CodeBlock*& m_codeBlock; HandlerInfo*& m_handler; }; -NEVER_INLINE HandlerInfo* Interpreter::unwind(CallFrame*& callFrame, JSValue& exceptionValue) +NEVER_INLINE HandlerInfo* Interpreter::unwind(VM& vm, CallFrame*& callFrame, Exception* exception, UnwindStart unwindStart) { + auto scope = DECLARE_CATCH_SCOPE(vm); + + if (unwindStart == UnwindFromCallerFrame) { + if (callFrame->callerFrameOrVMEntryFrame() == vm.topVMEntryFrame) + return nullptr; + + callFrame = callFrame->callerFrame(); + vm.topCallFrame = callFrame; + } + CodeBlock* codeBlock = callFrame->codeBlock(); - bool isTermination = false; + JSValue exceptionValue = exception->value(); ASSERT(!exceptionValue.isEmpty()); ASSERT(!exceptionValue.isCell() || exceptionValue.asCell()); // This shouldn't be possible (hence the assertions), but we're already in the slowest of @@ -672,60 +682,43 @@ NEVER_INLINE HandlerInfo* Interpreter::unwind(CallFrame*& callFrame, JSValue& ex if (exceptionValue.isEmpty() || (exceptionValue.isCell() && !exceptionValue.asCell())) exceptionValue = jsNull(); - if (exceptionValue.isObject()) { - isTermination = isTerminatedExecutionException(asObject(exceptionValue)); - } + ASSERT_UNUSED(scope, scope.exception() && scope.exception()->stack().size()); + + // Calculate an exception handler vPC, unwinding call frames as necessary. + HandlerInfo* handler = nullptr; + UnwindFunctor functor(callFrame, isTerminatedExecutionException(vm, exception), codeBlock, handler); + callFrame->iterate(functor); + if (!handler) + return nullptr; - ASSERT(callFrame->vm().exceptionStack().size()); + return handler; +} +void Interpreter::notifyDebuggerOfExceptionToBeThrown(CallFrame* callFrame, Exception* exception) +{ + VM& vm = callFrame->vm(); Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger(); - if (debugger && debugger->needsExceptionCallbacks()) { - // We need to clear the exception and the exception stack here in order to see if a new exception happens. - // Afterwards, the values are put back to continue processing this error. - ClearExceptionScope scope(&callFrame->vm()); + if (debugger && debugger->needsExceptionCallbacks() && !exception->didNotifyInspectorOfThrow()) { // This code assumes that if the debugger is enabled then there is no inlining. // If that assumption turns out to be false then we'll ignore the inlined call // frames. // https://bugs.webkit.org/show_bug.cgi?id=121754 - bool hasHandler; + bool hasCatchHandler; + bool isTermination = isTerminatedExecutionException(vm, exception); if (isTermination) - hasHandler = false; + hasCatchHandler = false; else { - GetExceptionHandlerFunctor functor; + GetCatchHandlerFunctor functor; callFrame->iterate(functor); - hasHandler = !!functor.handler(); + HandlerInfo* handler = functor.handler(); + ASSERT(!handler || handler->isCatchHandler()); + hasCatchHandler = !!handler; } - debugger->exception(callFrame, exceptionValue, hasHandler); + debugger->exception(callFrame, exception->value(), hasCatchHandler); } - - // Calculate an exception handler vPC, unwinding call frames as necessary. - HandlerInfo* handler = 0; - VM& vm = callFrame->vm(); - ASSERT(callFrame == vm.topCallFrame); - UnwindFunctor functor(callFrame, isTermination, codeBlock, handler); - callFrame->iterate(functor); - if (!handler) - return 0; - - if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->exceptionUnwind(callFrame); - - // Unwind the scope chain within the exception handler's call frame. - int targetScopeDepth = handler->scopeDepth; - if (codeBlock->needsActivation() && callFrame->uncheckedR(codeBlock->activationRegister().offset()).jsValue()) - ++targetScopeDepth; - - JSScope* scope = callFrame->scope(); - int scopeDelta = scope->depth() - targetScopeDepth; - RELEASE_ASSERT(scopeDelta >= 0); - - while (scopeDelta--) - scope = scope->next(); - callFrame->setScope(scope); - - return handler; + exception->setDidNotifyInspectorOfThrow(); } static inline JSValue checkedReturn(JSValue returnValue) @@ -740,36 +733,24 @@ static inline JSObject* checkedReturn(JSObject* 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) +JSValue Interpreter::executeProgram(const SourceCode& source, CallFrame* callFrame, JSObject* thisObj) { - SamplingScope samplingScope(this); - - JSScope* scope = callFrame->scope(); + JSScope* scope = thisObj->globalObject()->globalScope(); VM& vm = *scope->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); - ASSERT(!vm.exception()); - ASSERT(!vm.isCollectorBusy()); - if (vm.isCollectorBusy()) + ProgramExecutable* program = ProgramExecutable::create(callFrame, source); + ASSERT(throwScope.exception() || program); + RETURN_IF_EXCEPTION(throwScope, { }); + + ASSERT(!throwScope.exception()); + ASSERT(!vm.isCollectorBusyOnCurrentThread()); + RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); + if (vm.isCollectorBusyOnCurrentThread()) return jsNull(); - VMEntryScope entryScope(vm, scope->globalObject()); - if (!vm.isSafeToRecurse()) - return checkedReturn(throwStackOverflowError(callFrame)); + if (UNLIKELY(!vm.isSafeToRecurseSoft())) + return checkedReturn(throwStackOverflowError(callFrame, throwScope)); // 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 @@ -777,7 +758,7 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J Vector<JSONPData> JSONPData; bool parseResult; - const String programSource = program->source().toString(); + StringView programSource = program->source().view(); if (programSource.isNull()) return jsUndefined(); if (programSource.is8Bit()) { @@ -788,6 +769,7 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J parseResult = literalParser.tryJSONPParse(JSONPData, scope->globalObject()->globalObjectMethodTable()->supportsRichSourceInfo(scope->globalObject())); } + RETURN_IF_EXCEPTION(throwScope, { }); if (parseResult) { JSGlobalObject* globalObject = scope->globalObject(); JSValue result; @@ -808,23 +790,22 @@ 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)) { + RETURN_IF_EXCEPTION(throwScope, JSValue()); if (entry) - return callFrame->vm().throwException(callFrame, createUndefinedVariableError(globalObject->globalExec(), JSONPPath[i].m_pathEntryName)); + return throwException(callFrame, throwScope, 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(); + RETURN_IF_EXCEPTION(throwScope, JSValue()); continue; } case JSONPPathEntryTypeLookup: { - baseObject = baseObject.get(callFrame, JSONPPath[i].m_pathIndex); - if (callFrame->hadException()) - return jsUndefined(); + baseObject = baseObject.get(callFrame, static_cast<unsigned>(JSONPPath[i].m_pathIndex)); + RETURN_IF_EXCEPTION(throwScope, JSValue()); continue; } default: @@ -836,30 +817,26 @@ JSValue Interpreter::execute(ProgramExecutable* program, CallFrame* callFrame, J switch (JSONPPath.last().m_type) { case JSONPPathEntryTypeCall: { JSValue function = baseObject.get(callFrame, JSONPPath.last().m_pathEntryName); - if (callFrame->hadException()) - return jsUndefined(); + RETURN_IF_EXCEPTION(throwScope, JSValue()); CallData callData; CallType callType = getCallData(function, callData); - if (callType == CallTypeNone) - return callFrame->vm().throwException(callFrame, createNotAFunctionError(callFrame, function)); + if (callType == CallType::None) + return throwException(callFrame, throwScope, 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(); + RETURN_IF_EXCEPTION(throwScope, JSValue()); break; } case JSONPPathEntryTypeDot: { baseObject.put(callFrame, JSONPPath.last().m_pathEntryName, JSONPValue, slot); - if (callFrame->hadException()) - return jsUndefined(); + RETURN_IF_EXCEPTION(throwScope, JSValue()); break; } case JSONPPathEntryTypeLookup: { baseObject.putByIndex(callFrame, JSONPPath.last().m_pathIndex, JSONPValue, slot.isStrictMode()); - if (callFrame->hadException()) - return jsUndefined(); + RETURN_IF_EXCEPTION(throwScope, JSValue()); break; } default: @@ -874,210 +851,190 @@ 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)); + JSObject* error = program->initializeGlobalProperties(vm, callFrame, scope); + ASSERT(!throwScope.exception() || !error); + if (UNLIKELY(error)) + return checkedReturn(throwException(callFrame, throwScope, error)); - if (JSObject* error = program->prepareForExecution(callFrame, scope, CodeForCall)) - return checkedReturn(callFrame->vm().throwException(callFrame, error)); + ProgramCodeBlock* codeBlock; + { + CodeBlock* tempCodeBlock; + JSObject* error = program->prepareForExecution<ProgramExecutable>(vm, nullptr, scope, CodeForCall, tempCodeBlock); + ASSERT(throwScope.exception() == reinterpret_cast<Exception*>(error)); + if (UNLIKELY(error)) + return checkedReturn(error); + codeBlock = jsCast<ProgramCodeBlock*>(tempCodeBlock); + } - ProgramCodeBlock* codeBlock = program->codeBlock(); + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) + return throwTerminatedExecutionException(callFrame, throwScope); - if (UNLIKELY(vm.watchdog.didFire(callFrame))) - return throwTerminatedExecutionException(callFrame); + if (scope->structure()->isUncacheableDictionary()) + scope->flattenDictionaryObject(vm); ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. - if (UNLIKELY(!m_stack.entryCheck(codeBlock, 1))) - return checkedReturn(throwStackOverflowError(callFrame)); - ProtoCallFrame protoCallFrame; - protoCallFrame.init(codeBlock, scope, 0, thisObj, 1); - - if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->willExecute(callFrame, program->sourceURL(), program->lineNo()); + protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), thisObj, 1); // Execute the code: - JSValue result; - { - SamplingTool::CallRecord callRecord(m_sampler.get()); - Watchdog::Scope watchdogScope(vm.watchdog); - - result = program->generatedJITCode()->execute(&vm, &protoCallFrame, m_stack.getTopOfStack()); - } - - if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->didExecute(callFrame, program->sourceURL(), program->lineNo()); - + throwScope.release(); + JSValue result = program->generatedJITCode()->execute(&vm, &protoCallFrame); 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()) + auto throwScope = DECLARE_THROW_SCOPE(vm); + + ASSERT(!throwScope.exception()); + ASSERT(!vm.isCollectorBusyOnCurrentThread()); + if (vm.isCollectorBusyOnCurrentThread()) return jsNull(); - bool isJSCall = (callType == CallTypeJS); - JSScope* scope; + bool isJSCall = (callType == CallType::JS); + 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 { - ASSERT(callType == CallTypeHost); - scope = callFrame->scope(); + globalObject = scope->globalObject(); + } else { + ASSERT(callType == CallType::Host); + globalObject = function->globalObject(); } - VMEntryScope entryScope(vm, scope->globalObject()); - if (!vm.isSafeToRecurse()) - return checkedReturn(throwStackOverflowError(callFrame)); + VMEntryScope entryScope(vm, globalObject); + if (UNLIKELY(!vm.isSafeToRecurseSoft())) + return checkedReturn(throwStackOverflowError(callFrame, throwScope)); if (isJSCall) { // Compile the callee: - JSObject* compileError = callData.js.functionExecutable->prepareForExecution(callFrame, scope, CodeForCall); - if (UNLIKELY(!!compileError)) { - return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); - } - newCodeBlock = callData.js.functionExecutable->codeBlockForCall(); + JSObject* compileError = callData.js.functionExecutable->prepareForExecution<FunctionExecutable>(vm, jsCast<JSFunction*>(function), scope, CodeForCall, newCodeBlock); + ASSERT(throwScope.exception() == reinterpret_cast<Exception*>(compileError)); + if (UNLIKELY(!!compileError)) + return checkedReturn(compileError); + ASSERT(!!newCodeBlock); newCodeBlock->m_shouldAlwaysBeInlined = false; } else newCodeBlock = 0; - if (UNLIKELY(vm.watchdog.didFire(callFrame))) - return throwTerminatedExecutionException(callFrame); - - if (UNLIKELY(!m_stack.entryCheck(newCodeBlock, argsCount))) - return checkedReturn(throwStackOverflowError(callFrame)); + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) + return throwTerminatedExecutionException(callFrame, throwScope); ProtoCallFrame protoCallFrame; - protoCallFrame.init(newCodeBlock, scope, function, thisValue, argsCount, args.data()); - - if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->willExecute(callFrame, function); + protoCallFrame.init(newCodeBlock, function, thisValue, argsCount, args.data()); JSValue result; { - SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSCall); - Watchdog::Scope watchdogScope(vm.watchdog); - // Execute the code: - if (isJSCall) - result = callData.js.functionExecutable->generatedJITCodeForCall()->execute(&vm, &protoCallFrame, m_stack.getTopOfStack()); - else - result = JSValue::decode(callToNativeFunction(reinterpret_cast<void*>(callData.native.function), &vm.topCallFrame, &protoCallFrame, m_stack.getTopOfStack())); + if (isJSCall) { + throwScope.release(); + result = callData.js.functionExecutable->generatedJITCodeForCall()->execute(&vm, &protoCallFrame); + } else { + result = JSValue::decode(vmEntryToNative(reinterpret_cast<void*>(callData.native.function), &vm, &protoCallFrame)); + RETURN_IF_EXCEPTION(throwScope, JSValue()); + } } - 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) +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()); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + ASSERT(!throwScope.exception()); + ASSERT(!vm.isCollectorBusyOnCurrentThread()); // 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)); + if (vm.isCollectorBusyOnCurrentThread()) + return checkedReturn(throwStackOverflowError(callFrame, throwScope)); - bool isJSConstruct = (constructType == ConstructTypeJS); - JSScope* scope; + bool isJSConstruct = (constructType == ConstructType::JS); + 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 { - ASSERT(constructType == ConstructTypeHost); - scope = callFrame->scope(); + globalObject = scope->globalObject(); + } else { + ASSERT(constructType == ConstructType::Host); + globalObject = constructor->globalObject(); } - VMEntryScope entryScope(vm, scope->globalObject()); - if (!vm.isSafeToRecurse()) - return checkedReturn(throwStackOverflowError(callFrame)); + VMEntryScope entryScope(vm, globalObject); + if (UNLIKELY(!vm.isSafeToRecurseSoft())) + return checkedReturn(throwStackOverflowError(callFrame, throwScope)); if (isJSConstruct) { // Compile the callee: - JSObject* compileError = constructData.js.functionExecutable->prepareForExecution(callFrame, scope, CodeForConstruct); - if (UNLIKELY(!!compileError)) { - return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); - } - newCodeBlock = constructData.js.functionExecutable->codeBlockForConstruct(); + JSObject* compileError = constructData.js.functionExecutable->prepareForExecution<FunctionExecutable>(vm, jsCast<JSFunction*>(constructor), scope, CodeForConstruct, newCodeBlock); + ASSERT(throwScope.exception() == reinterpret_cast<Exception*>(compileError)); + if (UNLIKELY(!!compileError)) + return checkedReturn(compileError); + ASSERT(!!newCodeBlock); newCodeBlock->m_shouldAlwaysBeInlined = false; } else newCodeBlock = 0; - if (UNLIKELY(vm.watchdog.didFire(callFrame))) - return throwTerminatedExecutionException(callFrame); - - if (UNLIKELY(!m_stack.entryCheck(newCodeBlock, argsCount))) - return checkedReturn(throwStackOverflowError(callFrame)); + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) + return throwTerminatedExecutionException(callFrame, throwScope); ProtoCallFrame protoCallFrame; - protoCallFrame.init(newCodeBlock, scope, constructor, jsUndefined(), argsCount, args.data()); - - if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->willExecute(callFrame, constructor); + protoCallFrame.init(newCodeBlock, constructor, newTarget, argsCount, args.data()); JSValue result; { - SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSConstruct); - Watchdog::Scope watchdogScope(vm.watchdog); - // Execute the code. if (isJSConstruct) - result = constructData.js.functionExecutable->generatedJITCodeForConstruct()->execute(&vm, &protoCallFrame, m_stack.getTopOfStack()); + result = constructData.js.functionExecutable->generatedJITCodeForConstruct()->execute(&vm, &protoCallFrame); else { - result = JSValue::decode(callToNativeFunction(reinterpret_cast<void*>(constructData.native.function), &vm.topCallFrame, &protoCallFrame, m_stack.getTopOfStack())); + result = JSValue::decode(vmEntryToNative(reinterpret_cast<void*>(constructData.native.function), &vm, &protoCallFrame)); - if (!callFrame->hadException()) + if (LIKELY(!throwScope.exception())) RELEASE_ASSERT(result.isObject()); } } - if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->didExecute(callFrame, constructor); - - if (callFrame->hadException()) - return 0; + RETURN_IF_EXCEPTION(throwScope, 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) +CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionExecutable, CallFrame* callFrame, ProtoCallFrame* protoCallFrame, JSFunction* function, int argumentCountIncludingThis, JSScope* scope, const ArgList& args) { VM& vm = *scope->vm(); - ASSERT(!vm.exception()); + auto throwScope = DECLARE_THROW_SCOPE(vm); + ASSERT_UNUSED(throwScope, !throwScope.exception()); - if (vm.isCollectorBusy()) + if (vm.isCollectorBusyOnCurrentThread()) return CallFrameClosure(); // Compile the callee: - JSObject* error = functionExecutable->prepareForExecution(callFrame, scope, CodeForCall); - if (error) { - callFrame->vm().throwException(callFrame, error); + CodeBlock* newCodeBlock; + JSObject* error = functionExecutable->prepareForExecution<FunctionExecutable>(vm, function, scope, CodeForCall, newCodeBlock); + ASSERT(throwScope.exception() == reinterpret_cast<Exception*>(error)); + if (UNLIKELY(error)) return CallFrameClosure(); - } - CodeBlock* newCodeBlock = functionExecutable->codeBlockForCall(); newCodeBlock->m_shouldAlwaysBeInlined = false; size_t argsCount = argumentCountIncludingThis; - if (UNLIKELY(!m_stack.entryCheck(newCodeBlock, argsCount))) { - throwStackOverflowError(callFrame); - return CallFrameClosure(); - } - - protoCallFrame->init(newCodeBlock, scope, function, jsUndefined(), argsCount, args); + protoCallFrame->init(newCodeBlock, function, jsUndefined(), argsCount, args.data()); // Return the successful closure: CallFrameClosure result = { callFrame, protoCallFrame, function, functionExecutable, &vm, scope, newCodeBlock->numParameters(), argumentCountIncludingThis }; return result; @@ -1086,32 +1043,21 @@ CallFrameClosure Interpreter::prepareForRepeatCall(FunctionExecutable* functionE JSValue Interpreter::execute(CallFrameClosure& closure) { VM& vm = *closure.vm; - SamplingScope samplingScope(this); - - ASSERT(!vm.isCollectorBusy()); - if (vm.isCollectorBusy()) + auto throwScope = DECLARE_THROW_SCOPE(vm); + + ASSERT(!vm.isCollectorBusyOnCurrentThread()); + RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); + if (vm.isCollectorBusyOnCurrentThread()) return jsNull(); StackStats::CheckPoint stackCheckPoint; - closure.resetCallFrame(); - - if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->willExecute(closure.oldCallFrame, closure.function); - if (UNLIKELY(vm.watchdog.didFire(closure.oldCallFrame))) - return throwTerminatedExecutionException(closure.oldCallFrame); + if (UNLIKELY(vm.shouldTriggerTermination(closure.oldCallFrame))) + return throwTerminatedExecutionException(closure.oldCallFrame, throwScope); // Execute the code: - JSValue result; - { - SamplingTool::CallRecord callRecord(m_sampler.get()); - Watchdog::Scope watchdogScope(vm.watchdog); - - result = closure.functionExecutable->generatedJITCodeForCall()->execute(&vm, closure.protoCallFrame, m_stack.getTopOfStack()); - } - - if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->didExecute(closure.oldCallFrame, closure.function); + throwScope.release(); + JSValue result = closure.functionExecutable->generatedJITCodeForCall()->execute(&vm, closure.protoCallFrame); return checkedReturn(result); } @@ -1119,50 +1065,89 @@ JSValue Interpreter::execute(CallFrameClosure& closure) JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue thisValue, JSScope* scope) { VM& vm = *scope->vm(); - SamplingScope samplingScope(this); - + auto throwScope = DECLARE_THROW_SCOPE(vm); + ASSERT(scope->vm() == &callFrame->vm()); - ASSERT(!vm.exception()); - ASSERT(!vm.isCollectorBusy()); - if (vm.isCollectorBusy()) + ASSERT(!throwScope.exception()); + ASSERT(!vm.isCollectorBusyOnCurrentThread()); + RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); + if (vm.isCollectorBusyOnCurrentThread()) return jsNull(); VMEntryScope entryScope(vm, scope->globalObject()); - if (!vm.isSafeToRecurse()) - return checkedReturn(throwStackOverflowError(callFrame)); + if (UNLIKELY(!vm.isSafeToRecurseSoft())) + return checkedReturn(throwStackOverflowError(callFrame, throwScope)); unsigned numVariables = eval->numVariables(); int numFunctions = eval->numberOfFunctionDecls(); JSScope* variableObject; if ((numVariables || numFunctions) && eval->isStrictMode()) { - scope = StrictEvalActivation::create(callFrame); + scope = StrictEvalActivation::create(callFrame, scope); variableObject = scope; } else { for (JSScope* node = scope; ; node = node->next()) { RELEASE_ASSERT(node); - if (node->isVariableObject() && !node->isNameScopeObject()) { + if (node->isGlobalObject()) { variableObject = node; break; + } + if (node->isJSLexicalEnvironment()) { + JSLexicalEnvironment* lexicalEnvironment = jsCast<JSLexicalEnvironment*>(node); + if (lexicalEnvironment->symbolTable()->scopeType() == SymbolTable::ScopeType::VarScope) { + variableObject = node; + break; + } } } } - JSObject* compileError = eval->prepareForExecution(callFrame, scope, CodeForCall); - if (UNLIKELY(!!compileError)) - return checkedReturn(callFrame->vm().throwException(callFrame, compileError)); - EvalCodeBlock* codeBlock = eval->codeBlock(); + EvalCodeBlock* codeBlock; + { + CodeBlock* tempCodeBlock; + JSObject* compileError = eval->prepareForExecution<EvalExecutable>(vm, nullptr, scope, CodeForCall, tempCodeBlock); + ASSERT(throwScope.exception() == reinterpret_cast<Exception*>(compileError)); + if (UNLIKELY(!!compileError)) + return checkedReturn(compileError); + codeBlock = jsCast<EvalCodeBlock*>(tempCodeBlock); + } + + // 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(throwTypeError(callFrame, throwScope, 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(throwTypeError(callFrame, throwScope, makeString("Can't create duplicate global variable in eval: '", String(function->name().impl()), "'"))); + } + } + } + + if (variableObject->structure()->isUncacheableDictionary()) + variableObject->flattenDictionaryObject(vm); if (numVariables || numFunctions) { BatchedTransitionOptimizer optimizer(vm, variableObject); - if (variableObject->next()) - variableObject->globalObject()->varInjectionWatchpoint()->fireAll(); + if (variableObject->next() && !eval->isStrictMode()) + variableObject->globalObject()->varInjectionWatchpoint()->fireAll(vm, "Executed eval, fired VarInjection watchpoint"); for (unsigned i = 0; i < numVariables; ++i) { const Identifier& ident = codeBlock->variable(i); - if (!variableObject->hasProperty(callFrame, ident)) { + bool hasProperty = variableObject->hasProperty(callFrame, ident); + RETURN_IF_EXCEPTION(throwScope, checkedReturn(throwScope.exception())); + if (!hasProperty) { PutPropertySlot slot(variableObject); variableObject->methodTable()->put(variableObject, callFrame, ident, jsUndefined(), slot); + RETURN_IF_EXCEPTION(throwScope, checkedReturn(throwScope.exception())); } } @@ -1170,101 +1155,107 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSValue FunctionExecutable* function = codeBlock->functionDecl(i); PutPropertySlot slot(variableObject); variableObject->methodTable()->put(variableObject, callFrame, function->name(), JSFunction::create(vm, function, scope), slot); + RETURN_IF_EXCEPTION(throwScope, checkedReturn(throwScope.exception())); } } - if (UNLIKELY(vm.watchdog.didFire(callFrame))) - return throwTerminatedExecutionException(callFrame); + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) + return throwTerminatedExecutionException(callFrame, throwScope); ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. - if (UNLIKELY(!m_stack.entryCheck(codeBlock, 1))) - return checkedReturn(throwStackOverflowError(callFrame)); - ProtoCallFrame protoCallFrame; - protoCallFrame.init(codeBlock, scope, 0, thisValue, 1); - - if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->willExecute(callFrame, eval->sourceURL(), eval->lineNo()); + protoCallFrame.init(codeBlock, JSCallee::create(vm, scope->globalObject(), scope), thisValue, 1); // Execute the code: - JSValue result; - { - SamplingTool::CallRecord callRecord(m_sampler.get()); - Watchdog::Scope watchdogScope(vm.watchdog); + throwScope.release(); + JSValue result = eval->generatedJITCode()->execute(&vm, &protoCallFrame); + + return checkedReturn(result); +} - result = eval->generatedJITCode()->execute(&vm, &protoCallFrame, m_stack.getTopOfStack()); +JSValue Interpreter::execute(ModuleProgramExecutable* executable, CallFrame* callFrame, JSModuleEnvironment* scope) +{ + VM& vm = *scope->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + ASSERT(scope->vm() == &callFrame->vm()); + ASSERT(!throwScope.exception()); + ASSERT(!vm.isCollectorBusyOnCurrentThread()); + RELEASE_ASSERT(vm.currentThreadIsHoldingAPILock()); + if (vm.isCollectorBusyOnCurrentThread()) + return jsNull(); + + VMEntryScope entryScope(vm, scope->globalObject()); + if (UNLIKELY(!vm.isSafeToRecurseSoft())) + return checkedReturn(throwStackOverflowError(callFrame, throwScope)); + + ModuleProgramCodeBlock* codeBlock; + { + CodeBlock* tempCodeBlock; + JSObject* compileError = executable->prepareForExecution<ModuleProgramExecutable>(vm, nullptr, scope, CodeForCall, tempCodeBlock); + ASSERT(throwScope.exception() == reinterpret_cast<Exception*>(compileError)); + if (UNLIKELY(!!compileError)) + return checkedReturn(compileError); + codeBlock = jsCast<ModuleProgramCodeBlock*>(tempCodeBlock); } - if (LegacyProfiler* profiler = vm.enabledProfiler()) - profiler->didExecute(callFrame, eval->sourceURL(), eval->lineNo()); + if (UNLIKELY(vm.shouldTriggerTermination(callFrame))) + return throwTerminatedExecutionException(callFrame, throwScope); + + if (scope->structure()->isUncacheableDictionary()) + scope->flattenDictionaryObject(vm); + + 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); + + // Execute the code: + throwScope.release(); + JSValue result = executable->generatedJITCode()->execute(&vm, &protoCallFrame); return checkedReturn(result); } -NEVER_INLINE void Interpreter::debug(CallFrame* callFrame, DebugHookID debugHookID) +NEVER_INLINE void Interpreter::debug(CallFrame* callFrame, DebugHookType debugHookType) { + VM& vm = callFrame->vm(); + auto scope = DECLARE_CATCH_SCOPE(vm); Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger(); if (!debugger) return; - ASSERT(callFrame->codeBlock()->hasDebuggerRequests() || callFrame->hadException()); - switch (debugHookID) { + ASSERT(callFrame->codeBlock()->hasDebuggerRequests()); + ASSERT_UNUSED(scope, !scope.exception()); + + switch (debugHookType) { case DidEnterCallFrame: debugger->callEvent(callFrame); - return; + break; case WillLeaveCallFrame: debugger->returnEvent(callFrame); - return; + break; case WillExecuteStatement: debugger->atStatement(callFrame); - return; + break; + case WillExecuteExpression: + debugger->atExpression(callFrame); + break; case WillExecuteProgram: debugger->willExecuteProgram(callFrame); - return; + break; case DidExecuteProgram: debugger->didExecuteProgram(callFrame); - return; + break; case DidReachBreakpoint: debugger->didReachBreakpoint(callFrame); - return; - } -} - -void Interpreter::enableSampler() -{ -#if ENABLE(OPCODE_SAMPLING) - if (!m_sampler) { - m_sampler = adoptPtr(new SamplingTool(this)); - m_sampler->setup(); + break; } -#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 + ASSERT(!scope.exception()); } } // namespace JSC |