Files
tubestation/dom/base/JSExecutionContext.cpp
Norisz Fay 471367226c Backed out 17 changesets (bug 1896709) for causing build bustage and mochitest failures CLOSED TREE
Backed out changeset 046f0f522ce7 (bug 1896709)
Backed out changeset 0271e50308e6 (bug 1896709)
Backed out changeset 26fac760de19 (bug 1896709)
Backed out changeset 6fa3424dfc40 (bug 1896709)
Backed out changeset b7bcb9904435 (bug 1896709)
Backed out changeset c6878c5fdde4 (bug 1896709)
Backed out changeset 8ca8a4082e44 (bug 1896709)
Backed out changeset 20ff83806cc6 (bug 1896709)
Backed out changeset 6c9338852a19 (bug 1896709)
Backed out changeset e8615b5d82ab (bug 1896709)
Backed out changeset 842f2047a4bd (bug 1896709)
Backed out changeset 54eea04a7342 (bug 1896709)
Backed out changeset 220b544127cb (bug 1896709)
Backed out changeset 52ce0ebfb29a (bug 1896709)
Backed out changeset 34fc79dc1ad7 (bug 1896709)
Backed out changeset 50b0f6a23e3c (bug 1896709)
Backed out changeset 89c4c6bb465a (bug 1896709)
2024-07-18 14:48:46 +03:00

301 lines
8.1 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* This is not a generated file. It contains common utility functions
* invoked from the JavaScript code generated from IDL interfaces.
* The goal of the utility functions is to cut down on the size of
* the generated code itself.
*/
#include "mozilla/dom/JSExecutionContext.h"
#include <utility>
#include "ErrorList.h"
#include "MainThreadUtils.h"
#include "js/CompilationAndEvaluation.h"
#include "js/CompileOptions.h"
#include "js/Conversions.h"
#include "js/experimental/JSStencil.h"
#include "js/HeapAPI.h"
#include "js/ProfilingCategory.h"
#include "js/Promise.h"
#include "js/SourceText.h"
#include "js/Transcoding.h"
#include "js/Value.h"
#include "js/Wrapper.h"
#include "jsapi.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/dom/ScriptLoadContext.h"
#include "mozilla/Likely.h"
#include "nsContentUtils.h"
#include "nsTPromiseFlatString.h"
#include "xpcpublic.h"
#if !defined(DEBUG) && !defined(MOZ_ENABLE_JS_DUMP)
# include "mozilla/StaticPrefs_browser.h"
#endif
using namespace mozilla;
using namespace mozilla::dom;
static nsresult EvaluationExceptionToNSResult(JSContext* aCx) {
if (JS_IsExceptionPending(aCx)) {
return NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW;
}
return NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE;
}
JSExecutionContext::JSExecutionContext(
JSContext* aCx, JS::Handle<JSObject*> aGlobal,
JS::CompileOptions& aCompileOptions,
JS::Handle<JS::Value> aDebuggerPrivateValue,
JS::Handle<JSScript*> aDebuggerIntroductionScript)
: mAutoProfilerLabel("JSExecutionContext",
/* dynamicStr */ nullptr,
JS::ProfilingCategoryPair::JS),
mCx(aCx),
mRealm(aCx, aGlobal),
mRetValue(aCx),
mScript(aCx),
mCompileOptions(aCompileOptions),
mDebuggerPrivateValue(aCx, aDebuggerPrivateValue),
mDebuggerIntroductionScript(aCx, aDebuggerIntroductionScript),
mRv(NS_OK),
mSkip(false),
mCoerceToString(false),
mEncodeBytecode(false)
#ifdef DEBUG
,
mWantsReturnValue(false),
mScriptUsed(false)
#endif
{
MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(CycleCollectedJSContext::Get() &&
CycleCollectedJSContext::Get()->MicroTaskLevel());
MOZ_ASSERT(mRetValue.isUndefined());
MOZ_ASSERT(JS_IsGlobalObject(aGlobal));
if (MOZ_UNLIKELY(!xpc::Scriptability::Get(aGlobal).Allowed())) {
mSkip = true;
mRv = NS_OK;
}
}
nsresult JSExecutionContext::JoinOffThread(ScriptLoadContext* aContext) {
if (mSkip) {
return mRv;
}
MOZ_ASSERT(!mWantsReturnValue);
JS::InstantiationStorage storage;
RefPtr<JS::Stencil> stencil = aContext->StealOffThreadResult(mCx, &storage);
if (!stencil) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
return InstantiateStencil(std::move(stencil), &storage);
}
template <typename Unit>
nsresult JSExecutionContext::InternalCompile(JS::SourceText<Unit>& aSrcBuf) {
if (mSkip) {
return mRv;
}
MOZ_ASSERT(aSrcBuf.get());
MOZ_ASSERT(mRetValue.isUndefined());
#ifdef DEBUG
mWantsReturnValue = !mCompileOptions.noScriptRval;
#endif
RefPtr<JS::Stencil> stencil =
CompileGlobalScriptToStencil(mCx, mCompileOptions, aSrcBuf);
if (!stencil) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
return InstantiateStencil(std::move(stencil));
}
nsresult JSExecutionContext::Compile(JS::SourceText<char16_t>& aSrcBuf) {
return InternalCompile(aSrcBuf);
}
nsresult JSExecutionContext::Compile(JS::SourceText<Utf8Unit>& aSrcBuf) {
return InternalCompile(aSrcBuf);
}
nsresult JSExecutionContext::Compile(const nsAString& aScript) {
if (mSkip) {
return mRv;
}
const nsPromiseFlatString& flatScript = PromiseFlatString(aScript);
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(mCx, flatScript.get(), flatScript.Length(),
JS::SourceOwnership::Borrowed)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
return Compile(srcBuf);
}
nsresult JSExecutionContext::Decode(const JS::TranscodeRange& aBytecodeBuf) {
if (mSkip) {
return mRv;
}
JS::DecodeOptions decodeOptions(mCompileOptions);
decodeOptions.borrowBuffer = true;
MOZ_ASSERT(!mWantsReturnValue);
RefPtr<JS::Stencil> stencil;
JS::TranscodeResult tr = JS::DecodeStencil(mCx, decodeOptions, aBytecodeBuf,
getter_AddRefs(stencil));
// These errors are external parameters which should be handled before the
// decoding phase, and which are the only reasons why you might want to
// fallback on decoding failures.
MOZ_ASSERT(tr != JS::TranscodeResult::Failure_BadBuildId);
if (tr != JS::TranscodeResult::Ok) {
mSkip = true;
mRv = NS_ERROR_DOM_JS_DECODING_ERROR;
return mRv;
}
return InstantiateStencil(std::move(stencil));
}
nsresult JSExecutionContext::InstantiateStencil(
RefPtr<JS::Stencil>&& aStencil, JS::InstantiationStorage* aStorage) {
JS::InstantiateOptions instantiateOptions(mCompileOptions);
JS::Rooted<JSScript*> script(
mCx, JS::InstantiateGlobalStencil(mCx, instantiateOptions, aStencil,
aStorage));
if (!script) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
if (mEncodeBytecode) {
bool alreadyStarted;
if (!JS::StartIncrementalEncoding(mCx, std::move(aStencil),
alreadyStarted)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
MOZ_ASSERT(!alreadyStarted);
}
MOZ_ASSERT(!mScript);
mScript.set(script);
if (instantiateOptions.deferDebugMetadata) {
if (!JS::UpdateDebugMetadata(mCx, mScript, instantiateOptions,
mDebuggerPrivateValue, nullptr,
mDebuggerIntroductionScript, nullptr)) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
return NS_OK;
}
JSScript* JSExecutionContext::GetScript() {
#ifdef DEBUG
MOZ_ASSERT(!mSkip);
MOZ_ASSERT(mScript);
mScriptUsed = true;
#endif
return MaybeGetScript();
}
JSScript* JSExecutionContext::MaybeGetScript() { return mScript; }
nsresult JSExecutionContext::ExecScript() {
if (mSkip) {
return mRv;
}
MOZ_ASSERT(mScript);
if (!JS_ExecuteScript(mCx, mScript)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
return NS_OK;
}
static bool IsPromiseValue(JSContext* aCx, JS::Handle<JS::Value> aValue) {
if (!aValue.isObject()) {
return false;
}
// We only care about Promise here, so CheckedUnwrapStatic is fine.
JS::Rooted<JSObject*> obj(aCx, js::CheckedUnwrapStatic(&aValue.toObject()));
if (!obj) {
return false;
}
return JS::IsPromiseObject(obj);
}
nsresult JSExecutionContext::ExecScript(
JS::MutableHandle<JS::Value> aRetValue) {
if (mSkip) {
aRetValue.setUndefined();
return mRv;
}
MOZ_ASSERT(mScript);
MOZ_ASSERT(mWantsReturnValue);
if (!JS_ExecuteScript(mCx, mScript, aRetValue)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
#ifdef DEBUG
mWantsReturnValue = false;
#endif
if (mCoerceToString && IsPromiseValue(mCx, aRetValue)) {
// We're a javascript: url and we should treat Promise return values as
// undefined.
//
// Once bug 1477821 is fixed this code might be able to go away, or will
// become enshrined in the spec, depending.
aRetValue.setUndefined();
}
if (mCoerceToString && !aRetValue.isUndefined()) {
JSString* str = JS::ToString(mCx, aRetValue);
if (!str) {
// ToString can be a function call, so an exception can be raised while
// executing the function.
mSkip = true;
return EvaluationExceptionToNSResult(mCx);
}
aRetValue.set(JS::StringValue(str));
}
return NS_OK;
}