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 #include "LoadedScript.h"
9 #include "mozilla/HoldDropJSObjects.h"
10 #include "mozilla/UniquePtr.h" // mozilla::UniquePtr
12 #include "mozilla/dom/ScriptLoadContext.h" // ScriptLoadContext
13 #include "jsfriendapi.h"
14 #include "js/Modules.h" // JS::{Get,Set}ModulePrivate
15 #include "LoadContextBase.h" // LoadContextBase
17 namespace JS::loader
{
19 //////////////////////////////////////////////////////////////
21 //////////////////////////////////////////////////////////////
23 MOZ_DEFINE_MALLOC_SIZE_OF(LoadedScriptMallocSizeOf
)
25 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LoadedScript
)
26 NS_INTERFACE_MAP_ENTRY(nsISupports
)
29 NS_IMPL_CYCLE_COLLECTION_CLASS(LoadedScript
)
31 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(LoadedScript
)
32 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions
)
33 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL
)
34 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
36 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(LoadedScript
)
37 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions
)
38 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
40 NS_IMPL_CYCLE_COLLECTING_ADDREF(LoadedScript
)
41 NS_IMPL_CYCLE_COLLECTING_RELEASE(LoadedScript
)
43 LoadedScript::LoadedScript(ScriptKind aKind
,
44 mozilla::dom::ReferrerPolicy aReferrerPolicy
,
45 ScriptFetchOptions
* aFetchOptions
, nsIURI
* aURI
)
47 mReferrerPolicy(aReferrerPolicy
),
48 mFetchOptions(aFetchOptions
),
50 mDataType(DataType::eUnknown
),
51 mReceivedScriptTextLength(0),
53 MOZ_ASSERT(mFetchOptions
);
57 LoadedScript::~LoadedScript() {
58 mozilla::UnregisterWeakMemoryReporter(this);
59 mozilla::DropJSObjects(this);
62 void LoadedScript::RegisterMemoryReport() {
63 mozilla::RegisterWeakMemoryReporter(this);
67 LoadedScript::CollectReports(nsIHandleReportCallback
* aHandleReport
,
68 nsISupports
* aData
, bool aAnonymize
) {
69 #define COLLECT_REPORT(path, kind) \
70 MOZ_COLLECT_REPORT(path, KIND_HEAP, UNITS_BYTES, \
71 SizeOfIncludingThis(LoadedScriptMallocSizeOf), \
72 "Memory used for LoadedScript to hold on " kind \
76 case ScriptKind::eClassic
:
77 COLLECT_REPORT("explicit/js/script/loaded-script/classic", "scripts");
79 case ScriptKind::eImportMap
:
80 COLLECT_REPORT("explicit/js/script/loaded-script/import-map",
83 case ScriptKind::eModule
:
84 COLLECT_REPORT("explicit/js/script/loaded-script/module", "modules");
86 case ScriptKind::eEvent
:
87 COLLECT_REPORT("explicit/js/script/loaded-script/event", "event scripts");
95 size_t LoadedScript::SizeOfIncludingThis(
96 mozilla::MallocSizeOf aMallocSizeOf
) const {
97 size_t bytes
= aMallocSizeOf(this);
101 bytes
+= ScriptText
<char16_t
>().sizeOfExcludingThis(aMallocSizeOf
);
103 bytes
+= ScriptText
<Utf8Unit
>().sizeOfExcludingThis(aMallocSizeOf
);
107 bytes
+= mScriptBytecode
.sizeOfExcludingThis(aMallocSizeOf
);
111 void LoadedScript::AssociateWithScript(JSScript
* aScript
) {
112 // Verify that the rewritten URL is available when manipulating LoadedScript.
113 MOZ_ASSERT(mBaseURL
);
115 // Set a JSScript's private value to point to this object. The JS engine will
116 // increment our reference count by calling HostAddRefTopLevelScript(). This
117 // is decremented by HostReleaseTopLevelScript() below when the JSScript dies.
119 MOZ_ASSERT(JS::GetScriptPrivate(aScript
).isUndefined());
120 JS::SetScriptPrivate(aScript
, JS::PrivateValue(this));
123 nsresult
LoadedScript::GetScriptSource(JSContext
* aCx
,
124 MaybeSourceText
* aMaybeSource
,
125 LoadContextBase
* aMaybeLoadContext
) {
126 // If there's no script text, we try to get it from the element
127 bool isWindowContext
=
128 aMaybeLoadContext
&& aMaybeLoadContext
->IsWindowContext();
129 if (isWindowContext
&& aMaybeLoadContext
->AsWindowContext()->mIsInline
) {
130 nsAutoString inlineData
;
131 auto* scriptLoadContext
= aMaybeLoadContext
->AsWindowContext();
132 scriptLoadContext
->GetScriptElement()->GetScriptText(inlineData
);
134 size_t nbytes
= inlineData
.Length() * sizeof(char16_t
);
135 JS::UniqueTwoByteChars
chars(
136 static_cast<char16_t
*>(JS_malloc(aCx
, nbytes
)));
138 return NS_ERROR_OUT_OF_MEMORY
;
141 memcpy(chars
.get(), inlineData
.get(), nbytes
);
143 SourceText
<char16_t
> srcBuf
;
144 if (!srcBuf
.init(aCx
, std::move(chars
), inlineData
.Length())) {
145 return NS_ERROR_OUT_OF_MEMORY
;
148 aMaybeSource
->construct
<SourceText
<char16_t
>>(std::move(srcBuf
));
152 size_t length
= ScriptTextLength();
154 JS::UniqueTwoByteChars chars
;
155 chars
.reset(ScriptText
<char16_t
>().extractOrCopyRawBuffer());
157 JS_ReportOutOfMemory(aCx
);
158 return NS_ERROR_OUT_OF_MEMORY
;
161 SourceText
<char16_t
> srcBuf
;
162 if (!srcBuf
.init(aCx
, std::move(chars
), length
)) {
163 return NS_ERROR_OUT_OF_MEMORY
;
166 aMaybeSource
->construct
<SourceText
<char16_t
>>(std::move(srcBuf
));
170 MOZ_ASSERT(IsUTF8Text());
171 mozilla::UniquePtr
<Utf8Unit
[], JS::FreePolicy
> chars
;
172 chars
.reset(ScriptText
<Utf8Unit
>().extractOrCopyRawBuffer());
174 JS_ReportOutOfMemory(aCx
);
175 return NS_ERROR_OUT_OF_MEMORY
;
178 SourceText
<Utf8Unit
> srcBuf
;
179 if (!srcBuf
.init(aCx
, std::move(chars
), length
)) {
180 return NS_ERROR_OUT_OF_MEMORY
;
183 aMaybeSource
->construct
<SourceText
<Utf8Unit
>>(std::move(srcBuf
));
187 inline void CheckModuleScriptPrivate(LoadedScript
* script
,
188 const JS::Value
& aPrivate
) {
190 if (script
->IsModuleScript()) {
191 JSObject
* module
= script
->AsModuleScript()->mModuleRecord
.unbarrieredGet();
192 MOZ_ASSERT_IF(module
, JS::GetModulePrivate(module
) == aPrivate
);
197 void HostAddRefTopLevelScript(const JS::Value
& aPrivate
) {
198 // Increment the reference count of a LoadedScript object that is now pointed
199 // to by a JSScript. The reference count is decremented by
200 // HostReleaseTopLevelScript() below.
202 auto script
= static_cast<LoadedScript
*>(aPrivate
.toPrivate());
203 CheckModuleScriptPrivate(script
, aPrivate
);
207 void HostReleaseTopLevelScript(const JS::Value
& aPrivate
) {
208 // Decrement the reference count of a LoadedScript object that was pointed to
209 // by a JSScript. The reference count was originally incremented by
210 // HostAddRefTopLevelScript() above.
212 auto script
= static_cast<LoadedScript
*>(aPrivate
.toPrivate());
213 CheckModuleScriptPrivate(script
, aPrivate
);
217 //////////////////////////////////////////////////////////////
219 //////////////////////////////////////////////////////////////
221 EventScript::EventScript(mozilla::dom::ReferrerPolicy aReferrerPolicy
,
222 ScriptFetchOptions
* aFetchOptions
, nsIURI
* aURI
)
223 : LoadedScript(ScriptKind::eEvent
, aReferrerPolicy
, aFetchOptions
, aURI
) {
224 // EventScripts are not using ScriptLoadRequest, and mBaseURL and mURI are
229 //////////////////////////////////////////////////////////////
231 //////////////////////////////////////////////////////////////
233 ClassicScript::ClassicScript(mozilla::dom::ReferrerPolicy aReferrerPolicy
,
234 ScriptFetchOptions
* aFetchOptions
, nsIURI
* aURI
)
235 : LoadedScript(ScriptKind::eClassic
, aReferrerPolicy
, aFetchOptions
, aURI
) {
238 //////////////////////////////////////////////////////////////
240 //////////////////////////////////////////////////////////////
242 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(ModuleScript
, LoadedScript
)
244 NS_IMPL_CYCLE_COLLECTION_CLASS(ModuleScript
)
246 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ModuleScript
, LoadedScript
)
247 tmp
->UnlinkModuleRecord();
248 tmp
->mParseError
.setUndefined();
249 tmp
->mErrorToRethrow
.setUndefined();
250 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
252 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ModuleScript
, LoadedScript
)
253 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
255 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ModuleScript
, LoadedScript
)
256 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mModuleRecord
)
257 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mParseError
)
258 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mErrorToRethrow
)
259 NS_IMPL_CYCLE_COLLECTION_TRACE_END
261 ModuleScript::ModuleScript(mozilla::dom::ReferrerPolicy aReferrerPolicy
,
262 ScriptFetchOptions
* aFetchOptions
, nsIURI
* aURI
)
263 : LoadedScript(ScriptKind::eModule
, aReferrerPolicy
, aFetchOptions
, aURI
),
264 mHadImportMap(false),
265 mDebuggerDataInitialized(false) {
266 MOZ_ASSERT(!ModuleRecord());
267 MOZ_ASSERT(!HasParseError());
268 MOZ_ASSERT(!HasErrorToRethrow());
271 void ModuleScript::Shutdown() {
273 JS::ClearModuleEnvironment(mModuleRecord
);
276 UnlinkModuleRecord();
279 void ModuleScript::UnlinkModuleRecord() {
280 // Remove the module record's pointer to this object if present and decrement
281 // our reference count. The reference is added by SetModuleRecord() below.
283 // This takes care not to trigger gray unmarking because this takes a lot of
284 // time when we're tearing down the entire page. This is safe because we are
285 // only writing undefined into the module private, so it won't create any
288 JSObject
* module
= mModuleRecord
.unbarrieredGet();
289 MOZ_ASSERT(JS::GetModulePrivate(module
).toPrivate() == this);
290 JS::ClearModulePrivate(module
);
291 mModuleRecord
= nullptr;
295 ModuleScript::~ModuleScript() {
296 // The object may be destroyed without being unlinked first.
297 UnlinkModuleRecord();
300 void ModuleScript::SetModuleRecord(JS::Handle
<JSObject
*> aModuleRecord
) {
301 MOZ_ASSERT(!mModuleRecord
);
302 MOZ_ASSERT_IF(IsModuleScript(), !AsModuleScript()->HasParseError());
303 MOZ_ASSERT_IF(IsModuleScript(), !AsModuleScript()->HasErrorToRethrow());
305 mModuleRecord
= aModuleRecord
;
307 // Make module's host defined field point to this object. The JS engine will
308 // increment our reference count by calling HostAddRefTopLevelScript(). This
309 // is decremented when the field is cleared in UnlinkModuleRecord() above or
310 // when the module record dies.
311 MOZ_ASSERT(JS::GetModulePrivate(mModuleRecord
).isUndefined());
312 JS::SetModulePrivate(mModuleRecord
, JS::PrivateValue(this));
314 mozilla::HoldJSObjects(this);
317 void ModuleScript::SetParseError(const JS::Value
& aError
) {
318 MOZ_ASSERT(!aError
.isUndefined());
319 MOZ_ASSERT(!HasParseError());
320 MOZ_ASSERT(!HasErrorToRethrow());
322 UnlinkModuleRecord();
323 mParseError
= aError
;
324 mozilla::HoldJSObjects(this);
327 void ModuleScript::SetErrorToRethrow(const JS::Value
& aError
) {
328 MOZ_ASSERT(!aError
.isUndefined());
330 // This is only called after SetModuleRecord() or SetParseError() so we don't
331 // need to call HoldJSObjects() here.
332 MOZ_ASSERT(ModuleRecord() || HasParseError());
334 mErrorToRethrow
= aError
;
337 void ModuleScript::SetForPreload(bool aValue
) { mForPreload
= aValue
; }
338 void ModuleScript::SetHadImportMap(bool aValue
) { mHadImportMap
= aValue
; }
340 void ModuleScript::SetDebuggerDataInitialized() {
341 MOZ_ASSERT(ModuleRecord());
342 MOZ_ASSERT(!mDebuggerDataInitialized
);
344 mDebuggerDataInitialized
= true;
347 } // namespace JS::loader