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/. */
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"
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"
29 #include "js/Wrapper.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"
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
),
64 mCompileOptions(aCompileOptions
),
65 mDebuggerPrivateValue(aCx
, aDebuggerPrivateValue
),
66 mDebuggerIntroductionScript(aCx
, aDebuggerIntroductionScript
),
69 mCoerceToString(false),
70 mEncodeBytecode(false)
73 mWantsReturnValue(false),
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())) {
90 nsresult
JSExecutionContext::JoinOffThread(ScriptLoadContext
* aContext
) {
95 MOZ_ASSERT(!mWantsReturnValue
);
97 JS::InstantiationStorage storage
;
98 RefPtr
<JS::Stencil
> stencil
= aContext
->StealOffThreadResult(mCx
, &storage
);
101 mRv
= EvaluationExceptionToNSResult(mCx
);
105 return InstantiateStencil(std::move(stencil
), &storage
);
108 template <typename Unit
>
109 nsresult
JSExecutionContext::InternalCompile(JS::SourceText
<Unit
>& aSrcBuf
) {
114 MOZ_ASSERT(aSrcBuf
.get());
115 MOZ_ASSERT(mRetValue
.isUndefined());
117 mWantsReturnValue
= !mCompileOptions
.noScriptRval
;
120 RefPtr
<JS::Stencil
> stencil
=
121 CompileGlobalScriptToStencil(mCx
, mCompileOptions
, aSrcBuf
);
124 mRv
= EvaluationExceptionToNSResult(mCx
);
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
) {
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
)) {
149 mRv
= EvaluationExceptionToNSResult(mCx
);
153 return Compile(srcBuf
);
156 nsresult
JSExecutionContext::Decode(const JS::TranscodeRange
& aBytecodeBuf
) {
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
) {
174 mRv
= NS_ERROR_DOM_JS_DECODING_ERROR
;
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
,
189 mRv
= EvaluationExceptionToNSResult(mCx
);
193 if (mEncodeBytecode
) {
194 if (!JS::StartIncrementalEncoding(mCx
, std::move(aStencil
))) {
196 mRv
= EvaluationExceptionToNSResult(mCx
);
201 MOZ_ASSERT(!mScript
);
204 if (instantiateOptions
.deferDebugMetadata
) {
205 if (!JS::UpdateDebugMetadata(mCx
, mScript
, instantiateOptions
,
206 mDebuggerPrivateValue
, nullptr,
207 mDebuggerIntroductionScript
, nullptr)) {
208 return NS_ERROR_OUT_OF_MEMORY
;
215 JSScript
* JSExecutionContext::GetScript() {
222 return MaybeGetScript();
225 JSScript
* JSExecutionContext::MaybeGetScript() { return mScript
; }
227 nsresult
JSExecutionContext::ExecScript() {
234 if (!JS_ExecuteScript(mCx
, mScript
)) {
236 mRv
= EvaluationExceptionToNSResult(mCx
);
243 static bool IsPromiseValue(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
) {
244 if (!aValue
.isObject()) {
248 // We only care about Promise here, so CheckedUnwrapStatic is fine.
249 JS::Rooted
<JSObject
*> obj(aCx
, js::CheckedUnwrapStatic(&aValue
.toObject()));
254 return JS::IsPromiseObject(obj
);
257 nsresult
JSExecutionContext::ExecScript(
258 JS::MutableHandle
<JS::Value
> aRetValue
) {
260 aRetValue
.setUndefined();
265 MOZ_ASSERT(mWantsReturnValue
);
267 if (!JS_ExecuteScript(mCx
, mScript
, aRetValue
)) {
269 mRv
= EvaluationExceptionToNSResult(mCx
);
274 mWantsReturnValue
= false;
276 if (mCoerceToString
&& IsPromiseValue(mCx
, aRetValue
)) {
277 // We're a javascript: url and we should treat Promise return values as
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
);
288 // ToString can be a function call, so an exception can be raised while
289 // executing the function.
291 return EvaluationExceptionToNSResult(mCx
);
293 aRetValue
.set(JS::StringValue(str
));