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