/src/mozilla-central/dom/base/nsJSUtils.h
Line | Count | Source (jump to first uncovered line) |
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 | | #ifndef nsJSUtils_h__ |
8 | | #define nsJSUtils_h__ |
9 | | |
10 | | /** |
11 | | * This is not a generated file. It contains common utility functions |
12 | | * invoked from the JavaScript code generated from IDL interfaces. |
13 | | * The goal of the utility functions is to cut down on the size of |
14 | | * the generated code itself. |
15 | | */ |
16 | | |
17 | | #include "mozilla/Assertions.h" |
18 | | |
19 | | #include "GeckoProfiler.h" |
20 | | #include "jsapi.h" |
21 | | #include "jsfriendapi.h" |
22 | | #include "js/Conversions.h" |
23 | | #include "js/StableStringChars.h" |
24 | | #include "nsString.h" |
25 | | |
26 | | class nsIScriptContext; |
27 | | class nsIScriptElement; |
28 | | class nsIScriptGlobalObject; |
29 | | class nsXBLPrototypeBinding; |
30 | | |
31 | | namespace mozilla { |
32 | | namespace dom { |
33 | | class AutoJSAPI; |
34 | | class Element; |
35 | | } // namespace dom |
36 | | } // namespace mozilla |
37 | | |
38 | | class nsJSUtils |
39 | | { |
40 | | public: |
41 | | static bool GetCallingLocation(JSContext* aContext, nsACString& aFilename, |
42 | | uint32_t* aLineno = nullptr, |
43 | | uint32_t* aColumn = nullptr); |
44 | | static bool GetCallingLocation(JSContext* aContext, nsAString& aFilename, |
45 | | uint32_t* aLineno = nullptr, |
46 | | uint32_t* aColumn = nullptr); |
47 | | |
48 | | /** |
49 | | * Retrieve the inner window ID based on the given JSContext. |
50 | | * |
51 | | * @param JSContext aContext |
52 | | * The JSContext from which you want to find the inner window ID. |
53 | | * |
54 | | * @returns uint64_t the inner window ID. |
55 | | */ |
56 | | static uint64_t GetCurrentlyRunningCodeInnerWindowID(JSContext *aContext); |
57 | | |
58 | | static nsresult CompileFunction(mozilla::dom::AutoJSAPI& jsapi, |
59 | | JS::AutoObjectVector& aScopeChain, |
60 | | JS::CompileOptions& aOptions, |
61 | | const nsACString& aName, |
62 | | uint32_t aArgCount, |
63 | | const char** aArgArray, |
64 | | const nsAString& aBody, |
65 | | JSObject** aFunctionObject); |
66 | | |
67 | | |
68 | | // ExecutionContext is used to switch compartment. |
69 | | class MOZ_STACK_CLASS ExecutionContext { |
70 | | #ifdef MOZ_GECKO_PROFILER |
71 | | // Register stack annotations for the Gecko profiler. |
72 | | mozilla::AutoProfilerLabel mAutoProfilerLabel; |
73 | | #endif |
74 | | |
75 | | JSContext* mCx; |
76 | | |
77 | | // Handles switching to our global's realm. |
78 | | JSAutoRealm mRealm; |
79 | | |
80 | | // Set to a valid handle if a return value is expected. |
81 | | JS::Rooted<JS::Value> mRetValue; |
82 | | |
83 | | // Scope chain in which the execution takes place. |
84 | | JS::AutoObjectVector mScopeChain; |
85 | | |
86 | | // returned value forwarded when we have to interupt the execution eagerly |
87 | | // with mSkip. |
88 | | nsresult mRv; |
89 | | |
90 | | // Used to skip upcoming phases in case of a failure. In such case the |
91 | | // result is carried by mRv. |
92 | | bool mSkip; |
93 | | |
94 | | // Should the result be serialized before being returned. |
95 | | bool mCoerceToString; |
96 | | |
97 | | // Encode the bytecode before it is being executed. |
98 | | bool mEncodeBytecode; |
99 | | |
100 | | #ifdef DEBUG |
101 | | // Should we set the return value. |
102 | | bool mWantsReturnValue; |
103 | | |
104 | | bool mExpectScopeChain; |
105 | | #endif |
106 | | |
107 | | public: |
108 | | |
109 | | // Enter compartment in which the code would be executed. The JSContext |
110 | | // must come from an AutoEntryScript. |
111 | | ExecutionContext(JSContext* aCx, JS::Handle<JSObject*> aGlobal); |
112 | | |
113 | | ExecutionContext(const ExecutionContext&) = delete; |
114 | | ExecutionContext(ExecutionContext&&) = delete; |
115 | | |
116 | 0 | ~ExecutionContext() { |
117 | 0 | // This flag is resetted, when the returned value is extracted. |
118 | 0 | MOZ_ASSERT(!mWantsReturnValue); |
119 | 0 | } |
120 | | |
121 | | // The returned value would be converted to a string if the |
122 | | // |aCoerceToString| is flag set. |
123 | 0 | ExecutionContext& SetCoerceToString(bool aCoerceToString) { |
124 | 0 | mCoerceToString = aCoerceToString; |
125 | 0 | return *this; |
126 | 0 | } |
127 | | |
128 | | // When set, this flag records and encodes the bytecode as soon as it is |
129 | | // being compiled, and before it is being executed. The bytecode can then be |
130 | | // requested by using |JS::FinishIncrementalEncoding| with the mutable |
131 | | // handle |aScript| argument of |CompileAndExec| or |JoinAndExec|. |
132 | 0 | ExecutionContext& SetEncodeBytecode(bool aEncodeBytecode) { |
133 | 0 | mEncodeBytecode = aEncodeBytecode; |
134 | 0 | return *this; |
135 | 0 | } |
136 | | |
137 | | // Set the scope chain in which the code should be executed. |
138 | | void SetScopeChain(const JS::AutoObjectVector& aScopeChain); |
139 | | |
140 | | // Copy the returned value in the mutable handle argument, in case of a |
141 | | // evaluation failure either during the execution or the conversion of the |
142 | | // result to a string, the nsresult would be set to the corresponding result |
143 | | // code, and the mutable handle argument would remain unchanged. |
144 | | // |
145 | | // The value returned in the mutable handle argument is part of the |
146 | | // compartment given as argument to the ExecutionContext constructor. If the |
147 | | // caller is in a different compartment, then the out-param value should be |
148 | | // wrapped by calling |JS_WrapValue|. |
149 | | MOZ_MUST_USE nsresult |
150 | | ExtractReturnValue(JS::MutableHandle<JS::Value> aRetValue); |
151 | | |
152 | | // After getting a notification that an off-thread compilation terminated, |
153 | | // this function will take the result of the parser by moving it to the main |
154 | | // thread before starting the execution of the script. |
155 | | // |
156 | | // The compiled script would be returned in the |aScript| out-param. |
157 | | MOZ_MUST_USE nsresult JoinAndExec(JS::OffThreadToken** aOffThreadToken, |
158 | | JS::MutableHandle<JSScript*> aScript); |
159 | | |
160 | | // Compile a script contained in a SourceBuffer, and execute it. |
161 | | nsresult CompileAndExec(JS::CompileOptions& aCompileOptions, |
162 | | JS::SourceBufferHolder& aSrcBuf, |
163 | | JS::MutableHandle<JSScript*> aScript); |
164 | | |
165 | | // Compile a script contained in a string, and execute it. |
166 | | nsresult CompileAndExec(JS::CompileOptions& aCompileOptions, |
167 | | const nsAString& aScript); |
168 | | |
169 | | // Decode a script contained in a buffer, and execute it. |
170 | | MOZ_MUST_USE nsresult DecodeAndExec(JS::CompileOptions& aCompileOptions, |
171 | | mozilla::Vector<uint8_t>& aBytecodeBuf, |
172 | | size_t aBytecodeIndex); |
173 | | |
174 | | // After getting a notification that an off-thread decoding terminated, this |
175 | | // function will get the result of the decoder by moving it to the main |
176 | | // thread before starting the execution of the script. |
177 | | MOZ_MUST_USE nsresult DecodeJoinAndExec(JS::OffThreadToken** aOffThreadToken); |
178 | | |
179 | | MOZ_MUST_USE nsresult DecodeBinASTJoinAndExec(JS::OffThreadToken** aOffThreadToken, |
180 | | JS::MutableHandle<JSScript*> aScript); |
181 | | |
182 | | // Decode a BinAST encoded script contained in a buffer, and execute it. |
183 | | nsresult DecodeBinASTAndExec(JS::CompileOptions& aCompileOptions, |
184 | | const uint8_t* aBuf, size_t aLength, |
185 | | JS::MutableHandle<JSScript*> aScript); |
186 | | }; |
187 | | |
188 | | static nsresult CompileModule(JSContext* aCx, |
189 | | JS::SourceBufferHolder& aSrcBuf, |
190 | | JS::Handle<JSObject*> aEvaluationGlobal, |
191 | | JS::CompileOptions &aCompileOptions, |
192 | | JS::MutableHandle<JSScript*> aScript); |
193 | | |
194 | | static nsresult InitModuleSourceElement(JSContext* aCx, |
195 | | JS::Handle<JSScript*> aScript, |
196 | | nsIScriptElement* aElement); |
197 | | |
198 | | static nsresult ModuleInstantiate(JSContext* aCx, |
199 | | JS::Handle<JSScript*> aScript); |
200 | | |
201 | | static nsresult ModuleEvaluate(JSContext* aCx, |
202 | | JS::Handle<JSScript*> aScript); |
203 | | |
204 | | // Returns false if an exception got thrown on aCx. Passing a null |
205 | | // aElement is allowed; that wil produce an empty aScopeChain. |
206 | | static bool GetScopeChainForElement(JSContext* aCx, |
207 | | mozilla::dom::Element* aElement, |
208 | | JS::AutoObjectVector& aScopeChain); |
209 | | |
210 | | // Returns a scope chain suitable for XBL execution. |
211 | | // |
212 | | // This is by default GetScopeChainForElemenet, but will be different if the |
213 | | // <binding> element had the simpleScopeChain attribute. |
214 | | // |
215 | | // This is to prevent footguns like bug 1446342. |
216 | | static bool GetScopeChainForXBL(JSContext* aCx, |
217 | | mozilla::dom::Element* aBoundElement, |
218 | | const nsXBLPrototypeBinding& aProtoBinding, |
219 | | JS::AutoObjectVector& aScopeChain); |
220 | | |
221 | | static void ResetTimeZone(); |
222 | | }; |
223 | | |
224 | | template<typename T> |
225 | | inline bool |
226 | | AssignJSString(JSContext *cx, T &dest, JSString *s) |
227 | 14 | { |
228 | 14 | size_t len = JS::GetStringLength(s); |
229 | 14 | static_assert(js::MaxStringLength < (1 << 28), |
230 | 14 | "Shouldn't overflow here or in SetCapacity"); |
231 | 14 | if (MOZ_UNLIKELY(!dest.SetLength(len, mozilla::fallible))) { |
232 | 0 | JS_ReportOutOfMemory(cx); |
233 | 0 | return false; |
234 | 0 | } |
235 | 14 | return js::CopyStringChars(cx, dest.BeginWriting(), s, len); |
236 | 14 | } Unexecuted instantiation: bool AssignJSString<nsAutoJSString>(JSContext*, nsAutoJSString&, JSString*) Unexecuted instantiation: bool AssignJSString<nsTString<char16_t> >(JSContext*, nsTString<char16_t>&, JSString*) bool AssignJSString<mozilla::dom::binding_detail::FakeString>(JSContext*, mozilla::dom::binding_detail::FakeString&, JSString*) Line | Count | Source | 227 | 14 | { | 228 | 14 | size_t len = JS::GetStringLength(s); | 229 | 14 | static_assert(js::MaxStringLength < (1 << 28), | 230 | 14 | "Shouldn't overflow here or in SetCapacity"); | 231 | 14 | if (MOZ_UNLIKELY(!dest.SetLength(len, mozilla::fallible))) { | 232 | 0 | JS_ReportOutOfMemory(cx); | 233 | 0 | return false; | 234 | 0 | } | 235 | 14 | return js::CopyStringChars(cx, dest.BeginWriting(), s, len); | 236 | 14 | } |
Unexecuted instantiation: bool AssignJSString<nsTSubstring<char16_t> >(JSContext*, nsTSubstring<char16_t>&, JSString*) Unexecuted instantiation: bool AssignJSString<nsTAutoStringN<char16_t, 64ul> >(JSContext*, nsTAutoStringN<char16_t, 64ul>&, JSString*) |
237 | | |
238 | | inline void |
239 | | AssignJSFlatString(nsAString &dest, JSFlatString *s) |
240 | 0 | { |
241 | 0 | size_t len = js::GetFlatStringLength(s); |
242 | 0 | static_assert(js::MaxStringLength < (1 << 28), |
243 | 0 | "Shouldn't overflow here or in SetCapacity"); |
244 | 0 | dest.SetLength(len); |
245 | 0 | js::CopyFlatStringChars(dest.BeginWriting(), s, len); |
246 | 0 | } |
247 | | |
248 | | class nsAutoJSString : public nsAutoString |
249 | | { |
250 | | public: |
251 | | |
252 | | /** |
253 | | * nsAutoJSString should be default constructed, which leaves it empty |
254 | | * (this->IsEmpty()), and initialized with one of the init() methods below. |
255 | | */ |
256 | 0 | nsAutoJSString() {} |
257 | | |
258 | | bool init(JSContext* aContext, JSString* str) |
259 | 0 | { |
260 | 0 | return AssignJSString(aContext, *this, str); |
261 | 0 | } |
262 | | |
263 | | bool init(JSContext* aContext, const JS::Value &v) |
264 | 0 | { |
265 | 0 | if (v.isString()) { |
266 | 0 | return init(aContext, v.toString()); |
267 | 0 | } |
268 | 0 | |
269 | 0 | // Stringify, making sure not to run script. |
270 | 0 | JS::Rooted<JSString*> str(aContext); |
271 | 0 | if (v.isObject()) { |
272 | 0 | str = JS_NewStringCopyZ(aContext, "[Object]"); |
273 | 0 | } else { |
274 | 0 | JS::Rooted<JS::Value> rootedVal(aContext, v); |
275 | 0 | str = JS::ToString(aContext, rootedVal); |
276 | 0 | } |
277 | 0 |
|
278 | 0 | return str && init(aContext, str); |
279 | 0 | } |
280 | | |
281 | | bool init(JSContext* aContext, jsid id) |
282 | 0 | { |
283 | 0 | JS::Rooted<JS::Value> v(aContext); |
284 | 0 | return JS_IdToValue(aContext, id, &v) && init(aContext, v); |
285 | 0 | } |
286 | | |
287 | | bool init(const JS::Value &v); |
288 | | |
289 | 0 | ~nsAutoJSString() {} |
290 | | }; |
291 | | |
292 | | #endif /* nsJSUtils_h__ */ |