Line data Source code
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 "nsString.h"
24 :
25 : class nsIScriptContext;
26 : class nsIScriptGlobalObject;
27 :
28 : namespace mozilla {
29 : namespace dom {
30 : class AutoJSAPI;
31 : class Element;
32 : } // namespace dom
33 : } // namespace mozilla
34 :
35 : class nsJSUtils
36 : {
37 : public:
38 : static bool GetCallingLocation(JSContext* aContext, nsACString& aFilename,
39 : uint32_t* aLineno = nullptr,
40 : uint32_t* aColumn = nullptr);
41 : static bool GetCallingLocation(JSContext* aContext, nsAString& aFilename,
42 : uint32_t* aLineno = nullptr,
43 : uint32_t* aColumn = nullptr);
44 :
45 : static nsIScriptGlobalObject *GetStaticScriptGlobal(JSObject* aObj);
46 :
47 : static nsIScriptContext *GetStaticScriptContext(JSObject* aObj);
48 :
49 : /**
50 : * Retrieve the inner window ID based on the given JSContext.
51 : *
52 : * @param JSContext aContext
53 : * The JSContext from which you want to find the inner window ID.
54 : *
55 : * @returns uint64_t the inner window ID.
56 : */
57 : static uint64_t GetCurrentlyRunningCodeInnerWindowID(JSContext *aContext);
58 :
59 : static nsresult CompileFunction(mozilla::dom::AutoJSAPI& jsapi,
60 : JS::AutoObjectVector& aScopeChain,
61 : JS::CompileOptions& aOptions,
62 : const nsACString& aName,
63 : uint32_t aArgCount,
64 : const char** aArgArray,
65 : const nsAString& aBody,
66 : JSObject** aFunctionObject);
67 :
68 :
69 : // ExecutionContext is used to switch compartment.
70 : class MOZ_STACK_CLASS ExecutionContext {
71 : // Register stack annotations for the Gecko profiler.
72 : mozilla::AutoProfilerLabel mAutoProfilerLabel;
73 :
74 : JSContext* mCx;
75 :
76 : // Handles switching to our global's compartment.
77 : JSAutoCompartment mCompartment;
78 :
79 : // Set to a valid handle if a return value is expected.
80 : JS::Rooted<JS::Value> mRetValue;
81 :
82 : // Scope chain in which the execution takes place.
83 : JS::AutoObjectVector mScopeChain;
84 :
85 : // returned value forwarded when we have to interupt the execution eagerly
86 : // with mSkip.
87 : nsresult mRv;
88 :
89 : // Used to skip upcoming phases in case of a failure. In such case the
90 : // result is carried by mRv.
91 : bool mSkip;
92 :
93 : // Should the result be serialized before being returned.
94 : bool mCoerceToString;
95 :
96 : // Encode the bytecode before it is being executed.
97 : bool mEncodeBytecode;
98 :
99 : #ifdef DEBUG
100 : // Should we set the return value.
101 : bool mWantsReturnValue;
102 :
103 : bool mExpectScopeChain;
104 : #endif
105 :
106 : public:
107 :
108 : // Enter compartment in which the code would be executed. The JSContext
109 : // must come from an AutoEntryScript that has had
110 : // TakeOwnershipOfErrorReporting() called on it.
111 : ExecutionContext(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
112 :
113 : ExecutionContext(const ExecutionContext&) = delete;
114 : ExecutionContext(ExecutionContext&&) = delete;
115 :
116 234 : ~ExecutionContext() {
117 : // This flag is resetted, when the returned value is extracted.
118 117 : MOZ_ASSERT(!mWantsReturnValue);
119 117 : }
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 : }
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 5 : ExecutionContext& SetEncodeBytecode(bool aEncodeBytecode) {
133 5 : mEncodeBytecode = aEncodeBytecode;
134 5 : return *this;
135 : }
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(void **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(void **aOffThreadToken);
178 : };
179 :
180 : static nsresult CompileModule(JSContext* aCx,
181 : JS::SourceBufferHolder& aSrcBuf,
182 : JS::Handle<JSObject*> aEvaluationGlobal,
183 : JS::CompileOptions &aCompileOptions,
184 : JS::MutableHandle<JSObject*> aModule);
185 :
186 : static nsresult ModuleDeclarationInstantiation(JSContext* aCx,
187 : JS::Handle<JSObject*> aModule);
188 :
189 : static nsresult ModuleEvaluation(JSContext* aCx,
190 : JS::Handle<JSObject*> aModule);
191 :
192 : // Returns false if an exception got thrown on aCx. Passing a null
193 : // aElement is allowed; that wil produce an empty aScopeChain.
194 : static bool GetScopeChainForElement(JSContext* aCx,
195 : mozilla::dom::Element* aElement,
196 : JS::AutoObjectVector& aScopeChain);
197 :
198 : static void ResetTimeZone();
199 : };
200 :
201 : template<typename T>
202 : inline bool
203 6326 : AssignJSString(JSContext *cx, T &dest, JSString *s)
204 : {
205 6326 : size_t len = js::GetStringLength(s);
206 : static_assert(js::MaxStringLength < (1 << 28),
207 : "Shouldn't overflow here or in SetCapacity");
208 6326 : if (MOZ_UNLIKELY(!dest.SetLength(len, mozilla::fallible))) {
209 0 : JS_ReportOutOfMemory(cx);
210 0 : return false;
211 : }
212 6326 : return js::CopyStringChars(cx, dest.BeginWriting(), s, len);
213 : }
214 :
215 : inline void
216 0 : AssignJSFlatString(nsAString &dest, JSFlatString *s)
217 : {
218 0 : size_t len = js::GetFlatStringLength(s);
219 : static_assert(js::MaxStringLength < (1 << 28),
220 : "Shouldn't overflow here or in SetCapacity");
221 0 : dest.SetLength(len);
222 0 : js::CopyFlatStringChars(dest.BeginWriting(), s, len);
223 0 : }
224 :
225 : class nsAutoJSString : public nsAutoString
226 : {
227 : public:
228 :
229 : /**
230 : * nsAutoJSString should be default constructed, which leaves it empty
231 : * (this->IsEmpty()), and initialized with one of the init() methods below.
232 : */
233 3671 : nsAutoJSString() {}
234 :
235 3671 : bool init(JSContext* aContext, JSString* str)
236 : {
237 3671 : return AssignJSString(aContext, *this, str);
238 : }
239 :
240 24 : bool init(JSContext* aContext, const JS::Value &v)
241 : {
242 24 : if (v.isString()) {
243 24 : return init(aContext, v.toString());
244 : }
245 :
246 : // Stringify, making sure not to run script.
247 0 : JS::Rooted<JSString*> str(aContext);
248 0 : if (v.isObject()) {
249 0 : str = JS_NewStringCopyZ(aContext, "[Object]");
250 : } else {
251 0 : JS::Rooted<JS::Value> rootedVal(aContext, v);
252 0 : str = JS::ToString(aContext, rootedVal);
253 : }
254 :
255 0 : return str && init(aContext, str);
256 : }
257 :
258 18 : bool init(JSContext* aContext, jsid id)
259 : {
260 36 : JS::Rooted<JS::Value> v(aContext);
261 36 : return JS_IdToValue(aContext, id, &v) && init(aContext, v);
262 : }
263 :
264 : bool init(const JS::Value &v);
265 :
266 3671 : ~nsAutoJSString() {}
267 : };
268 :
269 : #endif /* nsJSUtils_h__ */
|