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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
|
/* -*- 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/. */
/*
* Options for JavaScript compilation.
*
* In the most common use case, a CompileOptions instance is allocated on the
* stack, and holds non-owning references to non-POD option values: strings,
* principals, objects, and so on. The code declaring the instance guarantees
* that such option values will outlive the CompileOptions itself: objects are
* otherwise rooted, principals have had their reference counts bumped, and
* strings won't be freed until the CompileOptions goes out of scope. In this
* situation, CompileOptions only refers to things others own, so it can be
* lightweight.
*
* In some cases, however, we need to hold compilation options with a
* non-stack-like lifetime. For example, JS::CompileOffThread needs to save
* compilation options where a worker thread can find them, then return
* immediately. The worker thread will come along at some later point, and use
* the options.
*
* The compiler itself just needs to be able to access a collection of options;
* it doesn't care who owns them, or what's keeping them alive. It does its
* own addrefs/copies/tracing/etc.
*
* Furthermore, in some cases compile options are propagated from one entity to
* another (e.g. from a script to a function defined in that script). This
* involves copying over some, but not all, of the options.
*
* So we have a class hierarchy that reflects these four use cases:
*
* - TransitiveCompileOptions is the common base class, representing options
* that should get propagated from a script to functions defined in that
* script. This class is abstract and is only ever used as a subclass.
*
* - ReadOnlyCompileOptions is the only subclass of TransitiveCompileOptions,
* representing a full set of compile options. It can be used by code that
* simply needs to access options set elsewhere, like the compiler. This
* class too is abstract and is only ever used as a subclass.
*
* - The usual CompileOptions class must be stack-allocated, and holds
* non-owning references to the filename, element, and so on. It's derived
* from ReadOnlyCompileOptions, so the compiler can use it.
*
* - OwningCompileOptions roots / copies / reference counts of all its values,
* and unroots / frees / releases them when it is destructed. It too is
* derived from ReadOnlyCompileOptions, so the compiler accepts it.
*/
#ifndef js_CompileOptions_h
#define js_CompileOptions_h
#include "mozilla/Attributes.h" // MOZ_MUST_USE
#include "mozilla/MemoryReporting.h" // mozilla::MallocSizeOf
#include <stddef.h> // size_t
#include <stdint.h> // uint8_t
#include "jstypes.h" // JS_PUBLIC_API
#include "js/RootingAPI.h" // JS::PersistentRooted, JS::Rooted
#include "js/Value.h"
struct JS_PUBLIC_API JSContext;
class JS_PUBLIC_API JSObject;
class JS_PUBLIC_API JSScript;
class JS_PUBLIC_API JSString;
namespace JS {
enum class AsmJSOption : uint8_t {
Enabled,
Disabled,
DisabledByDebugger,
};
/**
* The common base class for the CompileOptions hierarchy.
*
* Use this in code that needs to propagate compile options from one
* compilation unit to another.
*/
class JS_PUBLIC_API TransitiveCompileOptions {
protected:
/**
* The Web Platform allows scripts to be loaded from arbitrary cross-origin
* sources. This allows an attack by which a malicious website loads a
* sensitive file (say, a bank statement) cross-origin (using the user's
* cookies), and sniffs the generated syntax errors (via a window.onerror
* handler) for juicy morsels of its contents.
*
* To counter this attack, HTML5 specifies that script errors should be
* sanitized ("muted") when the script is not same-origin with the global
* for which it is loaded. Callers should set this flag for cross-origin
* scripts, and it will be propagated appropriately to child scripts and
* passed back in JSErrorReports.
*/
bool mutedErrors_ = false;
// Either the Realm configuration or specialized VM operating modes may
// disallow syntax-parse altogether. These conditions are checked in the
// CompileOptions constructor.
bool forceFullParse_ = false;
// Either the Realm configuration or the compile request may force
// strict-mode.
bool forceStrictMode_ = false;
// The context has specified that source pragmas should be parsed.
bool sourcePragmas_ = true;
const char* filename_ = nullptr;
const char* introducerFilename_ = nullptr;
const char16_t* sourceMapURL_ = nullptr;
// Flag used to bypass the filename validation callback.
// See also SetFilenameValidationCallback.
bool skipFilenameValidation_ = false;
public:
// POD options.
bool selfHostingMode = false;
AsmJSOption asmJSOption = AsmJSOption::Disabled;
bool throwOnAsmJSValidationFailureOption = false;
bool forceAsync = false;
bool discardSource = false;
bool sourceIsLazy = false;
bool allowHTMLComments = true;
bool hideScriptFromDebugger = false;
bool nonSyntacticScope = false;
bool privateClassFields = false;
bool privateClassMethods = false;
bool topLevelAwait = false;
// True if transcoding to XDR should use Stencil instead of JSScripts.
bool useStencilXDR = false;
// True if off-thread parsing should use a parse GlobalObject in order to
// directly allocate to the GC from a helper thread. If false, transfer the
// CompilationStencil back to main thread before allocating GC objects.
bool useOffThreadParseGlobal = true;
/**
* |introductionType| is a statically allocated C string: one of "eval",
* "Function", or "GeneratorFunction".
*/
const char* introductionType = nullptr;
unsigned introductionLineno = 0;
uint32_t introductionOffset = 0;
bool hasIntroductionInfo = false;
// Mask of operation kinds which should be instrumented.
uint32_t instrumentationKinds = 0;
protected:
TransitiveCompileOptions() = default;
// Set all POD options (those not requiring reference counts, copies,
// rooting, or other hand-holding) to their values in |rhs|.
void copyPODTransitiveOptions(const TransitiveCompileOptions& rhs);
public:
// Read-only accessors for non-POD options. The proper way to set these
// depends on the derived type.
bool mutedErrors() const { return mutedErrors_; }
bool forceFullParse() const { return forceFullParse_; }
bool forceStrictMode() const { return forceStrictMode_; }
bool skipFilenameValidation() const { return skipFilenameValidation_; }
bool sourcePragmas() const { return sourcePragmas_; }
const char* filename() const { return filename_; }
const char* introducerFilename() const { return introducerFilename_; }
const char16_t* sourceMapURL() const { return sourceMapURL_; }
virtual Value privateValue() const = 0;
virtual JSString* elementAttributeName() const = 0;
virtual JSScript* introductionScript() const = 0;
// For some compilations the spec requires the ScriptOrModule field of the
// resulting script to be set to the currently executing script. This can be
// achieved by setting this option with setScriptOrModule() below.
//
// Note that this field doesn't explicitly exist in our implementation;
// instead the ScriptSourceObject's private value is set to that associated
// with the specified script.
virtual JSScript* scriptOrModule() const = 0;
TransitiveCompileOptions(const TransitiveCompileOptions&) = delete;
TransitiveCompileOptions& operator=(const TransitiveCompileOptions&) = delete;
};
/**
* The class representing a full set of compile options.
*
* Use this in code that only needs to access compilation options created
* elsewhere, like the compiler. Don't instantiate this class (the constructor
* is protected anyway); instead, create instances only of the derived classes:
* CompileOptions and OwningCompileOptions.
*/
class JS_PUBLIC_API ReadOnlyCompileOptions : public TransitiveCompileOptions {
public:
// POD options.
unsigned lineno = 1;
unsigned column = 0;
// The offset within the ScriptSource's full uncompressed text of the first
// character we're presenting for compilation with this CompileOptions.
//
// When we compile a lazy script, we pass the compiler only the substring of
// the source the lazy function occupies. With chunked decompression, we may
// not even have the complete uncompressed source present in memory. But parse
// node positions are offsets within the ScriptSource's full text, and
// BaseScript indicate their substring of the full source by its starting and
// ending offsets within the full text. This scriptSourceOffset field lets the
// frontend convert between these offsets and offsets within the substring
// presented for compilation.
unsigned scriptSourceOffset = 0;
// These only apply to non-function scripts.
bool isRunOnce = false;
bool noScriptRval = false;
protected:
ReadOnlyCompileOptions() = default;
void copyPODNonTransitiveOptions(const ReadOnlyCompileOptions& rhs);
ReadOnlyCompileOptions(const ReadOnlyCompileOptions&) = delete;
ReadOnlyCompileOptions& operator=(const ReadOnlyCompileOptions&) = delete;
};
/**
* Compilation options, with dynamic lifetime. An instance of this type
* makes a copy of / holds / roots all dynamically allocated resources
* (principals; elements; strings) that it refers to. Its destructor frees
* / drops / unroots them. This is heavier than CompileOptions, below, but
* unlike CompileOptions, it can outlive any given stack frame.
*
* Note that this *roots* any JS values it refers to - they're live
* unconditionally. Thus, instances of this type can't be owned, directly
* or indirectly, by a JavaScript object: if any value that this roots ever
* comes to refer to the object that owns this, then the whole cycle, and
* anything else it entrains, will never be freed.
*/
class JS_PUBLIC_API OwningCompileOptions final : public ReadOnlyCompileOptions {
PersistentRooted<JSString*> elementAttributeNameRoot;
PersistentRooted<JSScript*> introductionScriptRoot;
PersistentRooted<JSScript*> scriptOrModuleRoot;
PersistentRooted<Value> privateValueRoot;
public:
// A minimal constructor, for use with OwningCompileOptions::copy.
explicit OwningCompileOptions(JSContext* cx);
~OwningCompileOptions();
Value privateValue() const override { return privateValueRoot; }
JSString* elementAttributeName() const override {
return elementAttributeNameRoot;
}
JSScript* introductionScript() const override {
return introductionScriptRoot;
}
JSScript* scriptOrModule() const override { return scriptOrModuleRoot; }
/** Set this to a copy of |rhs|. Return false on OOM. */
bool copy(JSContext* cx, const ReadOnlyCompileOptions& rhs);
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
OwningCompileOptions& setIsRunOnce(bool once) {
isRunOnce = once;
return *this;
}
OwningCompileOptions& setForceStrictMode() {
forceStrictMode_ = true;
return *this;
}
OwningCompileOptions& setModule() {
// ES6 10.2.1 Module code is always strict mode code.
setForceStrictMode();
setIsRunOnce(true);
allowHTMLComments = false;
return *this;
}
private:
void release();
OwningCompileOptions(const OwningCompileOptions&) = delete;
OwningCompileOptions& operator=(const OwningCompileOptions&) = delete;
};
/**
* Compilation options stored on the stack. An instance of this type
* simply holds references to dynamically allocated resources (element;
* filename; source map URL) that are owned by something else. If you
* create an instance of this type, it's up to you to guarantee that
* everything you store in it will outlive it.
*/
class MOZ_STACK_CLASS JS_PUBLIC_API CompileOptions final
: public ReadOnlyCompileOptions {
private:
Rooted<JSString*> elementAttributeNameRoot;
Rooted<JSScript*> introductionScriptRoot;
Rooted<JSScript*> scriptOrModuleRoot;
Rooted<Value> privateValueRoot;
public:
// Default options determined using the JSContext.
explicit CompileOptions(JSContext* cx);
// Copy both the transitive and the non-transitive options from another
// options object.
CompileOptions(JSContext* cx, const ReadOnlyCompileOptions& rhs)
: ReadOnlyCompileOptions(),
elementAttributeNameRoot(cx),
introductionScriptRoot(cx),
scriptOrModuleRoot(cx),
privateValueRoot(cx) {
copyPODNonTransitiveOptions(rhs);
copyPODTransitiveOptions(rhs);
filename_ = rhs.filename();
introducerFilename_ = rhs.introducerFilename();
sourceMapURL_ = rhs.sourceMapURL();
privateValueRoot = rhs.privateValue();
elementAttributeNameRoot = rhs.elementAttributeName();
introductionScriptRoot = rhs.introductionScript();
scriptOrModuleRoot = rhs.scriptOrModule();
}
Value privateValue() const override { return privateValueRoot; }
JSString* elementAttributeName() const override {
return elementAttributeNameRoot;
}
JSScript* introductionScript() const override {
return introductionScriptRoot;
}
JSScript* scriptOrModule() const override { return scriptOrModuleRoot; }
CompileOptions& setFile(const char* f) {
filename_ = f;
return *this;
}
CompileOptions& setLine(unsigned l) {
lineno = l;
return *this;
}
CompileOptions& setFileAndLine(const char* f, unsigned l) {
filename_ = f;
lineno = l;
return *this;
}
CompileOptions& setSourceMapURL(const char16_t* s) {
sourceMapURL_ = s;
return *this;
}
CompileOptions& setPrivateValue(const Value& v) {
privateValueRoot = v;
return *this;
}
CompileOptions& setElementAttributeName(JSString* p) {
elementAttributeNameRoot = p;
return *this;
}
CompileOptions& setScriptOrModule(JSScript* s) {
scriptOrModuleRoot = s;
return *this;
}
CompileOptions& setMutedErrors(bool mute) {
mutedErrors_ = mute;
return *this;
}
CompileOptions& setColumn(unsigned c) {
column = c;
return *this;
}
CompileOptions& setScriptSourceOffset(unsigned o) {
scriptSourceOffset = o;
return *this;
}
CompileOptions& setIsRunOnce(bool once) {
isRunOnce = once;
return *this;
}
CompileOptions& setNoScriptRval(bool nsr) {
noScriptRval = nsr;
return *this;
}
CompileOptions& setSkipFilenameValidation(bool b) {
skipFilenameValidation_ = b;
return *this;
}
CompileOptions& setSelfHostingMode(bool shm) {
selfHostingMode = shm;
return *this;
}
CompileOptions& setSourceIsLazy(bool l) {
sourceIsLazy = l;
return *this;
}
CompileOptions& setNonSyntacticScope(bool n) {
nonSyntacticScope = n;
return *this;
}
CompileOptions& setIntroductionType(const char* t) {
introductionType = t;
return *this;
}
CompileOptions& setIntroductionInfo(const char* introducerFn,
const char* intro, unsigned line,
JSScript* script, uint32_t offset) {
introducerFilename_ = introducerFn;
introductionType = intro;
introductionLineno = line;
introductionScriptRoot = script;
introductionOffset = offset;
hasIntroductionInfo = true;
return *this;
}
// Set introduction information according to any currently executing script.
CompileOptions& setIntroductionInfoToCaller(JSContext* cx,
const char* introductionType);
CompileOptions& setForceFullParse() {
forceFullParse_ = true;
return *this;
}
CompileOptions& setForceStrictMode() {
forceStrictMode_ = true;
return *this;
}
CompileOptions& setModule() {
// ES6 10.2.1 Module code is always strict mode code.
setForceStrictMode();
setIsRunOnce(true);
allowHTMLComments = false;
return *this;
}
CompileOptions(const CompileOptions& rhs) = delete;
CompileOptions& operator=(const CompileOptions& rhs) = delete;
};
} // namespace JS
#endif /* js_CompileOptions_h */
|