Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / base / JSExecutionContext.cpp
blobd8d526b6dcfb77cab20ed38d00590dc1f0f22f8c
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /**
8 * This is not a generated file. It contains common utility functions
9 * invoked from the JavaScript code generated from IDL interfaces.
10 * The goal of the utility functions is to cut down on the size of
11 * the generated code itself.
14 #include "mozilla/dom/JSExecutionContext.h"
16 #include <utility>
17 #include "ErrorList.h"
18 #include "MainThreadUtils.h"
19 #include "js/CompilationAndEvaluation.h"
20 #include "js/CompileOptions.h"
21 #include "js/Conversions.h"
22 #include "js/experimental/JSStencil.h"
23 #include "js/HeapAPI.h"
24 #include "js/ProfilingCategory.h"
25 #include "js/Promise.h"
26 #include "js/SourceText.h"
27 #include "js/Transcoding.h"
28 #include "js/Value.h"
29 #include "js/Wrapper.h"
30 #include "jsapi.h"
31 #include "mozilla/CycleCollectedJSContext.h"
32 #include "mozilla/dom/ScriptLoadContext.h"
33 #include "mozilla/Likely.h"
34 #include "nsContentUtils.h"
35 #include "nsTPromiseFlatString.h"
36 #include "xpcpublic.h"
38 #if !defined(DEBUG) && !defined(MOZ_ENABLE_JS_DUMP)
39 # include "mozilla/StaticPrefs_browser.h"
40 #endif
42 using namespace mozilla;
43 using namespace mozilla::dom;
45 static nsresult EvaluationExceptionToNSResult(JSContext* aCx) {
46 if (JS_IsExceptionPending(aCx)) {
47 return NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW;
49 return NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW_UNCATCHABLE;
52 JSExecutionContext::JSExecutionContext(
53 JSContext* aCx, JS::Handle<JSObject*> aGlobal,
54 JS::CompileOptions& aCompileOptions,
55 JS::Handle<JS::Value> aDebuggerPrivateValue,
56 JS::Handle<JSScript*> aDebuggerIntroductionScript)
57 : mAutoProfilerLabel("JSExecutionContext",
58 /* dynamicStr */ nullptr,
59 JS::ProfilingCategoryPair::JS),
60 mCx(aCx),
61 mRealm(aCx, aGlobal),
62 mRetValue(aCx),
63 mScript(aCx),
64 mCompileOptions(aCompileOptions),
65 mDebuggerPrivateValue(aCx, aDebuggerPrivateValue),
66 mDebuggerIntroductionScript(aCx, aDebuggerIntroductionScript),
67 mRv(NS_OK),
68 mSkip(false),
69 mCoerceToString(false),
70 mEncodeBytecode(false)
71 #ifdef DEBUG
73 mWantsReturnValue(false),
74 mScriptUsed(false)
75 #endif
77 MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
78 MOZ_ASSERT(NS_IsMainThread());
79 MOZ_ASSERT(CycleCollectedJSContext::Get() &&
80 CycleCollectedJSContext::Get()->MicroTaskLevel());
81 MOZ_ASSERT(mRetValue.isUndefined());
83 MOZ_ASSERT(JS_IsGlobalObject(aGlobal));
84 if (MOZ_UNLIKELY(!xpc::Scriptability::Get(aGlobal).Allowed())) {
85 mSkip = true;
86 mRv = NS_OK;
90 nsresult JSExecutionContext::JoinOffThread(ScriptLoadContext* aContext) {
91 if (mSkip) {
92 return mRv;
95 MOZ_ASSERT(!mWantsReturnValue);
97 JS::InstantiationStorage storage;
98 RefPtr<JS::Stencil> stencil = aContext->StealOffThreadResult(mCx, &storage);
99 if (!stencil) {
100 mSkip = true;
101 mRv = EvaluationExceptionToNSResult(mCx);
102 return mRv;
105 return InstantiateStencil(std::move(stencil), &storage);
108 template <typename Unit>
109 nsresult JSExecutionContext::InternalCompile(JS::SourceText<Unit>& aSrcBuf) {
110 if (mSkip) {
111 return mRv;
114 MOZ_ASSERT(aSrcBuf.get());
115 MOZ_ASSERT(mRetValue.isUndefined());
116 #ifdef DEBUG
117 mWantsReturnValue = !mCompileOptions.noScriptRval;
118 #endif
120 RefPtr<JS::Stencil> stencil =
121 CompileGlobalScriptToStencil(mCx, mCompileOptions, aSrcBuf);
122 if (!stencil) {
123 mSkip = true;
124 mRv = EvaluationExceptionToNSResult(mCx);
125 return mRv;
128 return InstantiateStencil(std::move(stencil));
131 nsresult JSExecutionContext::Compile(JS::SourceText<char16_t>& aSrcBuf) {
132 return InternalCompile(aSrcBuf);
135 nsresult JSExecutionContext::Compile(JS::SourceText<Utf8Unit>& aSrcBuf) {
136 return InternalCompile(aSrcBuf);
139 nsresult JSExecutionContext::Compile(const nsAString& aScript) {
140 if (mSkip) {
141 return mRv;
144 const nsPromiseFlatString& flatScript = PromiseFlatString(aScript);
145 JS::SourceText<char16_t> srcBuf;
146 if (!srcBuf.init(mCx, flatScript.get(), flatScript.Length(),
147 JS::SourceOwnership::Borrowed)) {
148 mSkip = true;
149 mRv = EvaluationExceptionToNSResult(mCx);
150 return mRv;
153 return Compile(srcBuf);
156 nsresult JSExecutionContext::Decode(const JS::TranscodeRange& aBytecodeBuf) {
157 if (mSkip) {
158 return mRv;
161 JS::DecodeOptions decodeOptions(mCompileOptions);
162 decodeOptions.borrowBuffer = true;
164 MOZ_ASSERT(!mWantsReturnValue);
165 RefPtr<JS::Stencil> stencil;
166 JS::TranscodeResult tr = JS::DecodeStencil(mCx, decodeOptions, aBytecodeBuf,
167 getter_AddRefs(stencil));
168 // These errors are external parameters which should be handled before the
169 // decoding phase, and which are the only reasons why you might want to
170 // fallback on decoding failures.
171 MOZ_ASSERT(tr != JS::TranscodeResult::Failure_BadBuildId);
172 if (tr != JS::TranscodeResult::Ok) {
173 mSkip = true;
174 mRv = NS_ERROR_DOM_JS_DECODING_ERROR;
175 return mRv;
178 return InstantiateStencil(std::move(stencil));
181 nsresult JSExecutionContext::InstantiateStencil(
182 RefPtr<JS::Stencil>&& aStencil, JS::InstantiationStorage* aStorage) {
183 JS::InstantiateOptions instantiateOptions(mCompileOptions);
184 JS::Rooted<JSScript*> script(
185 mCx, JS::InstantiateGlobalStencil(mCx, instantiateOptions, aStencil,
186 aStorage));
187 if (!script) {
188 mSkip = true;
189 mRv = EvaluationExceptionToNSResult(mCx);
190 return mRv;
193 if (mEncodeBytecode) {
194 if (!JS::StartIncrementalEncoding(mCx, std::move(aStencil))) {
195 mSkip = true;
196 mRv = EvaluationExceptionToNSResult(mCx);
197 return mRv;
201 MOZ_ASSERT(!mScript);
202 mScript.set(script);
204 if (instantiateOptions.deferDebugMetadata) {
205 if (!JS::UpdateDebugMetadata(mCx, mScript, instantiateOptions,
206 mDebuggerPrivateValue, nullptr,
207 mDebuggerIntroductionScript, nullptr)) {
208 return NS_ERROR_OUT_OF_MEMORY;
212 return NS_OK;
215 JSScript* JSExecutionContext::GetScript() {
216 #ifdef DEBUG
217 MOZ_ASSERT(!mSkip);
218 MOZ_ASSERT(mScript);
219 mScriptUsed = true;
220 #endif
222 return MaybeGetScript();
225 JSScript* JSExecutionContext::MaybeGetScript() { return mScript; }
227 nsresult JSExecutionContext::ExecScript() {
228 if (mSkip) {
229 return mRv;
232 MOZ_ASSERT(mScript);
234 if (!JS_ExecuteScript(mCx, mScript)) {
235 mSkip = true;
236 mRv = EvaluationExceptionToNSResult(mCx);
237 return mRv;
240 return NS_OK;
243 static bool IsPromiseValue(JSContext* aCx, JS::Handle<JS::Value> aValue) {
244 if (!aValue.isObject()) {
245 return false;
248 // We only care about Promise here, so CheckedUnwrapStatic is fine.
249 JS::Rooted<JSObject*> obj(aCx, js::CheckedUnwrapStatic(&aValue.toObject()));
250 if (!obj) {
251 return false;
254 return JS::IsPromiseObject(obj);
257 nsresult JSExecutionContext::ExecScript(
258 JS::MutableHandle<JS::Value> aRetValue) {
259 if (mSkip) {
260 aRetValue.setUndefined();
261 return mRv;
264 MOZ_ASSERT(mScript);
265 MOZ_ASSERT(mWantsReturnValue);
267 if (!JS_ExecuteScript(mCx, mScript, aRetValue)) {
268 mSkip = true;
269 mRv = EvaluationExceptionToNSResult(mCx);
270 return mRv;
273 #ifdef DEBUG
274 mWantsReturnValue = false;
275 #endif
276 if (mCoerceToString && IsPromiseValue(mCx, aRetValue)) {
277 // We're a javascript: url and we should treat Promise return values as
278 // undefined.
280 // Once bug 1477821 is fixed this code might be able to go away, or will
281 // become enshrined in the spec, depending.
282 aRetValue.setUndefined();
285 if (mCoerceToString && !aRetValue.isUndefined()) {
286 JSString* str = JS::ToString(mCx, aRetValue);
287 if (!str) {
288 // ToString can be a function call, so an exception can be raised while
289 // executing the function.
290 mSkip = true;
291 return EvaluationExceptionToNSResult(mCx);
293 aRetValue.set(JS::StringValue(str));
296 return NS_OK;