bool BaselineFrame::initForOsr(InterpreterFrame *fp, uint32_t numStackValues) { mozilla::PodZero(this); scopeChain_ = fp->scopeChain(); if (fp->hasCallObjUnchecked()) flags_ |= BaselineFrame::HAS_CALL_OBJ; if (fp->isEvalFrame()) { flags_ |= BaselineFrame::EVAL; evalScript_ = fp->script(); } if (fp->script()->needsArgsObj() && fp->hasArgsObj()) { flags_ |= BaselineFrame::HAS_ARGS_OBJ; argsObj_ = &fp->argsObj(); } if (fp->hasHookData()) { flags_ |= BaselineFrame::HAS_HOOK_DATA; hookData_ = fp->hookData(); } if (fp->hasReturnValue()) setReturnValue(fp->returnValue()); if (fp->hasPushedSPSFrame()) flags_ |= BaselineFrame::HAS_PUSHED_SPS_FRAME; frameSize_ = BaselineFrame::FramePointerOffset + BaselineFrame::Size() + numStackValues * sizeof(Value); JS_ASSERT(numValueSlots() == numStackValues); for (uint32_t i = 0; i < numStackValues; i++) *valueSlot(i) = fp->slots()[i]; JSContext *cx = GetJSContextFromJitCode(); if (cx->compartment()->debugMode()) { // In debug mode, update any Debugger.Frame objects for the // InterpreterFrame to point to the BaselineFrame. // The caller pushed a fake return address. ScriptFrameIter, used by the // debugger, wants a valid return address, but it's okay to just pick one. // In debug mode there's always at least 1 ICEntry (since there are always // debug prologue/epilogue calls). IonFrameIterator iter(cx); JS_ASSERT(iter.returnAddress() == nullptr); BaselineScript *baseline = fp->script()->baselineScript(); iter.current()->setReturnAddress(baseline->returnAddressForIC(baseline->icEntry(0))); if (!Debugger::handleBaselineOsr(cx, fp, this)) return false; } return true; }
ErrorCopier::~ErrorCopier() { JSContext* cx = ac->context()->asJSContext(); if (ac->origin() != cx->compartment() && cx->isExceptionPending()) { RootedValue exc(cx); if (cx->getPendingException(&exc) && exc.isObject() && exc.toObject().is<ErrorObject>()) { cx->clearPendingException(); ac.reset(); Rooted<ErrorObject*> errObj(cx, &exc.toObject().as<ErrorObject>()); JSObject* copyobj = CopyErrorObject(cx, errObj); if (copyobj) cx->setPendingException(ObjectValue(*copyobj)); } } }
ErrorCopier::~ErrorCopier() { JSContext *cx = ac.ref().context()->asJSContext(); if (ac.ref().origin() != cx->compartment() && cx->isExceptionPending()) { RootedValue exc(cx, cx->getPendingException()); if (exc.isObject() && exc.toObject().is<ErrorObject>() && exc.toObject().as<ErrorObject>().getExnPrivate()) { cx->clearPendingException(); ac.destroy(); Rooted<JSObject*> errObj(cx, &exc.toObject()); JSObject *copyobj = js_CopyErrorObject(cx, errObj, scope); if (copyobj) cx->setPendingException(ObjectValue(*copyobj)); } } }
ErrorCopier::~ErrorCopier() { JSContext* cx = ac->context(); // The provenance of Debugger.DebuggeeWouldRun is the topmost locking // debugger compartment; it should not be copied around. if (ac->origin() != cx->compartment() && cx->isExceptionPending() && !cx->isThrowingDebuggeeWouldRun()) { RootedValue exc(cx); if (cx->getPendingException(&exc) && exc.isObject() && exc.toObject().is<ErrorObject>()) { cx->clearPendingException(); ac.reset(); Rooted<ErrorObject*> errObj(cx, &exc.toObject().as<ErrorObject>()); JSObject* copyobj = CopyErrorObject(cx, errObj); if (copyobj) cx->setPendingException(ObjectValue(*copyobj)); } } }
JSScript* GlobalHelperThreadState::finishParseTask(JSContext* maybecx, JSRuntime* rt, void* token) { ScopedJSDeletePtr<ParseTask> parseTask; // The token is a ParseTask* which should be in the finished list. // Find and remove its entry. { AutoLockHelperThreadState lock; ParseTaskVector& finished = parseFinishedList(); for (size_t i = 0; i < finished.length(); i++) { if (finished[i] == token) { parseTask = finished[i]; remove(finished, &i); break; } } } MOZ_ASSERT(parseTask); if (!maybecx) { LeaveParseTaskZone(rt, parseTask); return nullptr; } JSContext* cx = maybecx; MOZ_ASSERT(cx->compartment()); // Make sure we have all the constructors we need for the prototype // remapping below, since we can't GC while that's happening. Rooted<GlobalObject*> global(cx, &cx->global()->as<GlobalObject>()); if (!EnsureParserCreatedClasses(cx)) { LeaveParseTaskZone(rt, parseTask); return nullptr; } mergeParseTaskCompartment(rt, parseTask, global, cx->compartment()); if (!parseTask->finish(cx)) return nullptr; RootedScript script(rt, parseTask->script); assertSameCompartment(cx, script); // Report any error or warnings generated during the parse, and inform the // debugger about the compiled scripts. for (size_t i = 0; i < parseTask->errors.length(); i++) parseTask->errors[i]->throwError(cx); if (parseTask->overRecursed) ReportOverRecursed(cx); if (cx->isExceptionPending()) return nullptr; if (!script) { // No error was reported, but no script produced. Assume we hit out of // memory. ReportOutOfMemory(cx); return nullptr; } // The Debugger only needs to be told about the topmost script that was compiled. Debugger::onNewScript(cx, script); // Update the compressed source table with the result. This is normally // called by setCompressedSource when compilation occurs on the main thread. if (script->scriptSource()->hasCompressedSource()) script->scriptSource()->updateCompressedSourceSet(rt); return script; }
JSScript * GlobalHelperThreadState::finishParseTask(JSContext *maybecx, JSRuntime *rt, void *token) { ScopedJSDeletePtr<ParseTask> parseTask; // The token is a ParseTask* which should be in the finished list. // Find and remove its entry. { AutoLockHelperThreadState lock; ParseTaskVector &finished = parseFinishedList(); for (size_t i = 0; i < finished.length(); i++) { if (finished[i] == token) { parseTask = finished[i]; remove(finished, &i); break; } } } MOZ_ASSERT(parseTask); if (!maybecx) { LeaveParseTaskZone(rt, parseTask); return nullptr; } JSContext *cx = maybecx; MOZ_ASSERT(cx->compartment()); // Make sure we have all the constructors we need for the prototype // remapping below, since we can't GC while that's happening. Rooted<GlobalObject*> global(cx, &cx->global()->as<GlobalObject>()); if (!GlobalObject::ensureConstructor(cx, global, JSProto_Object) || !GlobalObject::ensureConstructor(cx, global, JSProto_Array) || !GlobalObject::ensureConstructor(cx, global, JSProto_Function) || !GlobalObject::ensureConstructor(cx, global, JSProto_RegExp) || !GlobalObject::ensureConstructor(cx, global, JSProto_Iterator)) { LeaveParseTaskZone(rt, parseTask); return nullptr; } LeaveParseTaskZone(rt, parseTask); // Point the prototypes of any objects in the script's compartment to refer // to the corresponding prototype in the new compartment. This will briefly // create cross compartment pointers, which will be fixed by the // MergeCompartments call below. for (gc::ZoneCellIter iter(parseTask->cx->zone(), gc::FINALIZE_TYPE_OBJECT); !iter.done(); iter.next()) { types::TypeObject *object = iter.get<types::TypeObject>(); TaggedProto proto(object->proto()); if (!proto.isObject()) continue; JSProtoKey key = JS::IdentifyStandardPrototype(proto.toObject()); if (key == JSProto_Null) continue; MOZ_ASSERT(key == JSProto_Object || key == JSProto_Array || key == JSProto_Function || key == JSProto_RegExp || key == JSProto_Iterator); JSObject *newProto = GetBuiltinPrototypePure(global, key); MOZ_ASSERT(newProto); object->setProtoUnchecked(TaggedProto(newProto)); } // Move the parsed script and all its contents into the desired compartment. gc::MergeCompartments(parseTask->cx->compartment(), cx->compartment()); if (!parseTask->finish(cx)) return nullptr; RootedScript script(rt, parseTask->script); assertSameCompartment(cx, script); // Report any error or warnings generated during the parse, and inform the // debugger about the compiled scripts. for (size_t i = 0; i < parseTask->errors.length(); i++) parseTask->errors[i]->throwError(cx); if (parseTask->overRecursed) js_ReportOverRecursed(cx); if (script) { // The Debugger only needs to be told about the topmost script that was compiled. GlobalObject *compileAndGoGlobal = nullptr; if (script->compileAndGo()) compileAndGoGlobal = &script->global(); Debugger::onNewScript(cx, script, compileAndGoGlobal); // Update the compressed source table with the result. This is normally // called by setCompressedSource when compilation occurs on the main thread. if (script->scriptSource()->hasCompressedSource()) script->scriptSource()->updateCompressedSourceSet(rt); } return script; }
bool BaselineFrame::initForOsr(InterpreterFrame *fp, uint32_t numStackValues) { mozilla::PodZero(this); scopeChain_ = fp->scopeChain(); if (fp->hasCallObjUnchecked()) flags_ |= BaselineFrame::HAS_CALL_OBJ; if (fp->isEvalFrame()) { flags_ |= BaselineFrame::EVAL; evalScript_ = fp->script(); } if (fp->script()->needsArgsObj() && fp->hasArgsObj()) { flags_ |= BaselineFrame::HAS_ARGS_OBJ; argsObj_ = &fp->argsObj(); } if (fp->hasReturnValue()) setReturnValue(fp->returnValue()); // If the interpreter pushed an SPS frame when it entered the function, the // interpreter will pop it after the OSR trampoline returns. In order for // the Baseline frame to have its SPS flag set, it must have its own SPS // frame, which the Baseline code will pop on return. Note that the // profiler may have been enabled or disabled after the function was entered // but before OSR. JSContext *cx = GetJSContextFromJitCode(); SPSProfiler *p = &(cx->runtime()->spsProfiler); if (p->enabled()) { p->enter(fp->script(), fp->maybeFun()); flags_ |= BaselineFrame::HAS_PUSHED_SPS_FRAME; } frameSize_ = BaselineFrame::FramePointerOffset + BaselineFrame::Size() + numStackValues * sizeof(Value); MOZ_ASSERT(numValueSlots() == numStackValues); for (uint32_t i = 0; i < numStackValues; i++) *valueSlot(i) = fp->slots()[i]; if (cx->compartment()->debugMode()) { // In debug mode, update any Debugger.Frame objects for the // InterpreterFrame to point to the BaselineFrame. // The caller pushed a fake return address. ScriptFrameIter, used by the // debugger, wants a valid return address, but it's okay to just pick one. // In debug mode there's always at least 1 ICEntry (since there are always // debug prologue/epilogue calls). JitFrameIterator iter(cx); MOZ_ASSERT(iter.returnAddress() == nullptr); BaselineScript *baseline = fp->script()->baselineScript(); iter.current()->setReturnAddress(baseline->returnAddressForIC(baseline->icEntry(0))); if (!Debugger::handleBaselineOsr(cx, fp, this)) return false; } return true; }