summaryrefslogtreecommitdiff
path: root/dom/base/nsJSUtils.h
blob: 53b1330ed7d93e17901ba3f474f0a1342d95d331 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef nsJSUtils_h__
#define nsJSUtils_h__

/**
 * This is not a generated file. It contains common utility functions 
 * invoked from the JavaScript code generated from IDL interfaces.
 * The goal of the utility functions is to cut down on the size of
 * the generated code itself.
 */

#include "mozilla/Assertions.h"

#include "jsapi.h"
#include "jsfriendapi.h"
#include "js/Conversions.h"
#include "nsString.h"

class nsIScriptContext;
class nsIScriptGlobalObject;

namespace mozilla {
namespace dom {
class AutoJSAPI;
class Element;
} // namespace dom
} // namespace mozilla

class nsJSUtils
{
public:
  static bool GetCallingLocation(JSContext* aContext, nsACString& aFilename,
                                 uint32_t* aLineno = nullptr,
                                 uint32_t* aColumn = nullptr);
  static bool GetCallingLocation(JSContext* aContext, nsAString& aFilename,
                                 uint32_t* aLineno = nullptr,
                                 uint32_t* aColumn = nullptr);

  static nsIScriptGlobalObject *GetStaticScriptGlobal(JSObject* aObj);

  static nsIScriptContext *GetStaticScriptContext(JSObject* aObj);

  /**
   * Retrieve the inner window ID based on the given JSContext.
   *
   * @param JSContext aContext
   *        The JSContext from which you want to find the inner window ID.
   *
   * @returns uint64_t the inner window ID.
   */
  static uint64_t GetCurrentlyRunningCodeInnerWindowID(JSContext *aContext);

  static nsresult CompileFunction(mozilla::dom::AutoJSAPI& jsapi,
                                  JS::AutoObjectVector& aScopeChain,
                                  JS::CompileOptions& aOptions,
                                  const nsACString& aName,
                                  uint32_t aArgCount,
                                  const char** aArgArray,
                                  const nsAString& aBody,
                                  JSObject** aFunctionObject);

  // ExecutionContext is used to switch compartment.
  class MOZ_STACK_CLASS ExecutionContext {
    JSContext* mCx;

    // Handles switching to our global's compartment.
    JSAutoCompartment mCompartment;

    // Set to a valid handle if a return value is expected.
    JS::Rooted<JS::Value> mRetValue;

    // Scope chain in which the execution takes place.
    JS::AutoObjectVector mScopeChain;

    // The compiled script.
    JS::Rooted<JSScript*> mScript;

    // returned value forwarded when we have to interupt the execution eagerly
    // with mSkip.
    nsresult mRv;

    // Used to skip upcoming phases in case of a failure.  In such case the
    // result is carried by mRv.
    bool mSkip;

    // Should the result be serialized before being returned.
    bool mCoerceToString;

    // Encode the bytecode before it is being executed.
    bool mEncodeBytecode;

#ifdef DEBUG
    // Should we set the return value.
    bool mWantsReturnValue;

    bool mExpectScopeChain;

    bool mScriptUsed;
#endif

   public:

    // Enter compartment in which the code would be executed.  The JSContext
    // must come from an AutoEntryScript that has had
    // TakeOwnershipOfErrorReporting() called on it.
    ExecutionContext(JSContext* aCx, JS::Handle<JSObject*> aGlobal);

    ExecutionContext(const ExecutionContext&) = delete;
    ExecutionContext(ExecutionContext&&) = delete;

    ~ExecutionContext() {
      // This flag is reset when the returned value is extracted.
      MOZ_ASSERT_IF(!mSkip, !mWantsReturnValue);

      // If encoding was started we expect the script to have been
      // used when ending the encoding.
      MOZ_ASSERT_IF(mEncodeBytecode && mScript && mRv == NS_OK, mScriptUsed);
    }

    // The returned value would be converted to a string if the
    // |aCoerceToString| is flag set.
    ExecutionContext& SetCoerceToString(bool aCoerceToString) {
      mCoerceToString = aCoerceToString;
      return *this;
    }

    // When set, this flag records and encodes the bytecode as soon as it is
    // being compiled, and before it is being executed. The bytecode can then be
    // requested by using |JS::FinishIncrementalEncoding| with the mutable
    // handle |aScript| argument of |CompileAndExec| or |JoinAndExec|.
    ExecutionContext& SetEncodeBytecode(bool aEncodeBytecode) {
      mEncodeBytecode = aEncodeBytecode;
      return *this;
    }

    // Set the scope chain in which the code should be executed.
    void SetScopeChain(const JS::AutoObjectVector& aScopeChain);

    // After getting a notification that an off-thread compilation terminated,
    // this function will take the result of the parser and move it to the main
    // thread.
    [[nodiscard]] nsresult JoinCompile(void** aOffThreadToken);

    // Compile a script contained in a SourceText.
    nsresult Compile(JS::CompileOptions& aCompileOptions,
                     JS::SourceBufferHolder& aSrcBuf);

    // Compile a script contained in a string.
    nsresult Compile(JS::CompileOptions& aCompileOptions,
                     const nsAString& aScript);

    // Decode a script contained in a buffer.
    nsresult Decode(JS::CompileOptions& aCompileOptions,
                    mozilla::Vector<uint8_t>& aBytecodeBuf,
                    size_t aBytecodeIndex);

    // After getting a notification that an off-thread decoding terminated, this
    // function will get the result of the decoder and move it to the main
    // thread.
    nsresult JoinDecode(void** aOffThreadToken);

    // Get a successfully compiled script.
    JSScript* GetScript();

    // Execute the compiled script and ignore the return value.
    [[nodiscard]] nsresult ExecScript();

    // Execute the compiled script a get the return value.
    //
    // Copy the returned value into the mutable handle argument. In case of a
    // evaluation failure either during the execution or the conversion of the
    // result to a string, the nsresult is be set to the corresponding result
    // code and the mutable handle argument remains unchanged.
    //
    // The value returned in the mutable handle argument is part of the
    // compartment given as argument to the ExecutionContext constructor. If the
    // caller is in a different compartment, then the out-param value should be
    // wrapped by calling |JS_WrapValue|.
    [[nodiscard]] nsresult
    ExtractReturnValue(JS::MutableHandle<JS::Value> aRetValue);

    [[nodiscard]] nsresult ExecScript(JS::MutableHandle<JS::Value> aRetValue);
  };

  static nsresult CompileModule(JSContext* aCx,
                                JS::SourceBufferHolder& aSrcBuf,
                                JS::Handle<JSObject*> aEvaluationGlobal,
                                JS::CompileOptions &aCompileOptions,
                                JS::MutableHandle<JSObject*> aModule);

  static nsresult ModuleInstantiate(JSContext* aCx,
                                    JS::Handle<JSObject*> aModule);

  static nsresult ModuleEvaluate(JSContext* aCx,
                                 JS::Handle<JSObject*> aModule);

  // Returns false if an exception got thrown on aCx.  Passing a null
  // aElement is allowed; that wil produce an empty aScopeChain.
  static bool GetScopeChainForElement(JSContext* aCx,
                                      mozilla::dom::Element* aElement,
                                      JS::AutoObjectVector& aScopeChain);

  static void ResetTimeZone();
};

template<typename T>
inline bool
AssignJSString(JSContext *cx, T &dest, JSString *s)
{
  size_t len = js::GetStringLength(s);
  static_assert(js::MaxStringLength < (1 << 28),
                "Shouldn't overflow here or in SetCapacity");
  if (MOZ_UNLIKELY(!dest.SetLength(len, mozilla::fallible))) {
    JS_ReportOutOfMemory(cx);
    return false;
  }
  return js::CopyStringChars(cx, dest.BeginWriting(), s, len);
}

inline void
AssignJSFlatString(nsAString &dest, JSFlatString *s)
{
  size_t len = js::GetFlatStringLength(s);
  static_assert(js::MaxStringLength < (1 << 28),
                "Shouldn't overflow here or in SetCapacity");
  dest.SetLength(len);
  js::CopyFlatStringChars(dest.BeginWriting(), s, len);
}

class nsAutoJSString : public nsAutoString
{
public:

  /**
   * nsAutoJSString should be default constructed, which leaves it empty
   * (this->IsEmpty()), and initialized with one of the init() methods below.
   */
  nsAutoJSString() {}

  bool init(JSContext* aContext, JSString* str)
  {
    return AssignJSString(aContext, *this, str);
  }

  bool init(JSContext* aContext, const JS::Value &v)
  {
    if (v.isString()) {
      return init(aContext, v.toString());
    }

    // Stringify, making sure not to run script.
    JS::Rooted<JSString*> str(aContext);
    if (v.isObject()) {
      str = JS_NewStringCopyZ(aContext, "[Object]");
    } else {
      JS::Rooted<JS::Value> rootedVal(aContext, v);
      str = JS::ToString(aContext, rootedVal);
    }

    return str && init(aContext, str);
  }

  bool init(JSContext* aContext, jsid id)
  {
    JS::Rooted<JS::Value> v(aContext);
    return JS_IdToValue(aContext, id, &v) && init(aContext, v);
  }

  bool init(const JS::Value &v);

  ~nsAutoJSString() {}
};

#endif /* nsJSUtils_h__ */