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/. */
6
7 #include "LoadedScript.h"
8
9 #include "mozilla/HoldDropJSObjects.h"
10 #include "mozilla/dom/Element.h"
11
12 #include "jsfriendapi.h"
13 #include "js/Modules.h" // JS::{Get,Set}ModulePrivate
14
15 namespace JS::loader {
16
17 //////////////////////////////////////////////////////////////
18 // LoadedScript
19 //////////////////////////////////////////////////////////////
20
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LoadedScript)21 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LoadedScript)
22 NS_INTERFACE_MAP_END
23
24 NS_IMPL_CYCLE_COLLECTION_CLASS(LoadedScript)
25
26 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(LoadedScript)
27 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions)
28 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL)
29 NS_IMPL_CYCLE_COLLECTION_UNLINK(mElement)
30 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
31
32 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(LoadedScript)
33 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions, mElement)
34 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
35
36 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(LoadedScript)
37 NS_IMPL_CYCLE_COLLECTION_TRACE_END
38
39 NS_IMPL_CYCLE_COLLECTING_ADDREF(LoadedScript)
40 NS_IMPL_CYCLE_COLLECTING_RELEASE(LoadedScript)
41
42 LoadedScript::LoadedScript(ScriptKind aKind, ScriptFetchOptions* aFetchOptions,
43 nsIURI* aBaseURL, mozilla::dom::Element* aElement)
44 : mKind(aKind),
45 mFetchOptions(aFetchOptions),
46 mBaseURL(aBaseURL),
47 mElement(aElement) {
48 MOZ_ASSERT(mFetchOptions);
49 MOZ_ASSERT(mBaseURL);
50 }
51
~LoadedScript()52 LoadedScript::~LoadedScript() { mozilla::DropJSObjects(this); }
53
AssociateWithScript(JSScript * aScript)54 void LoadedScript::AssociateWithScript(JSScript* aScript) {
55 // Set a JSScript's private value to point to this object. The JS engine will
56 // increment our reference count by calling HostAddRefTopLevelScript(). This
57 // is decremented by HostReleaseTopLevelScript() below when the JSScript dies.
58
59 MOZ_ASSERT(JS::GetScriptPrivate(aScript).isUndefined());
60 JS::SetScriptPrivate(aScript, JS::PrivateValue(this));
61 }
62
CheckModuleScriptPrivate(LoadedScript * script,const JS::Value & aPrivate)63 inline void CheckModuleScriptPrivate(LoadedScript* script,
64 const JS::Value& aPrivate) {
65 #ifdef DEBUG
66 if (script->IsModuleScript()) {
67 JSObject* module = script->AsModuleScript()->mModuleRecord.unbarrieredGet();
68 MOZ_ASSERT_IF(module, JS::GetModulePrivate(module) == aPrivate);
69 }
70 #endif
71 }
72
HostAddRefTopLevelScript(const JS::Value & aPrivate)73 void HostAddRefTopLevelScript(const JS::Value& aPrivate) {
74 // Increment the reference count of a LoadedScript object that is now pointed
75 // to by a JSScript. The reference count is decremented by
76 // HostReleaseTopLevelScript() below.
77
78 auto script = static_cast<LoadedScript*>(aPrivate.toPrivate());
79 CheckModuleScriptPrivate(script, aPrivate);
80 script->AddRef();
81 }
82
HostReleaseTopLevelScript(const JS::Value & aPrivate)83 void HostReleaseTopLevelScript(const JS::Value& aPrivate) {
84 // Decrement the reference count of a LoadedScript object that was pointed to
85 // by a JSScript. The reference count was originally incremented by
86 // HostAddRefTopLevelScript() above.
87
88 auto script = static_cast<LoadedScript*>(aPrivate.toPrivate());
89 CheckModuleScriptPrivate(script, aPrivate);
90 script->Release();
91 }
92
93 //////////////////////////////////////////////////////////////
94 // EventScript
95 //////////////////////////////////////////////////////////////
96
EventScript(ScriptFetchOptions * aFetchOptions,nsIURI * aBaseURL,mozilla::dom::Element * aElement)97 EventScript::EventScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL,
98 mozilla::dom::Element* aElement)
99 : LoadedScript(ScriptKind::eEvent, aFetchOptions, aBaseURL, aElement) {}
100
101 //////////////////////////////////////////////////////////////
102 // ClassicScript
103 //////////////////////////////////////////////////////////////
104
ClassicScript(ScriptFetchOptions * aFetchOptions,nsIURI * aBaseURL,mozilla::dom::Element * aElement)105 ClassicScript::ClassicScript(ScriptFetchOptions* aFetchOptions,
106 nsIURI* aBaseURL, mozilla::dom::Element* aElement)
107 : LoadedScript(ScriptKind::eClassic, aFetchOptions, aBaseURL, aElement) {}
108
109 //////////////////////////////////////////////////////////////
110 // ModuleScript
111 //////////////////////////////////////////////////////////////
112
113 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleScript)
114 NS_INTERFACE_MAP_END_INHERITING(LoadedScript)
115
116 NS_IMPL_CYCLE_COLLECTION_CLASS(ModuleScript)
117
118 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ModuleScript, LoadedScript)
119 tmp->UnlinkModuleRecord();
120 tmp->mParseError.setUndefined();
121 tmp->mErrorToRethrow.setUndefined();
122 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
123
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ModuleScript,LoadedScript)124 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ModuleScript, LoadedScript)
125 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
126
127 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ModuleScript, LoadedScript)
128 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mModuleRecord)
129 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mParseError)
130 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mErrorToRethrow)
131 NS_IMPL_CYCLE_COLLECTION_TRACE_END
132
133 NS_IMPL_ADDREF_INHERITED(ModuleScript, LoadedScript)
134 NS_IMPL_RELEASE_INHERITED(ModuleScript, LoadedScript)
135
136 ModuleScript::ModuleScript(ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL,
137 mozilla::dom::Element* aElement)
138 : LoadedScript(ScriptKind::eModule, aFetchOptions, aBaseURL, aElement),
139 mDebuggerDataInitialized(false) {
140 MOZ_ASSERT(!ModuleRecord());
141 MOZ_ASSERT(!HasParseError());
142 MOZ_ASSERT(!HasErrorToRethrow());
143 }
144
UnlinkModuleRecord()145 void ModuleScript::UnlinkModuleRecord() {
146 // Remove the module record's pointer to this object if present and
147 // decrement our reference count. The reference is added by
148 // SetModuleRecord() below.
149 if (mModuleRecord) {
150 MOZ_ASSERT(JS::GetModulePrivate(mModuleRecord).toPrivate() == this);
151 JS::SetModulePrivate(mModuleRecord, JS::UndefinedValue());
152 mModuleRecord = nullptr;
153 }
154 }
155
~ModuleScript()156 ModuleScript::~ModuleScript() {
157 // The object may be destroyed without being unlinked first.
158 UnlinkModuleRecord();
159 }
160
SetModuleRecord(JS::Handle<JSObject * > aModuleRecord)161 void ModuleScript::SetModuleRecord(JS::Handle<JSObject*> aModuleRecord) {
162 MOZ_ASSERT(!mModuleRecord);
163 MOZ_ASSERT_IF(IsModuleScript(), !AsModuleScript()->HasParseError());
164 MOZ_ASSERT_IF(IsModuleScript(), !AsModuleScript()->HasErrorToRethrow());
165
166 mModuleRecord = aModuleRecord;
167
168 // Make module's host defined field point to this object. The JS engine will
169 // increment our reference count by calling HostAddRefTopLevelScript(). This
170 // is decremented when the field is cleared in UnlinkModuleRecord() above or
171 // when the module record dies.
172 MOZ_ASSERT(JS::GetModulePrivate(mModuleRecord).isUndefined());
173 JS::SetModulePrivate(mModuleRecord, JS::PrivateValue(this));
174
175 mozilla::HoldJSObjects(this);
176 }
177
SetParseError(const JS::Value & aError)178 void ModuleScript::SetParseError(const JS::Value& aError) {
179 MOZ_ASSERT(!aError.isUndefined());
180 MOZ_ASSERT(!HasParseError());
181 MOZ_ASSERT(!HasErrorToRethrow());
182
183 UnlinkModuleRecord();
184 mParseError = aError;
185 mozilla::HoldJSObjects(this);
186 }
187
SetErrorToRethrow(const JS::Value & aError)188 void ModuleScript::SetErrorToRethrow(const JS::Value& aError) {
189 MOZ_ASSERT(!aError.isUndefined());
190
191 // This is only called after SetModuleRecord() or SetParseError() so we don't
192 // need to call HoldJSObjects() here.
193 MOZ_ASSERT(ModuleRecord() || HasParseError());
194
195 mErrorToRethrow = aError;
196 }
197
SetDebuggerDataInitialized()198 void ModuleScript::SetDebuggerDataInitialized() {
199 MOZ_ASSERT(ModuleRecord());
200 MOZ_ASSERT(!mDebuggerDataInitialized);
201
202 mDebuggerDataInitialized = true;
203 }
204
205 } // namespace JS::loader
206