Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/js/CompileOptions.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
/*
7
 * Options for JavaScript compilation.
8
 *
9
 * In the most common use case, a CompileOptions instance is allocated on the
10
 * stack, and holds non-owning references to non-POD option values: strings,
11
 * principals, objects, and so on.  The code declaring the instance guarantees
12
 * that such option values will outlive the CompileOptions itself: objects are
13
 * otherwise rooted, principals have had their reference counts bumped, and
14
 * strings won't be freed until the CompileOptions goes out of scope.  In this
15
 * situation, CompileOptions only refers to things others own, so it can be
16
 * lightweight.
17
 *
18
 * In some cases, however, we need to hold compilation options with a
19
 * non-stack-like lifetime.  For example, JS::CompileOffThread needs to save
20
 * compilation options where a worker thread can find them, then return
21
 * immediately.  The worker thread will come along at some later point, and use
22
 * the options.
23
 *
24
 * The compiler itself just needs to be able to access a collection of options;
25
 * it doesn't care who owns them, or what's keeping them alive.  It does its
26
 * own addrefs/copies/tracing/etc.
27
 *
28
 * Furthermore, in some cases compile options are propagated from one entity to
29
 * another (e.g. from a script to a function defined in that script).  This
30
 * involves copying over some, but not all, of the options.
31
 *
32
 * So we have a class hierarchy that reflects these four use cases:
33
 *
34
 * - TransitiveCompileOptions is the common base class, representing options
35
 *   that should get propagated from a script to functions defined in that
36
 *   script.  This class is abstract and is only ever used as a subclass.
37
 *
38
 * - ReadOnlyCompileOptions is the only subclass of TransitiveCompileOptions,
39
 *   representing a full set of compile options.  It can be used by code that
40
 *   simply needs to access options set elsewhere, like the compiler.  This
41
 *   class too is abstract and is only ever used as a subclass.
42
 *
43
 * - The usual CompileOptions class must be stack-allocated, and holds
44
 *   non-owning references to the filename, element, and so on. It's derived
45
 *   from ReadOnlyCompileOptions, so the compiler can use it.
46
 *
47
 * - OwningCompileOptions roots / copies / reference counts of all its values,
48
 *   and unroots / frees / releases them when it is destructed. It too is
49
 *   derived from ReadOnlyCompileOptions, so the compiler accepts it.
50
 */
51
52
#ifndef js_CompileOptions_h
53
#define js_CompileOptions_h
54
55
#include "mozilla/Attributes.h" // MOZ_MUST_USE
56
#include "mozilla/MemoryReporting.h" // mozilla::MallocSizeOf
57
58
#include <stddef.h> // size_t
59
#include <stdint.h> // uint8_t
60
61
#include "jstypes.h" // JS_PUBLIC_API
62
63
#include "js/RootingAPI.h" // JS::PersistentRooted, JS::Rooted
64
65
struct JSContext;
66
class JSObject;
67
class JSScript;
68
class JSString;
69
70
namespace JS {
71
72
enum class AsmJSOption : uint8_t
73
{
74
    Enabled,
75
    Disabled,
76
    DisabledByDebugger,
77
};
78
79
/**
80
 * The common base class for the CompileOptions hierarchy.
81
 *
82
 * Use this in code that needs to propagate compile options from one
83
 * compilation unit to another.
84
 */
85
class JS_PUBLIC_API(TransitiveCompileOptions)
86
{
87
  protected:
88
    /**
89
     * The Web Platform allows scripts to be loaded from arbitrary cross-origin
90
     * sources. This allows an attack by which a malicious website loads a
91
     * sensitive file (say, a bank statement) cross-origin (using the user's
92
     * cookies), and sniffs the generated syntax errors (via a window.onerror
93
     * handler) for juicy morsels of its contents.
94
     *
95
     * To counter this attack, HTML5 specifies that script errors should be
96
     * sanitized ("muted") when the script is not same-origin with the global
97
     * for which it is loaded. Callers should set this flag for cross-origin
98
     * scripts, and it will be propagated appropriately to child scripts and
99
     * passed back in JSErrorReports.
100
     */
101
    bool mutedErrors_ = false;
102
103
    const char* filename_ = nullptr;
104
    const char* introducerFilename_ = nullptr;
105
    const char16_t* sourceMapURL_ = nullptr;
106
107
  public:
108
    // POD options.
109
    bool selfHostingMode = false;
110
    bool canLazilyParse = true;
111
    bool strictOption = false;
112
    bool extraWarningsOption = false;
113
    bool werrorOption = false;
114
    AsmJSOption asmJSOption = AsmJSOption::Disabled;
115
    bool throwOnAsmJSValidationFailureOption = false;
116
    bool forceAsync = false;
117
    bool sourceIsLazy = false;
118
    bool allowHTMLComments = true;
119
    bool isProbablySystemCode = false;
120
    bool hideScriptFromDebugger = false;
121
122
    /**
123
     * |introductionType| is a statically allocated C string: one of "eval",
124
     * "Function", or "GeneratorFunction".
125
     */
126
    const char* introductionType = nullptr;
127
128
    unsigned introductionLineno = 0;
129
    uint32_t introductionOffset = 0;
130
    bool hasIntroductionInfo = false;
131
132
  protected:
133
1.51k
    TransitiveCompileOptions() = default;
134
135
    // Set all POD options (those not requiring reference counts, copies,
136
    // rooting, or other hand-holding) to their values in |rhs|.
137
    void copyPODTransitiveOptions(const TransitiveCompileOptions& rhs);
138
139
  public:
140
    // Read-only accessors for non-POD options. The proper way to set these
141
    // depends on the derived type.
142
0
    bool mutedErrors() const { return mutedErrors_; }
143
1.48k
    const char* filename() const { return filename_; }
144
1.48k
    const char* introducerFilename() const { return introducerFilename_; }
145
1.48k
    const char16_t* sourceMapURL() const { return sourceMapURL_; }
146
    virtual JSObject* element() const = 0;
147
    virtual JSString* elementAttributeName() const = 0;
148
    virtual JSScript* introductionScript() const = 0;
149
150
  private:
151
    void operator=(const TransitiveCompileOptions&) = delete;
152
};
153
154
class JS_PUBLIC_API(CompileOptions);
155
156
/**
157
 * The class representing a full set of compile options.
158
 *
159
 * Use this in code that only needs to access compilation options created
160
 * elsewhere, like the compiler.  Don't instantiate this class (the constructor
161
 * is protected anyway); instead, create instances only of the derived classes:
162
 * CompileOptions and OwningCompileOptions.
163
 */
164
class JS_PUBLIC_API(ReadOnlyCompileOptions)
165
  : public TransitiveCompileOptions
166
{
167
  public:
168
    // POD options.
169
    unsigned lineno  = 1;
170
    unsigned column = 0;
171
172
    // The offset within the ScriptSource's full uncompressed text of the first
173
    // character we're presenting for compilation with this CompileOptions.
174
    //
175
    // When we compile a LazyScript, we pass the compiler only the substring of
176
    // the source the lazy function occupies. With chunked decompression, we
177
    // may not even have the complete uncompressed source present in memory. But
178
    // parse node positions are offsets within the ScriptSource's full text,
179
    // and LazyScripts indicate their substring of the full source by its
180
    // starting and ending offsets within the full text. This
181
    // scriptSourceOffset field lets the frontend convert between these
182
    // offsets and offsets within the substring presented for compilation.
183
    unsigned scriptSourceOffset = 0;
184
185
    // isRunOnce only applies to non-function scripts.
186
    bool isRunOnce =  false;
187
188
    bool nonSyntacticScope = false;
189
    bool noScriptRval = false;
190
    bool allowSyntaxParser = true;
191
192
193
  private:
194
    friend class CompileOptions;
195
196
  protected:
197
1.51k
    ReadOnlyCompileOptions() = default;
198
199
    // Set all POD options (those not requiring reference counts, copies,
200
    // rooting, or other hand-holding) to their values in |rhs|.
201
    void copyPODOptions(const ReadOnlyCompileOptions& rhs);
202
203
  public:
204
    // Read-only accessors for non-POD options. The proper way to set these
205
    // depends on the derived type.
206
26
    bool mutedErrors() const { return mutedErrors_; }
207
43
    const char* filename() const { return filename_; }
208
26
    const char* introducerFilename() const { return introducerFilename_; }
209
16
    const char16_t* sourceMapURL() const { return sourceMapURL_; }
210
    virtual JSObject* element() const override = 0;
211
    virtual JSString* elementAttributeName() const override = 0;
212
    virtual JSScript* introductionScript() const override = 0;
213
214
  private:
215
    void operator=(const ReadOnlyCompileOptions&) = delete;
216
};
217
218
/**
219
 * Compilation options, with dynamic lifetime. An instance of this type
220
 * makes a copy of / holds / roots all dynamically allocated resources
221
 * (principals; elements; strings) that it refers to. Its destructor frees
222
 * / drops / unroots them. This is heavier than CompileOptions, below, but
223
 * unlike CompileOptions, it can outlive any given stack frame.
224
 *
225
 * Note that this *roots* any JS values it refers to - they're live
226
 * unconditionally. Thus, instances of this type can't be owned, directly
227
 * or indirectly, by a JavaScript object: if any value that this roots ever
228
 * comes to refer to the object that owns this, then the whole cycle, and
229
 * anything else it entrains, will never be freed.
230
 */
231
class JS_PUBLIC_API(OwningCompileOptions) final
232
  : public ReadOnlyCompileOptions
233
{
234
    PersistentRooted<JSObject*> elementRoot;
235
    PersistentRooted<JSString*> elementAttributeNameRoot;
236
    PersistentRooted<JSScript*> introductionScriptRoot;
237
238
  public:
239
    // A minimal constructor, for use with OwningCompileOptions::copy.
240
    explicit OwningCompileOptions(JSContext* cx);
241
    ~OwningCompileOptions();
242
243
0
    JSObject* element() const override { return elementRoot; }
244
0
    JSString* elementAttributeName() const override { return elementAttributeNameRoot; }
245
0
    JSScript* introductionScript() const override { return introductionScriptRoot; }
246
247
    /** Set this to a copy of |rhs|.  Return false on OOM. */
248
    bool copy(JSContext* cx, const ReadOnlyCompileOptions& rhs);
249
250
    /* These setters make copies of their string arguments and are fallible. */
251
    MOZ_MUST_USE bool setFile(JSContext* cx, const char* f);
252
    MOZ_MUST_USE bool setFileAndLine(JSContext* cx, const char* f, unsigned l);
253
    MOZ_MUST_USE bool setSourceMapURL(JSContext* cx, const char16_t* s);
254
    MOZ_MUST_USE bool setIntroducerFilename(JSContext* cx, const char* s);
255
256
    /* These setters are infallible, and can be chained. */
257
258
0
    OwningCompileOptions& setLine(unsigned l) {
259
0
        lineno = l;
260
0
        return *this;
261
0
    }
262
263
0
    OwningCompileOptions& setElement(JSObject* e) {
264
0
        elementRoot = e;
265
0
        return *this;
266
0
    }
267
268
0
    OwningCompileOptions& setElementAttributeName(JSString* p) {
269
0
        elementAttributeNameRoot = p;
270
0
        return *this;
271
0
    }
272
273
0
    OwningCompileOptions& setIntroductionScript(JSScript* s) {
274
0
        introductionScriptRoot = s;
275
0
        return *this;
276
0
    }
277
278
0
    OwningCompileOptions& setMutedErrors(bool mute) {
279
0
        mutedErrors_ = mute;
280
0
        return *this;
281
0
    }
282
283
0
    OwningCompileOptions& setColumn(unsigned c) {
284
0
        column = c;
285
0
        return *this;
286
0
    }
287
288
0
    OwningCompileOptions& setScriptSourceOffset(unsigned o) {
289
0
        scriptSourceOffset = o;
290
0
        return *this;
291
0
    }
292
293
0
    OwningCompileOptions& setIsRunOnce(bool once) {
294
0
        isRunOnce = once;
295
0
        return *this;
296
0
    }
297
298
0
    OwningCompileOptions& setNoScriptRval(bool nsr) {
299
0
        noScriptRval = nsr;
300
0
        return *this;
301
0
    }
302
303
0
    OwningCompileOptions& setSelfHostingMode(bool shm) {
304
0
        selfHostingMode = shm;
305
0
        return *this;
306
0
    }
307
308
0
    OwningCompileOptions& setCanLazilyParse(bool clp) {
309
0
        canLazilyParse = clp;
310
0
        return *this;
311
0
    }
312
313
0
    OwningCompileOptions& setAllowSyntaxParser(bool clp) {
314
0
        allowSyntaxParser = clp;
315
0
        return *this;
316
0
    }
317
318
0
    OwningCompileOptions& setSourceIsLazy(bool l) {
319
0
        sourceIsLazy = l;
320
0
        return *this;
321
0
    }
322
323
0
    OwningCompileOptions& setNonSyntacticScope(bool n) {
324
0
        nonSyntacticScope = n;
325
0
        return *this;
326
0
    }
327
328
0
    OwningCompileOptions& setIntroductionType(const char* t) {
329
0
        introductionType = t;
330
0
        return *this;
331
0
    }
332
333
    bool setIntroductionInfo(JSContext* cx, const char* introducerFn,
334
                             const char* intro, unsigned line,
335
                             JSScript* script, uint32_t offset)
336
0
    {
337
0
        if (!setIntroducerFilename(cx, introducerFn)) {
338
0
            return false;
339
0
        }
340
0
341
0
        introductionType = intro;
342
0
        introductionLineno = line;
343
0
        introductionScriptRoot = script;
344
0
        introductionOffset = offset;
345
0
        hasIntroductionInfo = true;
346
0
        return true;
347
0
    }
348
349
    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
350
351
  private:
352
    void operator=(const CompileOptions& rhs) = delete;
353
};
354
355
/**
356
 * Compilation options stored on the stack. An instance of this type
357
 * simply holds references to dynamically allocated resources (element;
358
 * filename; source map URL) that are owned by something else. If you
359
 * create an instance of this type, it's up to you to guarantee that
360
 * everything you store in it will outlive it.
361
 */
362
class MOZ_STACK_CLASS JS_PUBLIC_API(CompileOptions) final
363
  : public ReadOnlyCompileOptions
364
{
365
  private:
366
    Rooted<JSObject*> elementRoot;
367
    Rooted<JSString*> elementAttributeNameRoot;
368
    Rooted<JSScript*> introductionScriptRoot;
369
370
  public:
371
    explicit CompileOptions(JSContext* cx);
372
373
    CompileOptions(JSContext* cx, const ReadOnlyCompileOptions& rhs)
374
      : ReadOnlyCompileOptions(),
375
        elementRoot(cx),
376
        elementAttributeNameRoot(cx),
377
        introductionScriptRoot(cx)
378
8
    {
379
8
        copyPODOptions(rhs);
380
8
381
8
        filename_ = rhs.filename();
382
8
        introducerFilename_ = rhs.introducerFilename();
383
8
        sourceMapURL_ = rhs.sourceMapURL();
384
8
        elementRoot = rhs.element();
385
8
        elementAttributeNameRoot = rhs.elementAttributeName();
386
8
        introductionScriptRoot = rhs.introductionScript();
387
8
    }
388
389
    CompileOptions(JSContext* cx, const TransitiveCompileOptions& rhs)
390
      : ReadOnlyCompileOptions(),
391
        elementRoot(cx),
392
        elementAttributeNameRoot(cx),
393
        introductionScriptRoot(cx)
394
1.48k
    {
395
1.48k
        copyPODTransitiveOptions(rhs);
396
1.48k
397
1.48k
        filename_ = rhs.filename();
398
1.48k
        introducerFilename_ = rhs.introducerFilename();
399
1.48k
        sourceMapURL_ = rhs.sourceMapURL();
400
1.48k
        elementRoot = rhs.element();
401
1.48k
        elementAttributeNameRoot = rhs.elementAttributeName();
402
1.48k
        introductionScriptRoot = rhs.introductionScript();
403
1.48k
    }
404
405
1.50k
    JSObject* element() const override {
406
1.50k
        return elementRoot;
407
1.50k
    }
408
409
1.50k
    JSString* elementAttributeName() const override {
410
1.50k
        return elementAttributeNameRoot;
411
1.50k
    }
412
413
1.50k
    JSScript* introductionScript() const override {
414
1.50k
        return introductionScriptRoot;
415
1.50k
    }
416
417
0
    CompileOptions& setFile(const char* f) {
418
0
        filename_ = f;
419
0
        return *this;
420
0
    }
421
422
0
    CompileOptions& setLine(unsigned l) {
423
0
        lineno = l;
424
0
        return *this;
425
0
    }
426
427
9
    CompileOptions& setFileAndLine(const char* f, unsigned l) {
428
9
        filename_ = f;
429
9
        lineno = l;
430
9
        return *this;
431
9
    }
432
433
0
    CompileOptions& setSourceMapURL(const char16_t* s) {
434
0
        sourceMapURL_ = s;
435
0
        return *this;
436
0
    }
437
438
0
    CompileOptions& setElement(JSObject* e) {
439
0
        elementRoot = e;
440
0
        return *this;
441
0
    }
442
443
0
    CompileOptions& setElementAttributeName(JSString* p) {
444
0
        elementAttributeNameRoot = p;
445
0
        return *this;
446
0
    }
447
448
0
    CompileOptions& setIntroductionScript(JSScript* s) {
449
0
        introductionScriptRoot = s;
450
0
        return *this;
451
0
    }
452
453
6
    CompileOptions& setMutedErrors(bool mute) {
454
6
        mutedErrors_ = mute;
455
6
        return *this;
456
6
    }
457
458
0
    CompileOptions& setColumn(unsigned c) {
459
0
        column = c;
460
0
        return *this;
461
0
    }
462
463
0
    CompileOptions& setScriptSourceOffset(unsigned o) {
464
0
        scriptSourceOffset = o;
465
0
        return *this;
466
0
    }
467
468
3
    CompileOptions& setIsRunOnce(bool once) {
469
3
        isRunOnce = once;
470
3
        return *this;
471
3
    }
472
473
20
    CompileOptions& setNoScriptRval(bool nsr) {
474
20
        noScriptRval = nsr;
475
20
        return *this;
476
20
    }
477
478
10
    CompileOptions& setSelfHostingMode(bool shm) {
479
10
        selfHostingMode = shm;
480
10
        return *this;
481
10
    }
482
483
4
    CompileOptions& setCanLazilyParse(bool clp) {
484
4
        canLazilyParse = clp;
485
4
        return *this;
486
4
    }
487
488
0
    CompileOptions& setAllowSyntaxParser(bool clp) {
489
0
        allowSyntaxParser = clp;
490
0
        return *this;
491
0
    }
492
493
5
    CompileOptions& setSourceIsLazy(bool l) {
494
5
        sourceIsLazy = l;
495
5
        return *this;
496
5
    }
497
498
5
    CompileOptions& setNonSyntacticScope(bool n) {
499
5
        nonSyntacticScope = n;
500
5
        return *this;
501
5
    }
502
503
13
    CompileOptions& setIntroductionType(const char* t) {
504
13
        introductionType = t;
505
13
        return *this;
506
13
    }
507
508
    CompileOptions& setIntroductionInfo(const char* introducerFn,
509
                                        const char* intro, unsigned line,
510
                                        JSScript* script, uint32_t offset)
511
0
    {
512
0
        introducerFilename_ = introducerFn;
513
0
        introductionType = intro;
514
0
        introductionLineno = line;
515
0
        introductionScriptRoot = script;
516
0
        introductionOffset = offset;
517
0
        hasIntroductionInfo = true;
518
0
        return *this;
519
0
    }
520
521
5
    CompileOptions& maybeMakeStrictMode(bool strict) {
522
5
        strictOption = strictOption || strict;
523
5
        return *this;
524
5
    }
525
526
  private:
527
    void operator=(const CompileOptions& rhs) = delete;
528
};
529
530
} // namespace JS
531
532
#endif /* js_CompileOptions_h */