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(mozilla::Vector
<uint8_t>& aBytecodeBuf
,
157 size_t aBytecodeIndex
) {
162 JS::DecodeOptions
decodeOptions(mCompileOptions
);
163 decodeOptions
.borrowBuffer
= true;
165 JS::TranscodeRange
range(aBytecodeBuf
.begin() + aBytecodeIndex
,
166 aBytecodeBuf
.length() - aBytecodeIndex
);
168 MOZ_ASSERT(!mWantsReturnValue
);
169 RefPtr
<JS::Stencil
> stencil
;
170 JS::TranscodeResult tr
=
171 JS::DecodeStencil(mCx
, decodeOptions
, range
, getter_AddRefs(stencil
));
172 // These errors are external parameters which should be handled before the
173 // decoding phase, and which are the only reasons why you might want to
174 // fallback on decoding failures.
175 MOZ_ASSERT(tr
!= JS::TranscodeResult::Failure_BadBuildId
);
176 if (tr
!= JS::TranscodeResult::Ok
) {
178 mRv
= NS_ERROR_DOM_JS_DECODING_ERROR
;
182 return InstantiateStencil(std::move(stencil
));
185 nsresult
JSExecutionContext::InstantiateStencil(
186 RefPtr
<JS::Stencil
>&& aStencil
, JS::InstantiationStorage
* aStorage
) {
187 JS::InstantiateOptions
instantiateOptions(mCompileOptions
);
188 JS::Rooted
<JSScript
*> script(
189 mCx
, JS::InstantiateGlobalStencil(mCx
, instantiateOptions
, aStencil
,
193 mRv
= EvaluationExceptionToNSResult(mCx
);
197 if (mEncodeBytecode
) {
198 if (!JS::StartIncrementalEncoding(mCx
, std::move(aStencil
))) {
200 mRv
= EvaluationExceptionToNSResult(mCx
);
205 MOZ_ASSERT(!mScript
);
208 if (instantiateOptions
.deferDebugMetadata
) {
209 if (!JS::UpdateDebugMetadata(mCx
, mScript
, instantiateOptions
,
210 mDebuggerPrivateValue
, nullptr,
211 mDebuggerIntroductionScript
, nullptr)) {
212 return NS_ERROR_OUT_OF_MEMORY
;
219 JSScript
* JSExecutionContext::GetScript() {
226 return MaybeGetScript();
229 JSScript
* JSExecutionContext::MaybeGetScript() { return mScript
; }
231 nsresult
JSExecutionContext::ExecScript() {
238 if (!JS_ExecuteScript(mCx
, mScript
)) {
240 mRv
= EvaluationExceptionToNSResult(mCx
);
247 static bool IsPromiseValue(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
) {
248 if (!aValue
.isObject()) {
252 // We only care about Promise here, so CheckedUnwrapStatic is fine.
253 JS::Rooted
<JSObject
*> obj(aCx
, js::CheckedUnwrapStatic(&aValue
.toObject()));
258 return JS::IsPromiseObject(obj
);
261 nsresult
JSExecutionContext::ExecScript(
262 JS::MutableHandle
<JS::Value
> aRetValue
) {
264 aRetValue
.setUndefined();
269 MOZ_ASSERT(mWantsReturnValue
);
271 if (!JS_ExecuteScript(mCx
, mScript
, aRetValue
)) {
273 mRv
= EvaluationExceptionToNSResult(mCx
);
278 mWantsReturnValue
= false;
280 if (mCoerceToString
&& IsPromiseValue(mCx
, aRetValue
)) {
281 // We're a javascript: url and we should treat Promise return values as
284 // Once bug 1477821 is fixed this code might be able to go away, or will
285 // become enshrined in the spec, depending.
286 aRetValue
.setUndefined();
289 if (mCoerceToString
&& !aRetValue
.isUndefined()) {
290 JSString
* str
= JS::ToString(mCx
, aRetValue
);
292 // ToString can be a function call, so an exception can be raised while
293 // executing the function.
295 return EvaluationExceptionToNSResult(mCx
);
297 aRetValue
.set(JS::StringValue(str
));