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