1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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 #include "js/Value.h"
65 
66 struct JS_PUBLIC_API JSContext;
67 class JS_PUBLIC_API JSObject;
68 class JS_PUBLIC_API JSScript;
69 class JS_PUBLIC_API JSString;
70 
71 namespace JS {
72 
73 enum class AsmJSOption : uint8_t {
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  protected:
87   /**
88    * The Web Platform allows scripts to be loaded from arbitrary cross-origin
89    * sources. This allows an attack by which a malicious website loads a
90    * sensitive file (say, a bank statement) cross-origin (using the user's
91    * cookies), and sniffs the generated syntax errors (via a window.onerror
92    * handler) for juicy morsels of its contents.
93    *
94    * To counter this attack, HTML5 specifies that script errors should be
95    * sanitized ("muted") when the script is not same-origin with the global
96    * for which it is loaded. Callers should set this flag for cross-origin
97    * scripts, and it will be propagated appropriately to child scripts and
98    * passed back in JSErrorReports.
99    */
100   bool mutedErrors_ = false;
101 
102   // Either the Realm configuration or specialized VM operating modes may
103   // disallow syntax-parse altogether. These conditions are checked in the
104   // CompileOptions constructor.
105   bool forceFullParse_ = false;
106 
107   // Either the Realm configuration or the compile request may force
108   // strict-mode.
109   bool forceStrictMode_ = false;
110 
111   // The context has specified that source pragmas should be parsed.
112   bool sourcePragmas_ = true;
113 
114   const char* filename_ = nullptr;
115   const char* introducerFilename_ = nullptr;
116   const char16_t* sourceMapURL_ = nullptr;
117 
118   // Flag used to bypass the filename validation callback.
119   // See also SetFilenameValidationCallback.
120   bool skipFilenameValidation_ = false;
121 
122  public:
123   // POD options.
124   bool selfHostingMode = false;
125   AsmJSOption asmJSOption = AsmJSOption::Disabled;
126   bool throwOnAsmJSValidationFailureOption = false;
127   bool forceAsync = false;
128   bool discardSource = false;
129   bool sourceIsLazy = false;
130   bool allowHTMLComments = true;
131   bool hideScriptFromDebugger = false;
132   bool nonSyntacticScope = false;
133 
134   /**
135    * |introductionType| is a statically allocated C string: one of "eval",
136    * "Function", or "GeneratorFunction".
137    */
138   const char* introductionType = nullptr;
139 
140   unsigned introductionLineno = 0;
141   uint32_t introductionOffset = 0;
142   bool hasIntroductionInfo = false;
143 
144   // Mask of operation kinds which should be instrumented.
145   uint32_t instrumentationKinds = 0;
146 
147  protected:
148   TransitiveCompileOptions() = default;
149 
150   // Set all POD options (those not requiring reference counts, copies,
151   // rooting, or other hand-holding) to their values in |rhs|.
152   void copyPODTransitiveOptions(const TransitiveCompileOptions& rhs);
153 
154  public:
155   // Read-only accessors for non-POD options. The proper way to set these
156   // depends on the derived type.
mutedErrors()157   bool mutedErrors() const { return mutedErrors_; }
forceFullParse()158   bool forceFullParse() const { return forceFullParse_; }
forceStrictMode()159   bool forceStrictMode() const { return forceStrictMode_; }
skipFilenameValidation()160   bool skipFilenameValidation() const { return skipFilenameValidation_; }
sourcePragmas()161   bool sourcePragmas() const { return sourcePragmas_; }
filename()162   const char* filename() const { return filename_; }
introducerFilename()163   const char* introducerFilename() const { return introducerFilename_; }
sourceMapURL()164   const char16_t* sourceMapURL() const { return sourceMapURL_; }
165   virtual Value privateValue() const = 0;
166   virtual JSString* elementAttributeName() const = 0;
167   virtual JSScript* introductionScript() const = 0;
168 
169   // For some compilations the spec requires the ScriptOrModule field of the
170   // resulting script to be set to the currently executing script. This can be
171   // achieved by setting this option with setScriptOrModule() below.
172   //
173   // Note that this field doesn't explicitly exist in our implementation;
174   // instead the ScriptSourceObject's private value is set to that associated
175   // with the specified script.
176   virtual JSScript* scriptOrModule() const = 0;
177 
178   TransitiveCompileOptions(const TransitiveCompileOptions&) = delete;
179   TransitiveCompileOptions& operator=(const TransitiveCompileOptions&) = delete;
180 };
181 
182 /**
183  * The class representing a full set of compile options.
184  *
185  * Use this in code that only needs to access compilation options created
186  * elsewhere, like the compiler.  Don't instantiate this class (the constructor
187  * is protected anyway); instead, create instances only of the derived classes:
188  * CompileOptions and OwningCompileOptions.
189  */
190 class JS_PUBLIC_API ReadOnlyCompileOptions : public TransitiveCompileOptions {
191  public:
192   // POD options.
193   unsigned lineno = 1;
194   unsigned column = 0;
195 
196   // The offset within the ScriptSource's full uncompressed text of the first
197   // character we're presenting for compilation with this CompileOptions.
198   //
199   // When we compile a lazy script, we pass the compiler only the substring of
200   // the source the lazy function occupies. With chunked decompression, we may
201   // not even have the complete uncompressed source present in memory. But parse
202   // node positions are offsets within the ScriptSource's full text, and
203   // BaseScript indicate their substring of the full source by its starting and
204   // ending offsets within the full text. This scriptSourceOffset field lets the
205   // frontend convert between these offsets and offsets within the substring
206   // presented for compilation.
207   unsigned scriptSourceOffset = 0;
208 
209   // These only apply to non-function scripts.
210   bool isRunOnce = false;
211   bool noScriptRval = false;
212 
213  protected:
214   ReadOnlyCompileOptions() = default;
215 
216   void copyPODNonTransitiveOptions(const ReadOnlyCompileOptions& rhs);
217 
218   ReadOnlyCompileOptions(const ReadOnlyCompileOptions&) = delete;
219   ReadOnlyCompileOptions& operator=(const ReadOnlyCompileOptions&) = delete;
220 };
221 
222 /**
223  * Compilation options, with dynamic lifetime. An instance of this type
224  * makes a copy of / holds / roots all dynamically allocated resources
225  * (principals; elements; strings) that it refers to. Its destructor frees
226  * / drops / unroots them. This is heavier than CompileOptions, below, but
227  * unlike CompileOptions, it can outlive any given stack frame.
228  *
229  * Note that this *roots* any JS values it refers to - they're live
230  * unconditionally. Thus, instances of this type can't be owned, directly
231  * or indirectly, by a JavaScript object: if any value that this roots ever
232  * comes to refer to the object that owns this, then the whole cycle, and
233  * anything else it entrains, will never be freed.
234  */
235 class JS_PUBLIC_API OwningCompileOptions final : public ReadOnlyCompileOptions {
236   PersistentRooted<JSString*> elementAttributeNameRoot;
237   PersistentRooted<JSScript*> introductionScriptRoot;
238   PersistentRooted<JSScript*> scriptOrModuleRoot;
239   PersistentRooted<Value> privateValueRoot;
240 
241  public:
242   // A minimal constructor, for use with OwningCompileOptions::copy.
243   explicit OwningCompileOptions(JSContext* cx);
244   ~OwningCompileOptions();
245 
privateValue()246   Value privateValue() const override { return privateValueRoot; }
elementAttributeName()247   JSString* elementAttributeName() const override {
248     return elementAttributeNameRoot;
249   }
introductionScript()250   JSScript* introductionScript() const override {
251     return introductionScriptRoot;
252   }
scriptOrModule()253   JSScript* scriptOrModule() const override { return scriptOrModuleRoot; }
254 
255   /** Set this to a copy of |rhs|.  Return false on OOM. */
256   bool copy(JSContext* cx, const ReadOnlyCompileOptions& rhs);
257 
258   size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
259 
260  private:
261   void release();
262 
263   OwningCompileOptions(const OwningCompileOptions&) = delete;
264   OwningCompileOptions& operator=(const OwningCompileOptions&) = delete;
265 };
266 
267 /**
268  * Compilation options stored on the stack. An instance of this type
269  * simply holds references to dynamically allocated resources (element;
270  * filename; source map URL) that are owned by something else. If you
271  * create an instance of this type, it's up to you to guarantee that
272  * everything you store in it will outlive it.
273  */
274 class MOZ_STACK_CLASS JS_PUBLIC_API CompileOptions final
275     : public ReadOnlyCompileOptions {
276  private:
277   Rooted<JSString*> elementAttributeNameRoot;
278   Rooted<JSScript*> introductionScriptRoot;
279   Rooted<JSScript*> scriptOrModuleRoot;
280   Rooted<Value> privateValueRoot;
281 
282  public:
283   // Default options determined using the JSContext.
284   explicit CompileOptions(JSContext* cx);
285 
286   // Copy both the transitive and the non-transitive options from another
287   // options object.
CompileOptions(JSContext * cx,const ReadOnlyCompileOptions & rhs)288   CompileOptions(JSContext* cx, const ReadOnlyCompileOptions& rhs)
289       : ReadOnlyCompileOptions(),
290         elementAttributeNameRoot(cx),
291         introductionScriptRoot(cx),
292         scriptOrModuleRoot(cx),
293         privateValueRoot(cx) {
294     copyPODNonTransitiveOptions(rhs);
295     copyPODTransitiveOptions(rhs);
296 
297     filename_ = rhs.filename();
298     introducerFilename_ = rhs.introducerFilename();
299     sourceMapURL_ = rhs.sourceMapURL();
300     privateValueRoot = rhs.privateValue();
301     elementAttributeNameRoot = rhs.elementAttributeName();
302     introductionScriptRoot = rhs.introductionScript();
303     scriptOrModuleRoot = rhs.scriptOrModule();
304   }
305 
privateValue()306   Value privateValue() const override { return privateValueRoot; }
307 
elementAttributeName()308   JSString* elementAttributeName() const override {
309     return elementAttributeNameRoot;
310   }
311 
introductionScript()312   JSScript* introductionScript() const override {
313     return introductionScriptRoot;
314   }
315 
scriptOrModule()316   JSScript* scriptOrModule() const override { return scriptOrModuleRoot; }
317 
setFile(const char * f)318   CompileOptions& setFile(const char* f) {
319     filename_ = f;
320     return *this;
321   }
322 
setLine(unsigned l)323   CompileOptions& setLine(unsigned l) {
324     lineno = l;
325     return *this;
326   }
327 
setFileAndLine(const char * f,unsigned l)328   CompileOptions& setFileAndLine(const char* f, unsigned l) {
329     filename_ = f;
330     lineno = l;
331     return *this;
332   }
333 
setSourceMapURL(const char16_t * s)334   CompileOptions& setSourceMapURL(const char16_t* s) {
335     sourceMapURL_ = s;
336     return *this;
337   }
338 
setPrivateValue(const Value & v)339   CompileOptions& setPrivateValue(const Value& v) {
340     privateValueRoot = v;
341     return *this;
342   }
343 
setElementAttributeName(JSString * p)344   CompileOptions& setElementAttributeName(JSString* p) {
345     elementAttributeNameRoot = p;
346     return *this;
347   }
348 
setScriptOrModule(JSScript * s)349   CompileOptions& setScriptOrModule(JSScript* s) {
350     scriptOrModuleRoot = s;
351     return *this;
352   }
353 
setMutedErrors(bool mute)354   CompileOptions& setMutedErrors(bool mute) {
355     mutedErrors_ = mute;
356     return *this;
357   }
358 
setColumn(unsigned c)359   CompileOptions& setColumn(unsigned c) {
360     column = c;
361     return *this;
362   }
363 
setScriptSourceOffset(unsigned o)364   CompileOptions& setScriptSourceOffset(unsigned o) {
365     scriptSourceOffset = o;
366     return *this;
367   }
368 
setIsRunOnce(bool once)369   CompileOptions& setIsRunOnce(bool once) {
370     isRunOnce = once;
371     return *this;
372   }
373 
setNoScriptRval(bool nsr)374   CompileOptions& setNoScriptRval(bool nsr) {
375     noScriptRval = nsr;
376     return *this;
377   }
378 
setSkipFilenameValidation(bool b)379   CompileOptions& setSkipFilenameValidation(bool b) {
380     skipFilenameValidation_ = b;
381     return *this;
382   }
383 
setSelfHostingMode(bool shm)384   CompileOptions& setSelfHostingMode(bool shm) {
385     selfHostingMode = shm;
386     return *this;
387   }
388 
setSourceIsLazy(bool l)389   CompileOptions& setSourceIsLazy(bool l) {
390     sourceIsLazy = l;
391     return *this;
392   }
393 
setNonSyntacticScope(bool n)394   CompileOptions& setNonSyntacticScope(bool n) {
395     nonSyntacticScope = n;
396     return *this;
397   }
398 
setIntroductionType(const char * t)399   CompileOptions& setIntroductionType(const char* t) {
400     introductionType = t;
401     return *this;
402   }
403 
setIntroductionInfo(const char * introducerFn,const char * intro,unsigned line,JSScript * script,uint32_t offset)404   CompileOptions& setIntroductionInfo(const char* introducerFn,
405                                       const char* intro, unsigned line,
406                                       JSScript* script, uint32_t offset) {
407     introducerFilename_ = introducerFn;
408     introductionType = intro;
409     introductionLineno = line;
410     introductionScriptRoot = script;
411     introductionOffset = offset;
412     hasIntroductionInfo = true;
413     return *this;
414   }
415 
416   // Set introduction information according to any currently executing script.
417   CompileOptions& setIntroductionInfoToCaller(JSContext* cx,
418                                               const char* introductionType);
419 
setForceFullParse()420   CompileOptions& setForceFullParse() {
421     forceFullParse_ = true;
422     return *this;
423   }
424 
setForceStrictMode()425   CompileOptions& setForceStrictMode() {
426     forceStrictMode_ = true;
427     return *this;
428   }
429 
430   CompileOptions(const CompileOptions& rhs) = delete;
431   CompileOptions& operator=(const CompileOptions& rhs) = delete;
432 };
433 
434 }  // namespace JS
435 
436 #endif /* js_CompileOptions_h */
437