Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * vim: set ts=8 sts=4 et sw=4 tw=99: |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | /* JS script descriptor. */ |
michael@0 | 8 | |
michael@0 | 9 | #ifndef jsscript_h |
michael@0 | 10 | #define jsscript_h |
michael@0 | 11 | |
michael@0 | 12 | #include "mozilla/MemoryReporting.h" |
michael@0 | 13 | #include "mozilla/PodOperations.h" |
michael@0 | 14 | |
michael@0 | 15 | #include "jsatom.h" |
michael@0 | 16 | #ifdef JS_THREADSAFE |
michael@0 | 17 | #include "jslock.h" |
michael@0 | 18 | #endif |
michael@0 | 19 | #include "jsobj.h" |
michael@0 | 20 | #include "jsopcode.h" |
michael@0 | 21 | #include "jstypes.h" |
michael@0 | 22 | |
michael@0 | 23 | #include "gc/Barrier.h" |
michael@0 | 24 | #include "gc/Rooting.h" |
michael@0 | 25 | #include "jit/IonCode.h" |
michael@0 | 26 | #include "vm/Shape.h" |
michael@0 | 27 | |
michael@0 | 28 | namespace JS { |
michael@0 | 29 | struct ScriptSourceInfo; |
michael@0 | 30 | } |
michael@0 | 31 | |
michael@0 | 32 | namespace js { |
michael@0 | 33 | |
michael@0 | 34 | namespace jit { |
michael@0 | 35 | struct BaselineScript; |
michael@0 | 36 | struct IonScriptCounts; |
michael@0 | 37 | } |
michael@0 | 38 | |
michael@0 | 39 | # define ION_DISABLED_SCRIPT ((js::jit::IonScript *)0x1) |
michael@0 | 40 | # define ION_COMPILING_SCRIPT ((js::jit::IonScript *)0x2) |
michael@0 | 41 | |
michael@0 | 42 | # define BASELINE_DISABLED_SCRIPT ((js::jit::BaselineScript *)0x1) |
michael@0 | 43 | |
michael@0 | 44 | class BreakpointSite; |
michael@0 | 45 | class BindingIter; |
michael@0 | 46 | class LazyScript; |
michael@0 | 47 | class RegExpObject; |
michael@0 | 48 | struct SourceCompressionTask; |
michael@0 | 49 | class Shape; |
michael@0 | 50 | class WatchpointMap; |
michael@0 | 51 | class NestedScopeObject; |
michael@0 | 52 | |
michael@0 | 53 | namespace frontend { |
michael@0 | 54 | class BytecodeEmitter; |
michael@0 | 55 | } |
michael@0 | 56 | |
michael@0 | 57 | } |
michael@0 | 58 | |
michael@0 | 59 | /* |
michael@0 | 60 | * Type of try note associated with each catch or finally block, and also with |
michael@0 | 61 | * for-in and other kinds of loops. Non-for-in loops do not need these notes |
michael@0 | 62 | * for exception unwinding, but storing their boundaries here is helpful for |
michael@0 | 63 | * heuristics that need to know whether a given op is inside a loop. |
michael@0 | 64 | */ |
michael@0 | 65 | typedef enum JSTryNoteKind { |
michael@0 | 66 | JSTRY_CATCH, |
michael@0 | 67 | JSTRY_FINALLY, |
michael@0 | 68 | JSTRY_ITER, |
michael@0 | 69 | JSTRY_LOOP |
michael@0 | 70 | } JSTryNoteKind; |
michael@0 | 71 | |
michael@0 | 72 | /* |
michael@0 | 73 | * Exception handling record. |
michael@0 | 74 | */ |
michael@0 | 75 | struct JSTryNote { |
michael@0 | 76 | uint8_t kind; /* one of JSTryNoteKind */ |
michael@0 | 77 | uint32_t stackDepth; /* stack depth upon exception handler entry */ |
michael@0 | 78 | uint32_t start; /* start of the try statement or loop |
michael@0 | 79 | relative to script->main */ |
michael@0 | 80 | uint32_t length; /* length of the try statement or loop */ |
michael@0 | 81 | }; |
michael@0 | 82 | |
michael@0 | 83 | namespace js { |
michael@0 | 84 | |
michael@0 | 85 | // A block scope has a range in bytecode: it is entered at some offset, and left |
michael@0 | 86 | // at some later offset. Scopes can be nested. Given an offset, the |
michael@0 | 87 | // BlockScopeNote containing that offset whose with the highest start value |
michael@0 | 88 | // indicates the block scope. The block scope list is sorted by increasing |
michael@0 | 89 | // start value. |
michael@0 | 90 | // |
michael@0 | 91 | // It is possible to leave a scope nonlocally, for example via a "break" |
michael@0 | 92 | // statement, so there may be short bytecode ranges in a block scope in which we |
michael@0 | 93 | // are popping the block chain in preparation for a goto. These exits are also |
michael@0 | 94 | // nested with respect to outer scopes. The scopes in these exits are indicated |
michael@0 | 95 | // by the "index" field, just like any other block. If a nonlocal exit pops the |
michael@0 | 96 | // last block scope, the index will be NoBlockScopeIndex. |
michael@0 | 97 | // |
michael@0 | 98 | struct BlockScopeNote { |
michael@0 | 99 | static const uint32_t NoBlockScopeIndex = UINT32_MAX; |
michael@0 | 100 | |
michael@0 | 101 | uint32_t index; // Index of NestedScopeObject in the object |
michael@0 | 102 | // array, or NoBlockScopeIndex if there is no |
michael@0 | 103 | // block scope in this range. |
michael@0 | 104 | uint32_t start; // Bytecode offset at which this scope starts, |
michael@0 | 105 | // from script->main(). |
michael@0 | 106 | uint32_t length; // Bytecode length of scope. |
michael@0 | 107 | uint32_t parent; // Index of parent block scope in notes, or UINT32_MAX. |
michael@0 | 108 | }; |
michael@0 | 109 | |
michael@0 | 110 | struct ConstArray { |
michael@0 | 111 | js::HeapValue *vector; /* array of indexed constant values */ |
michael@0 | 112 | uint32_t length; |
michael@0 | 113 | }; |
michael@0 | 114 | |
michael@0 | 115 | struct ObjectArray { |
michael@0 | 116 | js::HeapPtrObject *vector; // Array of indexed objects. |
michael@0 | 117 | uint32_t length; // Count of indexed objects. |
michael@0 | 118 | }; |
michael@0 | 119 | |
michael@0 | 120 | struct TryNoteArray { |
michael@0 | 121 | JSTryNote *vector; // Array of indexed try notes. |
michael@0 | 122 | uint32_t length; // Count of indexed try notes. |
michael@0 | 123 | }; |
michael@0 | 124 | |
michael@0 | 125 | struct BlockScopeArray { |
michael@0 | 126 | BlockScopeNote *vector; // Array of indexed BlockScopeNote records. |
michael@0 | 127 | uint32_t length; // Count of indexed try notes. |
michael@0 | 128 | }; |
michael@0 | 129 | |
michael@0 | 130 | class Binding |
michael@0 | 131 | { |
michael@0 | 132 | // One JSScript stores one Binding per formal/variable so we use a |
michael@0 | 133 | // packed-word representation. |
michael@0 | 134 | uintptr_t bits_; |
michael@0 | 135 | |
michael@0 | 136 | static const uintptr_t KIND_MASK = 0x3; |
michael@0 | 137 | static const uintptr_t ALIASED_BIT = 0x4; |
michael@0 | 138 | static const uintptr_t NAME_MASK = ~(KIND_MASK | ALIASED_BIT); |
michael@0 | 139 | |
michael@0 | 140 | public: |
michael@0 | 141 | // A "binding" is a formal, 'var', or 'const' declaration. A function's |
michael@0 | 142 | // lexical scope is composed of these three kinds of bindings. |
michael@0 | 143 | enum Kind { ARGUMENT, VARIABLE, CONSTANT }; |
michael@0 | 144 | |
michael@0 | 145 | explicit Binding() : bits_(0) {} |
michael@0 | 146 | |
michael@0 | 147 | Binding(PropertyName *name, Kind kind, bool aliased) { |
michael@0 | 148 | JS_STATIC_ASSERT(CONSTANT <= KIND_MASK); |
michael@0 | 149 | JS_ASSERT((uintptr_t(name) & ~NAME_MASK) == 0); |
michael@0 | 150 | JS_ASSERT((uintptr_t(kind) & ~KIND_MASK) == 0); |
michael@0 | 151 | bits_ = uintptr_t(name) | uintptr_t(kind) | (aliased ? ALIASED_BIT : 0); |
michael@0 | 152 | } |
michael@0 | 153 | |
michael@0 | 154 | PropertyName *name() const { |
michael@0 | 155 | return (PropertyName *)(bits_ & NAME_MASK); |
michael@0 | 156 | } |
michael@0 | 157 | |
michael@0 | 158 | Kind kind() const { |
michael@0 | 159 | return Kind(bits_ & KIND_MASK); |
michael@0 | 160 | } |
michael@0 | 161 | |
michael@0 | 162 | bool aliased() const { |
michael@0 | 163 | return bool(bits_ & ALIASED_BIT); |
michael@0 | 164 | } |
michael@0 | 165 | }; |
michael@0 | 166 | |
michael@0 | 167 | JS_STATIC_ASSERT(sizeof(Binding) == sizeof(uintptr_t)); |
michael@0 | 168 | |
michael@0 | 169 | class Bindings; |
michael@0 | 170 | typedef InternalHandle<Bindings *> InternalBindingsHandle; |
michael@0 | 171 | |
michael@0 | 172 | /* |
michael@0 | 173 | * Formal parameters and local variables are stored in a shape tree |
michael@0 | 174 | * path encapsulated within this class. This class represents bindings for |
michael@0 | 175 | * both function and top-level scripts (the latter is needed to track names in |
michael@0 | 176 | * strict mode eval code, to give such code its own lexical environment). |
michael@0 | 177 | */ |
michael@0 | 178 | class Bindings |
michael@0 | 179 | { |
michael@0 | 180 | friend class BindingIter; |
michael@0 | 181 | friend class AliasedFormalIter; |
michael@0 | 182 | |
michael@0 | 183 | HeapPtr<Shape> callObjShape_; |
michael@0 | 184 | uintptr_t bindingArrayAndFlag_; |
michael@0 | 185 | uint16_t numArgs_; |
michael@0 | 186 | uint16_t numBlockScoped_; |
michael@0 | 187 | uint32_t numVars_; |
michael@0 | 188 | |
michael@0 | 189 | /* |
michael@0 | 190 | * During parsing, bindings are allocated out of a temporary LifoAlloc. |
michael@0 | 191 | * After parsing, a JSScript object is created and the bindings are |
michael@0 | 192 | * permanently transferred to it. On error paths, the JSScript object may |
michael@0 | 193 | * end up with bindings that still point to the (new released) LifoAlloc |
michael@0 | 194 | * memory. To avoid tracing these bindings during GC, we keep track of |
michael@0 | 195 | * whether the bindings are temporary or permanent in the low bit of |
michael@0 | 196 | * bindingArrayAndFlag_. |
michael@0 | 197 | */ |
michael@0 | 198 | static const uintptr_t TEMPORARY_STORAGE_BIT = 0x1; |
michael@0 | 199 | bool bindingArrayUsingTemporaryStorage() const { |
michael@0 | 200 | return bindingArrayAndFlag_ & TEMPORARY_STORAGE_BIT; |
michael@0 | 201 | } |
michael@0 | 202 | |
michael@0 | 203 | public: |
michael@0 | 204 | |
michael@0 | 205 | Binding *bindingArray() const { |
michael@0 | 206 | return reinterpret_cast<Binding *>(bindingArrayAndFlag_ & ~TEMPORARY_STORAGE_BIT); |
michael@0 | 207 | } |
michael@0 | 208 | |
michael@0 | 209 | inline Bindings(); |
michael@0 | 210 | |
michael@0 | 211 | /* |
michael@0 | 212 | * Initialize a Bindings with a pointer into temporary storage. |
michael@0 | 213 | * bindingArray must have length numArgs+numVars. Before the temporary |
michael@0 | 214 | * storage is release, switchToScriptStorage must be called, providing a |
michael@0 | 215 | * pointer into the Binding array stored in script->data. |
michael@0 | 216 | */ |
michael@0 | 217 | static bool initWithTemporaryStorage(ExclusiveContext *cx, InternalBindingsHandle self, |
michael@0 | 218 | unsigned numArgs, uint32_t numVars, |
michael@0 | 219 | Binding *bindingArray, unsigned numBlockScoped); |
michael@0 | 220 | |
michael@0 | 221 | // CompileScript parses and compiles one statement at a time, but the result |
michael@0 | 222 | // is one Script object. There will be no vars or bindings, because those |
michael@0 | 223 | // go on the global, but there may be block-scoped locals, and the number of |
michael@0 | 224 | // block-scoped locals may increase as we parse more expressions. This |
michael@0 | 225 | // helper updates the number of block scoped variables in a script as it is |
michael@0 | 226 | // being parsed. |
michael@0 | 227 | void updateNumBlockScoped(unsigned numBlockScoped) { |
michael@0 | 228 | JS_ASSERT(!callObjShape_); |
michael@0 | 229 | JS_ASSERT(numVars_ == 0); |
michael@0 | 230 | JS_ASSERT(numBlockScoped < LOCALNO_LIMIT); |
michael@0 | 231 | JS_ASSERT(numBlockScoped >= numBlockScoped_); |
michael@0 | 232 | numBlockScoped_ = numBlockScoped; |
michael@0 | 233 | } |
michael@0 | 234 | |
michael@0 | 235 | uint8_t *switchToScriptStorage(Binding *newStorage); |
michael@0 | 236 | |
michael@0 | 237 | /* |
michael@0 | 238 | * Clone srcScript's bindings (as part of js::CloneScript). dstScriptData |
michael@0 | 239 | * is the pointer to what will eventually be dstScript->data. |
michael@0 | 240 | */ |
michael@0 | 241 | static bool clone(JSContext *cx, InternalBindingsHandle self, uint8_t *dstScriptData, |
michael@0 | 242 | HandleScript srcScript); |
michael@0 | 243 | |
michael@0 | 244 | unsigned numArgs() const { return numArgs_; } |
michael@0 | 245 | uint32_t numVars() const { return numVars_; } |
michael@0 | 246 | unsigned numBlockScoped() const { return numBlockScoped_; } |
michael@0 | 247 | uint32_t numLocals() const { return numVars() + numBlockScoped(); } |
michael@0 | 248 | |
michael@0 | 249 | // Return the size of the bindingArray. |
michael@0 | 250 | uint32_t count() const { return numArgs() + numVars(); } |
michael@0 | 251 | |
michael@0 | 252 | /* Return the initial shape of call objects created for this scope. */ |
michael@0 | 253 | Shape *callObjShape() const { return callObjShape_; } |
michael@0 | 254 | |
michael@0 | 255 | /* Convenience method to get the var index of 'arguments'. */ |
michael@0 | 256 | static uint32_t argumentsVarIndex(ExclusiveContext *cx, InternalBindingsHandle); |
michael@0 | 257 | |
michael@0 | 258 | /* Return whether the binding at bindingIndex is aliased. */ |
michael@0 | 259 | bool bindingIsAliased(uint32_t bindingIndex); |
michael@0 | 260 | |
michael@0 | 261 | /* Return whether this scope has any aliased bindings. */ |
michael@0 | 262 | bool hasAnyAliasedBindings() const { |
michael@0 | 263 | if (!callObjShape_) |
michael@0 | 264 | return false; |
michael@0 | 265 | |
michael@0 | 266 | return !callObjShape_->isEmptyShape(); |
michael@0 | 267 | } |
michael@0 | 268 | |
michael@0 | 269 | void trace(JSTracer *trc); |
michael@0 | 270 | }; |
michael@0 | 271 | |
michael@0 | 272 | template <> |
michael@0 | 273 | struct GCMethods<Bindings> { |
michael@0 | 274 | static Bindings initial(); |
michael@0 | 275 | static ThingRootKind kind() { return THING_ROOT_BINDINGS; } |
michael@0 | 276 | static bool poisoned(const Bindings &bindings) { |
michael@0 | 277 | return IsPoisonedPtr(static_cast<Shape *>(bindings.callObjShape())); |
michael@0 | 278 | } |
michael@0 | 279 | }; |
michael@0 | 280 | |
michael@0 | 281 | class ScriptCounts |
michael@0 | 282 | { |
michael@0 | 283 | friend class ::JSScript; |
michael@0 | 284 | friend struct ScriptAndCounts; |
michael@0 | 285 | |
michael@0 | 286 | /* |
michael@0 | 287 | * This points to a single block that holds an array of PCCounts followed |
michael@0 | 288 | * by an array of doubles. Each element in the PCCounts array has a |
michael@0 | 289 | * pointer into the array of doubles. |
michael@0 | 290 | */ |
michael@0 | 291 | PCCounts *pcCountsVector; |
michael@0 | 292 | |
michael@0 | 293 | /* Information about any Ion compilations for the script. */ |
michael@0 | 294 | jit::IonScriptCounts *ionCounts; |
michael@0 | 295 | |
michael@0 | 296 | public: |
michael@0 | 297 | ScriptCounts() : pcCountsVector(nullptr), ionCounts(nullptr) { } |
michael@0 | 298 | |
michael@0 | 299 | inline void destroy(FreeOp *fop); |
michael@0 | 300 | |
michael@0 | 301 | void set(js::ScriptCounts counts) { |
michael@0 | 302 | pcCountsVector = counts.pcCountsVector; |
michael@0 | 303 | ionCounts = counts.ionCounts; |
michael@0 | 304 | } |
michael@0 | 305 | }; |
michael@0 | 306 | |
michael@0 | 307 | typedef HashMap<JSScript *, |
michael@0 | 308 | ScriptCounts, |
michael@0 | 309 | DefaultHasher<JSScript *>, |
michael@0 | 310 | SystemAllocPolicy> ScriptCountsMap; |
michael@0 | 311 | |
michael@0 | 312 | class DebugScript |
michael@0 | 313 | { |
michael@0 | 314 | friend class ::JSScript; |
michael@0 | 315 | |
michael@0 | 316 | /* |
michael@0 | 317 | * When non-zero, compile script in single-step mode. The top bit is set and |
michael@0 | 318 | * cleared by setStepMode, as used by JSD. The lower bits are a count, |
michael@0 | 319 | * adjusted by changeStepModeCount, used by the Debugger object. Only |
michael@0 | 320 | * when the bit is clear and the count is zero may we compile the script |
michael@0 | 321 | * without single-step support. |
michael@0 | 322 | */ |
michael@0 | 323 | uint32_t stepMode; |
michael@0 | 324 | |
michael@0 | 325 | /* Number of breakpoint sites at opcodes in the script. */ |
michael@0 | 326 | uint32_t numSites; |
michael@0 | 327 | |
michael@0 | 328 | /* |
michael@0 | 329 | * Array with all breakpoints installed at opcodes in the script, indexed |
michael@0 | 330 | * by the offset of the opcode into the script. |
michael@0 | 331 | */ |
michael@0 | 332 | BreakpointSite *breakpoints[1]; |
michael@0 | 333 | }; |
michael@0 | 334 | |
michael@0 | 335 | typedef HashMap<JSScript *, |
michael@0 | 336 | DebugScript *, |
michael@0 | 337 | DefaultHasher<JSScript *>, |
michael@0 | 338 | SystemAllocPolicy> DebugScriptMap; |
michael@0 | 339 | |
michael@0 | 340 | class ScriptSource; |
michael@0 | 341 | |
michael@0 | 342 | class SourceDataCache |
michael@0 | 343 | { |
michael@0 | 344 | typedef HashMap<ScriptSource *, |
michael@0 | 345 | const jschar *, |
michael@0 | 346 | DefaultHasher<ScriptSource *>, |
michael@0 | 347 | SystemAllocPolicy> Map; |
michael@0 | 348 | |
michael@0 | 349 | public: |
michael@0 | 350 | // Hold an entry in the source data cache and prevent it from being purged on GC. |
michael@0 | 351 | class AutoHoldEntry |
michael@0 | 352 | { |
michael@0 | 353 | SourceDataCache *cache_; |
michael@0 | 354 | ScriptSource *source_; |
michael@0 | 355 | const jschar *charsToFree_; |
michael@0 | 356 | public: |
michael@0 | 357 | explicit AutoHoldEntry(); |
michael@0 | 358 | ~AutoHoldEntry(); |
michael@0 | 359 | private: |
michael@0 | 360 | void holdEntry(SourceDataCache *cache, ScriptSource *source); |
michael@0 | 361 | void deferDelete(const jschar *chars); |
michael@0 | 362 | ScriptSource *source() const { return source_; } |
michael@0 | 363 | friend class SourceDataCache; |
michael@0 | 364 | }; |
michael@0 | 365 | |
michael@0 | 366 | private: |
michael@0 | 367 | Map *map_; |
michael@0 | 368 | AutoHoldEntry *holder_; |
michael@0 | 369 | |
michael@0 | 370 | public: |
michael@0 | 371 | SourceDataCache() : map_(nullptr), holder_(nullptr) {} |
michael@0 | 372 | |
michael@0 | 373 | const jschar *lookup(ScriptSource *ss, AutoHoldEntry &asp); |
michael@0 | 374 | bool put(ScriptSource *ss, const jschar *chars, AutoHoldEntry &asp); |
michael@0 | 375 | |
michael@0 | 376 | void purge(); |
michael@0 | 377 | |
michael@0 | 378 | size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf); |
michael@0 | 379 | |
michael@0 | 380 | private: |
michael@0 | 381 | void holdEntry(AutoHoldEntry &holder, ScriptSource *ss); |
michael@0 | 382 | void releaseEntry(AutoHoldEntry &holder); |
michael@0 | 383 | }; |
michael@0 | 384 | |
michael@0 | 385 | class ScriptSource |
michael@0 | 386 | { |
michael@0 | 387 | friend class SourceCompressionTask; |
michael@0 | 388 | |
michael@0 | 389 | // A note on concurrency: |
michael@0 | 390 | // |
michael@0 | 391 | // The source may be compressed by a worker thread during parsing. (See |
michael@0 | 392 | // SourceCompressionTask.) When compression is running in the background, |
michael@0 | 393 | // ready() returns false. The compression thread touches the |data| union |
michael@0 | 394 | // and |compressedLength_|. Therefore, it is not safe to read these members |
michael@0 | 395 | // unless ready() is true. With that said, users of the public ScriptSource |
michael@0 | 396 | // API should be fine. |
michael@0 | 397 | |
michael@0 | 398 | union { |
michael@0 | 399 | // Before setSourceCopy or setSource are successfully called, this union |
michael@0 | 400 | // has a nullptr pointer. When the script source is ready, |
michael@0 | 401 | // compressedLength_ != 0 implies compressed holds the compressed data; |
michael@0 | 402 | // otherwise, source holds the uncompressed source. There is a special |
michael@0 | 403 | // pointer |emptySource| for source code for length 0. |
michael@0 | 404 | // |
michael@0 | 405 | // The only function allowed to malloc, realloc, or free the pointers in |
michael@0 | 406 | // this union is adjustDataSize(). Don't do it elsewhere. |
michael@0 | 407 | jschar *source; |
michael@0 | 408 | unsigned char *compressed; |
michael@0 | 409 | } data; |
michael@0 | 410 | uint32_t refs; |
michael@0 | 411 | uint32_t length_; |
michael@0 | 412 | uint32_t compressedLength_; |
michael@0 | 413 | char *filename_; |
michael@0 | 414 | jschar *displayURL_; |
michael@0 | 415 | jschar *sourceMapURL_; |
michael@0 | 416 | JSPrincipals *originPrincipals_; |
michael@0 | 417 | |
michael@0 | 418 | // bytecode offset in caller script that generated this code. |
michael@0 | 419 | // This is present for eval-ed code, as well as "new Function(...)"-introduced |
michael@0 | 420 | // scripts. |
michael@0 | 421 | uint32_t introductionOffset_; |
michael@0 | 422 | |
michael@0 | 423 | // If this ScriptSource was generated by a code-introduction mechanism such as |eval| |
michael@0 | 424 | // or |new Function|, the debugger needs access to the "raw" filename of the top-level |
michael@0 | 425 | // script that contains the eval-ing code. To keep track of this, we must preserve |
michael@0 | 426 | // the original outermost filename (of the original introducer script), so that instead |
michael@0 | 427 | // of a filename of "foo.js line 30 > eval line 10 > Function", we can obtain the |
michael@0 | 428 | // original raw filename of "foo.js". |
michael@0 | 429 | char *introducerFilename_; |
michael@0 | 430 | |
michael@0 | 431 | // A string indicating how this source code was introduced into the system. |
michael@0 | 432 | // This accessor returns one of the following values: |
michael@0 | 433 | // "eval" for code passed to |eval|. |
michael@0 | 434 | // "Function" for code passed to the |Function| constructor. |
michael@0 | 435 | // "Worker" for code loaded by calling the Web worker constructor—the worker's main script. |
michael@0 | 436 | // "importScripts" for code by calling |importScripts| in a web worker. |
michael@0 | 437 | // "handler" for code assigned to DOM elements' event handler IDL attributes. |
michael@0 | 438 | // "scriptElement" for code belonging to <script> elements. |
michael@0 | 439 | // undefined if the implementation doesn't know how the code was introduced. |
michael@0 | 440 | // This is a constant, statically allocated C string, so does not need |
michael@0 | 441 | // memory management. |
michael@0 | 442 | const char *introductionType_; |
michael@0 | 443 | |
michael@0 | 444 | // True if we can call JSRuntime::sourceHook to load the source on |
michael@0 | 445 | // demand. If sourceRetrievable_ and hasSourceData() are false, it is not |
michael@0 | 446 | // possible to get source at all. |
michael@0 | 447 | bool sourceRetrievable_:1; |
michael@0 | 448 | bool argumentsNotIncluded_:1; |
michael@0 | 449 | bool ready_:1; |
michael@0 | 450 | bool hasIntroductionOffset_:1; |
michael@0 | 451 | |
michael@0 | 452 | public: |
michael@0 | 453 | explicit ScriptSource() |
michael@0 | 454 | : refs(0), |
michael@0 | 455 | length_(0), |
michael@0 | 456 | compressedLength_(0), |
michael@0 | 457 | filename_(nullptr), |
michael@0 | 458 | displayURL_(nullptr), |
michael@0 | 459 | sourceMapURL_(nullptr), |
michael@0 | 460 | originPrincipals_(nullptr), |
michael@0 | 461 | introductionOffset_(0), |
michael@0 | 462 | introducerFilename_(nullptr), |
michael@0 | 463 | introductionType_(nullptr), |
michael@0 | 464 | sourceRetrievable_(false), |
michael@0 | 465 | argumentsNotIncluded_(false), |
michael@0 | 466 | ready_(true), |
michael@0 | 467 | hasIntroductionOffset_(false) |
michael@0 | 468 | { |
michael@0 | 469 | data.source = nullptr; |
michael@0 | 470 | } |
michael@0 | 471 | void incref() { refs++; } |
michael@0 | 472 | void decref() { |
michael@0 | 473 | JS_ASSERT(refs != 0); |
michael@0 | 474 | if (--refs == 0) |
michael@0 | 475 | destroy(); |
michael@0 | 476 | } |
michael@0 | 477 | bool initFromOptions(ExclusiveContext *cx, const ReadOnlyCompileOptions &options); |
michael@0 | 478 | bool setSourceCopy(ExclusiveContext *cx, |
michael@0 | 479 | JS::SourceBufferHolder &srcBuf, |
michael@0 | 480 | bool argumentsNotIncluded, |
michael@0 | 481 | SourceCompressionTask *tok); |
michael@0 | 482 | void setSource(const jschar *src, size_t length); |
michael@0 | 483 | bool ready() const { return ready_; } |
michael@0 | 484 | void setSourceRetrievable() { sourceRetrievable_ = true; } |
michael@0 | 485 | bool sourceRetrievable() const { return sourceRetrievable_; } |
michael@0 | 486 | bool hasSourceData() const { return !ready() || !!data.source; } |
michael@0 | 487 | uint32_t length() const { |
michael@0 | 488 | JS_ASSERT(hasSourceData()); |
michael@0 | 489 | return length_; |
michael@0 | 490 | } |
michael@0 | 491 | bool argumentsNotIncluded() const { |
michael@0 | 492 | JS_ASSERT(hasSourceData()); |
michael@0 | 493 | return argumentsNotIncluded_; |
michael@0 | 494 | } |
michael@0 | 495 | const jschar *chars(JSContext *cx, SourceDataCache::AutoHoldEntry &asp); |
michael@0 | 496 | JSFlatString *substring(JSContext *cx, uint32_t start, uint32_t stop); |
michael@0 | 497 | void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, |
michael@0 | 498 | JS::ScriptSourceInfo *info) const; |
michael@0 | 499 | |
michael@0 | 500 | // XDR handling |
michael@0 | 501 | template <XDRMode mode> |
michael@0 | 502 | bool performXDR(XDRState<mode> *xdr); |
michael@0 | 503 | |
michael@0 | 504 | bool setFilename(ExclusiveContext *cx, const char *filename); |
michael@0 | 505 | const char *introducerFilename() const { |
michael@0 | 506 | return introducerFilename_; |
michael@0 | 507 | } |
michael@0 | 508 | bool hasIntroductionType() const { |
michael@0 | 509 | return introductionType_; |
michael@0 | 510 | } |
michael@0 | 511 | const char *introductionType() const { |
michael@0 | 512 | JS_ASSERT(hasIntroductionType()); |
michael@0 | 513 | return introductionType_; |
michael@0 | 514 | } |
michael@0 | 515 | const char *filename() const { |
michael@0 | 516 | return filename_; |
michael@0 | 517 | } |
michael@0 | 518 | |
michael@0 | 519 | // Display URLs |
michael@0 | 520 | bool setDisplayURL(ExclusiveContext *cx, const jschar *displayURL); |
michael@0 | 521 | const jschar *displayURL(); |
michael@0 | 522 | bool hasDisplayURL() const { return displayURL_ != nullptr; } |
michael@0 | 523 | |
michael@0 | 524 | // Source maps |
michael@0 | 525 | bool setSourceMapURL(ExclusiveContext *cx, const jschar *sourceMapURL); |
michael@0 | 526 | const jschar *sourceMapURL(); |
michael@0 | 527 | bool hasSourceMapURL() const { return sourceMapURL_ != nullptr; } |
michael@0 | 528 | |
michael@0 | 529 | JSPrincipals *originPrincipals() const { return originPrincipals_; } |
michael@0 | 530 | |
michael@0 | 531 | bool hasIntroductionOffset() const { return hasIntroductionOffset_; } |
michael@0 | 532 | uint32_t introductionOffset() const { |
michael@0 | 533 | JS_ASSERT(hasIntroductionOffset()); |
michael@0 | 534 | return introductionOffset_; |
michael@0 | 535 | } |
michael@0 | 536 | void setIntroductionOffset(uint32_t offset) { |
michael@0 | 537 | JS_ASSERT(!hasIntroductionOffset()); |
michael@0 | 538 | JS_ASSERT(offset <= (uint32_t)INT32_MAX); |
michael@0 | 539 | introductionOffset_ = offset; |
michael@0 | 540 | hasIntroductionOffset_ = true; |
michael@0 | 541 | } |
michael@0 | 542 | |
michael@0 | 543 | private: |
michael@0 | 544 | void destroy(); |
michael@0 | 545 | bool compressed() const { return compressedLength_ != 0; } |
michael@0 | 546 | size_t computedSizeOfData() const { |
michael@0 | 547 | return compressed() ? compressedLength_ : sizeof(jschar) * length_; |
michael@0 | 548 | } |
michael@0 | 549 | bool adjustDataSize(size_t nbytes); |
michael@0 | 550 | const jschar *getOffThreadCompressionChars(ExclusiveContext *cx); |
michael@0 | 551 | }; |
michael@0 | 552 | |
michael@0 | 553 | class ScriptSourceHolder |
michael@0 | 554 | { |
michael@0 | 555 | ScriptSource *ss; |
michael@0 | 556 | public: |
michael@0 | 557 | explicit ScriptSourceHolder(ScriptSource *ss) |
michael@0 | 558 | : ss(ss) |
michael@0 | 559 | { |
michael@0 | 560 | ss->incref(); |
michael@0 | 561 | } |
michael@0 | 562 | ~ScriptSourceHolder() |
michael@0 | 563 | { |
michael@0 | 564 | ss->decref(); |
michael@0 | 565 | } |
michael@0 | 566 | }; |
michael@0 | 567 | |
michael@0 | 568 | class ScriptSourceObject : public JSObject |
michael@0 | 569 | { |
michael@0 | 570 | public: |
michael@0 | 571 | static const Class class_; |
michael@0 | 572 | |
michael@0 | 573 | static void trace(JSTracer *trc, JSObject *obj); |
michael@0 | 574 | static void finalize(FreeOp *fop, JSObject *obj); |
michael@0 | 575 | static ScriptSourceObject *create(ExclusiveContext *cx, ScriptSource *source, |
michael@0 | 576 | const ReadOnlyCompileOptions &options); |
michael@0 | 577 | |
michael@0 | 578 | ScriptSource *source() { |
michael@0 | 579 | return static_cast<ScriptSource *>(getReservedSlot(SOURCE_SLOT).toPrivate()); |
michael@0 | 580 | } |
michael@0 | 581 | |
michael@0 | 582 | void setSource(ScriptSource *source); |
michael@0 | 583 | |
michael@0 | 584 | JSObject *element() const; |
michael@0 | 585 | void initElement(HandleObject element); |
michael@0 | 586 | const Value &elementAttributeName() const; |
michael@0 | 587 | |
michael@0 | 588 | JSScript *introductionScript() const { |
michael@0 | 589 | void *untyped = getReservedSlot(INTRODUCTION_SCRIPT_SLOT).toPrivate(); |
michael@0 | 590 | return static_cast<JSScript *>(untyped); |
michael@0 | 591 | } |
michael@0 | 592 | void initIntroductionScript(JSScript *script); |
michael@0 | 593 | |
michael@0 | 594 | private: |
michael@0 | 595 | static const uint32_t SOURCE_SLOT = 0; |
michael@0 | 596 | static const uint32_t ELEMENT_SLOT = 1; |
michael@0 | 597 | static const uint32_t ELEMENT_PROPERTY_SLOT = 2; |
michael@0 | 598 | static const uint32_t INTRODUCTION_SCRIPT_SLOT = 3; |
michael@0 | 599 | static const uint32_t RESERVED_SLOTS = 4; |
michael@0 | 600 | }; |
michael@0 | 601 | |
michael@0 | 602 | enum GeneratorKind { NotGenerator, LegacyGenerator, StarGenerator }; |
michael@0 | 603 | |
michael@0 | 604 | static inline unsigned |
michael@0 | 605 | GeneratorKindAsBits(GeneratorKind generatorKind) { |
michael@0 | 606 | return static_cast<unsigned>(generatorKind); |
michael@0 | 607 | } |
michael@0 | 608 | |
michael@0 | 609 | static inline GeneratorKind |
michael@0 | 610 | GeneratorKindFromBits(unsigned val) { |
michael@0 | 611 | JS_ASSERT(val <= StarGenerator); |
michael@0 | 612 | return static_cast<GeneratorKind>(val); |
michael@0 | 613 | } |
michael@0 | 614 | |
michael@0 | 615 | /* |
michael@0 | 616 | * NB: after a successful XDR_DECODE, XDRScript callers must do any required |
michael@0 | 617 | * subsequent set-up of owning function or script object and then call |
michael@0 | 618 | * js_CallNewScriptHook. |
michael@0 | 619 | */ |
michael@0 | 620 | template<XDRMode mode> |
michael@0 | 621 | bool |
michael@0 | 622 | XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript, |
michael@0 | 623 | HandleFunction fun, MutableHandleScript scriptp); |
michael@0 | 624 | |
michael@0 | 625 | JSScript * |
michael@0 | 626 | CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript script, |
michael@0 | 627 | NewObjectKind newKind = GenericObject); |
michael@0 | 628 | |
michael@0 | 629 | template<XDRMode mode> |
michael@0 | 630 | bool |
michael@0 | 631 | XDRLazyScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript, |
michael@0 | 632 | HandleFunction fun, MutableHandle<LazyScript *> lazy); |
michael@0 | 633 | |
michael@0 | 634 | /* |
michael@0 | 635 | * Code any constant value. |
michael@0 | 636 | */ |
michael@0 | 637 | template<XDRMode mode> |
michael@0 | 638 | bool |
michael@0 | 639 | XDRScriptConst(XDRState<mode> *xdr, MutableHandleValue vp); |
michael@0 | 640 | |
michael@0 | 641 | } /* namespace js */ |
michael@0 | 642 | |
michael@0 | 643 | class JSScript : public js::gc::BarrieredCell<JSScript> |
michael@0 | 644 | { |
michael@0 | 645 | static const uint32_t stepFlagMask = 0x80000000U; |
michael@0 | 646 | static const uint32_t stepCountMask = 0x7fffffffU; |
michael@0 | 647 | |
michael@0 | 648 | template <js::XDRMode mode> |
michael@0 | 649 | friend |
michael@0 | 650 | bool |
michael@0 | 651 | js::XDRScript(js::XDRState<mode> *xdr, js::HandleObject enclosingScope, js::HandleScript enclosingScript, |
michael@0 | 652 | js::HandleFunction fun, js::MutableHandleScript scriptp); |
michael@0 | 653 | |
michael@0 | 654 | friend JSScript * |
michael@0 | 655 | js::CloneScript(JSContext *cx, js::HandleObject enclosingScope, js::HandleFunction fun, js::HandleScript src, |
michael@0 | 656 | js::NewObjectKind newKind); |
michael@0 | 657 | |
michael@0 | 658 | public: |
michael@0 | 659 | // |
michael@0 | 660 | // We order fields according to their size in order to avoid wasting space |
michael@0 | 661 | // for alignment. |
michael@0 | 662 | // |
michael@0 | 663 | |
michael@0 | 664 | // Larger-than-word-sized fields. |
michael@0 | 665 | |
michael@0 | 666 | public: |
michael@0 | 667 | js::Bindings bindings; /* names of top-level variables in this script |
michael@0 | 668 | (and arguments if this is a function script) */ |
michael@0 | 669 | |
michael@0 | 670 | bool hasAnyAliasedBindings() const { |
michael@0 | 671 | return bindings.hasAnyAliasedBindings(); |
michael@0 | 672 | } |
michael@0 | 673 | |
michael@0 | 674 | js::Binding *bindingArray() const { |
michael@0 | 675 | return bindings.bindingArray(); |
michael@0 | 676 | } |
michael@0 | 677 | |
michael@0 | 678 | unsigned numArgs() const { |
michael@0 | 679 | return bindings.numArgs(); |
michael@0 | 680 | } |
michael@0 | 681 | |
michael@0 | 682 | js::Shape *callObjShape() const { |
michael@0 | 683 | return bindings.callObjShape(); |
michael@0 | 684 | } |
michael@0 | 685 | |
michael@0 | 686 | // Word-sized fields. |
michael@0 | 687 | |
michael@0 | 688 | private: |
michael@0 | 689 | jsbytecode *code_; /* bytecodes and their immediate operands */ |
michael@0 | 690 | public: |
michael@0 | 691 | uint8_t *data; /* pointer to variable-length data array (see |
michael@0 | 692 | comment above Create() for details) */ |
michael@0 | 693 | |
michael@0 | 694 | js::HeapPtrAtom *atoms; /* maps immediate index to literal struct */ |
michael@0 | 695 | |
michael@0 | 696 | JSCompartment *compartment_; |
michael@0 | 697 | |
michael@0 | 698 | /* Persistent type information retained across GCs. */ |
michael@0 | 699 | js::types::TypeScript *types; |
michael@0 | 700 | |
michael@0 | 701 | private: |
michael@0 | 702 | // This script's ScriptSourceObject, or a CCW thereof. |
michael@0 | 703 | // |
michael@0 | 704 | // (When we clone a JSScript into a new compartment, we don't clone its |
michael@0 | 705 | // source object. Instead, the clone refers to a wrapper.) |
michael@0 | 706 | js::HeapPtrObject sourceObject_; |
michael@0 | 707 | |
michael@0 | 708 | js::HeapPtrFunction function_; |
michael@0 | 709 | |
michael@0 | 710 | // For callsite clones, which cannot have enclosing scopes, the original |
michael@0 | 711 | // function; otherwise the enclosing scope |
michael@0 | 712 | js::HeapPtrObject enclosingScopeOrOriginalFunction_; |
michael@0 | 713 | |
michael@0 | 714 | /* Information attached by Baseline/Ion for sequential mode execution. */ |
michael@0 | 715 | js::jit::IonScript *ion; |
michael@0 | 716 | js::jit::BaselineScript *baseline; |
michael@0 | 717 | |
michael@0 | 718 | /* Information attached by Ion for parallel mode execution */ |
michael@0 | 719 | js::jit::IonScript *parallelIon; |
michael@0 | 720 | |
michael@0 | 721 | /* Information used to re-lazify a lazily-parsed interpreted function. */ |
michael@0 | 722 | js::LazyScript *lazyScript; |
michael@0 | 723 | |
michael@0 | 724 | /* |
michael@0 | 725 | * Pointer to either baseline->method()->raw() or ion->method()->raw(), or |
michael@0 | 726 | * nullptr if there's no Baseline or Ion script. |
michael@0 | 727 | */ |
michael@0 | 728 | uint8_t *baselineOrIonRaw; |
michael@0 | 729 | uint8_t *baselineOrIonSkipArgCheck; |
michael@0 | 730 | |
michael@0 | 731 | // 32-bit fields. |
michael@0 | 732 | |
michael@0 | 733 | uint32_t length_; /* length of code vector */ |
michael@0 | 734 | uint32_t dataSize_; /* size of the used part of the data array */ |
michael@0 | 735 | |
michael@0 | 736 | uint32_t lineno_; /* base line number of script */ |
michael@0 | 737 | uint32_t column_; /* base column of script, optionally set */ |
michael@0 | 738 | |
michael@0 | 739 | uint32_t mainOffset_;/* offset of main entry point from code, after |
michael@0 | 740 | predef'ing prolog */ |
michael@0 | 741 | |
michael@0 | 742 | uint32_t natoms_; /* length of atoms array */ |
michael@0 | 743 | uint32_t nslots_; /* vars plus maximum stack depth */ |
michael@0 | 744 | |
michael@0 | 745 | /* Range of characters in scriptSource which contains this script's source. */ |
michael@0 | 746 | uint32_t sourceStart_; |
michael@0 | 747 | uint32_t sourceEnd_; |
michael@0 | 748 | |
michael@0 | 749 | uint32_t useCount; /* Number of times the script has been called |
michael@0 | 750 | * or has had backedges taken. When running in |
michael@0 | 751 | * ion, also increased for any inlined scripts. |
michael@0 | 752 | * Reset if the script's JIT code is forcibly |
michael@0 | 753 | * discarded. */ |
michael@0 | 754 | |
michael@0 | 755 | #ifdef DEBUG |
michael@0 | 756 | // Unique identifier within the compartment for this script, used for |
michael@0 | 757 | // printing analysis information. |
michael@0 | 758 | uint32_t id_; |
michael@0 | 759 | uint32_t idpad; |
michael@0 | 760 | #endif |
michael@0 | 761 | |
michael@0 | 762 | // 16-bit fields. |
michael@0 | 763 | |
michael@0 | 764 | uint16_t version; /* JS version under which script was compiled */ |
michael@0 | 765 | |
michael@0 | 766 | uint16_t funLength_; /* ES6 function length */ |
michael@0 | 767 | |
michael@0 | 768 | uint16_t nTypeSets_; /* number of type sets used in this script for |
michael@0 | 769 | dynamic type monitoring */ |
michael@0 | 770 | |
michael@0 | 771 | uint16_t staticLevel_;/* static level for display maintenance */ |
michael@0 | 772 | |
michael@0 | 773 | // Bit fields. |
michael@0 | 774 | |
michael@0 | 775 | public: |
michael@0 | 776 | // The kinds of the optional arrays. |
michael@0 | 777 | enum ArrayKind { |
michael@0 | 778 | CONSTS, |
michael@0 | 779 | OBJECTS, |
michael@0 | 780 | REGEXPS, |
michael@0 | 781 | TRYNOTES, |
michael@0 | 782 | BLOCK_SCOPES, |
michael@0 | 783 | ARRAY_KIND_BITS |
michael@0 | 784 | }; |
michael@0 | 785 | |
michael@0 | 786 | private: |
michael@0 | 787 | // The bits in this field indicate the presence/non-presence of several |
michael@0 | 788 | // optional arrays in |data|. See the comments above Create() for details. |
michael@0 | 789 | uint8_t hasArrayBits:ARRAY_KIND_BITS; |
michael@0 | 790 | |
michael@0 | 791 | // The GeneratorKind of the script. |
michael@0 | 792 | uint8_t generatorKindBits_:2; |
michael@0 | 793 | |
michael@0 | 794 | // 1-bit fields. |
michael@0 | 795 | |
michael@0 | 796 | // No need for result value of last expression statement. |
michael@0 | 797 | bool noScriptRval_:1; |
michael@0 | 798 | |
michael@0 | 799 | // Can call getCallerFunction(). |
michael@0 | 800 | bool savedCallerFun_:1; |
michael@0 | 801 | |
michael@0 | 802 | // Code is in strict mode. |
michael@0 | 803 | bool strict_:1; |
michael@0 | 804 | |
michael@0 | 805 | // Code has "use strict"; explicitly. |
michael@0 | 806 | bool explicitUseStrict_:1; |
michael@0 | 807 | |
michael@0 | 808 | // See Parser::compileAndGo. |
michael@0 | 809 | bool compileAndGo_:1; |
michael@0 | 810 | |
michael@0 | 811 | // see Parser::selfHostingMode. |
michael@0 | 812 | bool selfHosted_:1; |
michael@0 | 813 | |
michael@0 | 814 | // See FunctionContextFlags. |
michael@0 | 815 | bool bindingsAccessedDynamically_:1; |
michael@0 | 816 | bool funHasExtensibleScope_:1; |
michael@0 | 817 | bool funNeedsDeclEnvObject_:1; |
michael@0 | 818 | |
michael@0 | 819 | // True if any formalIsAliased(i). |
michael@0 | 820 | bool funHasAnyAliasedFormal_:1; |
michael@0 | 821 | |
michael@0 | 822 | // Have warned about uses of undefined properties in this script. |
michael@0 | 823 | bool warnedAboutUndefinedProp_:1; |
michael@0 | 824 | |
michael@0 | 825 | // Script has singleton objects. |
michael@0 | 826 | bool hasSingletons_:1; |
michael@0 | 827 | |
michael@0 | 828 | // Script is a lambda to treat as running once. |
michael@0 | 829 | bool treatAsRunOnce_:1; |
michael@0 | 830 | |
michael@0 | 831 | // If treatAsRunOnce, whether script has executed. |
michael@0 | 832 | bool hasRunOnce_:1; |
michael@0 | 833 | |
michael@0 | 834 | // Script has been reused for a clone. |
michael@0 | 835 | bool hasBeenCloned_:1; |
michael@0 | 836 | |
michael@0 | 837 | // Script has been inlined at least once, and can't be relazified. |
michael@0 | 838 | bool hasBeenInlined_:1; |
michael@0 | 839 | |
michael@0 | 840 | // Script came from eval(), and is still active. |
michael@0 | 841 | bool isActiveEval_:1; |
michael@0 | 842 | |
michael@0 | 843 | // Script came from eval(), and is in eval cache. |
michael@0 | 844 | bool isCachedEval_:1; |
michael@0 | 845 | |
michael@0 | 846 | // Set for functions defined at the top level within an 'eval' script. |
michael@0 | 847 | bool directlyInsideEval_:1; |
michael@0 | 848 | |
michael@0 | 849 | // Both 'arguments' and f.apply() are used. This is likely to be a wrapper. |
michael@0 | 850 | bool usesArgumentsAndApply_:1; |
michael@0 | 851 | |
michael@0 | 852 | /* script is attempted to be cloned anew at each callsite. This is |
michael@0 | 853 | temporarily needed for ParallelArray selfhosted code until type |
michael@0 | 854 | information can be made context sensitive. See discussion in |
michael@0 | 855 | bug 826148. */ |
michael@0 | 856 | bool shouldCloneAtCallsite_:1; |
michael@0 | 857 | bool isCallsiteClone_:1; /* is a callsite clone; has a link to the original function */ |
michael@0 | 858 | bool shouldInline_:1; /* hint to inline when possible */ |
michael@0 | 859 | |
michael@0 | 860 | // IonMonkey compilation hints. |
michael@0 | 861 | bool failedBoundsCheck_:1; /* script has had hoisted bounds checks fail */ |
michael@0 | 862 | bool failedShapeGuard_:1; /* script has had hoisted shape guard fail */ |
michael@0 | 863 | bool hadFrequentBailouts_:1; |
michael@0 | 864 | bool uninlineable_:1; /* explicitly marked as uninlineable */ |
michael@0 | 865 | |
michael@0 | 866 | // Idempotent cache has triggered invalidation. |
michael@0 | 867 | bool invalidatedIdempotentCache_:1; |
michael@0 | 868 | |
michael@0 | 869 | // If the generator was created implicitly via a generator expression, |
michael@0 | 870 | // isGeneratorExp will be true. |
michael@0 | 871 | bool isGeneratorExp_:1; |
michael@0 | 872 | |
michael@0 | 873 | // Script has an entry in JSCompartment::scriptCountsMap. |
michael@0 | 874 | bool hasScriptCounts_:1; |
michael@0 | 875 | |
michael@0 | 876 | // Script has an entry in JSCompartment::debugScriptMap. |
michael@0 | 877 | bool hasDebugScript_:1; |
michael@0 | 878 | |
michael@0 | 879 | // Freeze constraints for stack type sets have been generated. |
michael@0 | 880 | bool hasFreezeConstraints_:1; |
michael@0 | 881 | |
michael@0 | 882 | /* See comments below. */ |
michael@0 | 883 | bool argsHasVarBinding_:1; |
michael@0 | 884 | bool needsArgsAnalysis_:1; |
michael@0 | 885 | bool needsArgsObj_:1; |
michael@0 | 886 | |
michael@0 | 887 | // |
michael@0 | 888 | // End of fields. Start methods. |
michael@0 | 889 | // |
michael@0 | 890 | |
michael@0 | 891 | public: |
michael@0 | 892 | static JSScript *Create(js::ExclusiveContext *cx, |
michael@0 | 893 | js::HandleObject enclosingScope, bool savedCallerFun, |
michael@0 | 894 | const JS::ReadOnlyCompileOptions &options, unsigned staticLevel, |
michael@0 | 895 | js::HandleObject sourceObject, uint32_t sourceStart, |
michael@0 | 896 | uint32_t sourceEnd); |
michael@0 | 897 | |
michael@0 | 898 | void initCompartment(js::ExclusiveContext *cx); |
michael@0 | 899 | |
michael@0 | 900 | // Three ways ways to initialize a JSScript. Callers of partiallyInit() |
michael@0 | 901 | // and fullyInitTrivial() are responsible for notifying the debugger after |
michael@0 | 902 | // successfully creating any kind (function or other) of new JSScript. |
michael@0 | 903 | // However, callers of fullyInitFromEmitter() do not need to do this. |
michael@0 | 904 | static bool partiallyInit(js::ExclusiveContext *cx, JS::Handle<JSScript*> script, |
michael@0 | 905 | uint32_t nconsts, uint32_t nobjects, uint32_t nregexps, |
michael@0 | 906 | uint32_t ntrynotes, uint32_t nblockscopes, |
michael@0 | 907 | uint32_t nTypeSets); |
michael@0 | 908 | static bool fullyInitFromEmitter(js::ExclusiveContext *cx, JS::Handle<JSScript*> script, |
michael@0 | 909 | js::frontend::BytecodeEmitter *bce); |
michael@0 | 910 | // Initialize a no-op script. |
michael@0 | 911 | static bool fullyInitTrivial(js::ExclusiveContext *cx, JS::Handle<JSScript*> script); |
michael@0 | 912 | |
michael@0 | 913 | inline JSPrincipals *principals(); |
michael@0 | 914 | |
michael@0 | 915 | JSCompartment *compartment() const { return compartment_; } |
michael@0 | 916 | |
michael@0 | 917 | void setVersion(JSVersion v) { version = v; } |
michael@0 | 918 | |
michael@0 | 919 | // Script bytecode is immutable after creation. |
michael@0 | 920 | jsbytecode *code() const { |
michael@0 | 921 | return code_; |
michael@0 | 922 | } |
michael@0 | 923 | size_t length() const { |
michael@0 | 924 | return length_; |
michael@0 | 925 | } |
michael@0 | 926 | |
michael@0 | 927 | void setCode(jsbytecode *code) { code_ = code; } |
michael@0 | 928 | void setLength(size_t length) { length_ = length; } |
michael@0 | 929 | |
michael@0 | 930 | jsbytecode *codeEnd() const { return code() + length(); } |
michael@0 | 931 | |
michael@0 | 932 | bool containsPC(const jsbytecode *pc) const { |
michael@0 | 933 | return pc >= code() && pc < codeEnd(); |
michael@0 | 934 | } |
michael@0 | 935 | |
michael@0 | 936 | size_t pcToOffset(const jsbytecode *pc) const { |
michael@0 | 937 | JS_ASSERT(containsPC(pc)); |
michael@0 | 938 | return size_t(pc - code()); |
michael@0 | 939 | } |
michael@0 | 940 | |
michael@0 | 941 | jsbytecode *offsetToPC(size_t offset) const { |
michael@0 | 942 | JS_ASSERT(offset < length()); |
michael@0 | 943 | return code() + offset; |
michael@0 | 944 | } |
michael@0 | 945 | |
michael@0 | 946 | size_t mainOffset() const { |
michael@0 | 947 | return mainOffset_; |
michael@0 | 948 | } |
michael@0 | 949 | |
michael@0 | 950 | size_t lineno() const { |
michael@0 | 951 | return lineno_; |
michael@0 | 952 | } |
michael@0 | 953 | |
michael@0 | 954 | size_t column() const { |
michael@0 | 955 | return column_; |
michael@0 | 956 | } |
michael@0 | 957 | |
michael@0 | 958 | void setColumn(size_t column) { column_ = column; } |
michael@0 | 959 | |
michael@0 | 960 | // The fixed part of a stack frame is comprised of vars (in function code) |
michael@0 | 961 | // and block-scoped locals (in all kinds of code). |
michael@0 | 962 | size_t nfixed() const { |
michael@0 | 963 | return function_ ? bindings.numLocals() : bindings.numBlockScoped(); |
michael@0 | 964 | } |
michael@0 | 965 | |
michael@0 | 966 | // Number of fixed slots reserved for vars. Only nonzero for function code. |
michael@0 | 967 | size_t nfixedvars() const { |
michael@0 | 968 | return function_ ? bindings.numVars() : 0; |
michael@0 | 969 | } |
michael@0 | 970 | |
michael@0 | 971 | size_t nslots() const { |
michael@0 | 972 | return nslots_; |
michael@0 | 973 | } |
michael@0 | 974 | |
michael@0 | 975 | size_t staticLevel() const { |
michael@0 | 976 | return staticLevel_; |
michael@0 | 977 | } |
michael@0 | 978 | |
michael@0 | 979 | size_t nTypeSets() const { |
michael@0 | 980 | return nTypeSets_; |
michael@0 | 981 | } |
michael@0 | 982 | |
michael@0 | 983 | size_t funLength() const { |
michael@0 | 984 | return funLength_; |
michael@0 | 985 | } |
michael@0 | 986 | |
michael@0 | 987 | size_t sourceStart() const { |
michael@0 | 988 | return sourceStart_; |
michael@0 | 989 | } |
michael@0 | 990 | |
michael@0 | 991 | size_t sourceEnd() const { |
michael@0 | 992 | return sourceEnd_; |
michael@0 | 993 | } |
michael@0 | 994 | |
michael@0 | 995 | bool noScriptRval() const { |
michael@0 | 996 | return noScriptRval_; |
michael@0 | 997 | } |
michael@0 | 998 | |
michael@0 | 999 | bool savedCallerFun() const { return savedCallerFun_; } |
michael@0 | 1000 | |
michael@0 | 1001 | bool strict() const { |
michael@0 | 1002 | return strict_; |
michael@0 | 1003 | } |
michael@0 | 1004 | |
michael@0 | 1005 | bool explicitUseStrict() const { return explicitUseStrict_; } |
michael@0 | 1006 | |
michael@0 | 1007 | bool compileAndGo() const { |
michael@0 | 1008 | return compileAndGo_; |
michael@0 | 1009 | } |
michael@0 | 1010 | |
michael@0 | 1011 | bool selfHosted() const { return selfHosted_; } |
michael@0 | 1012 | bool bindingsAccessedDynamically() const { return bindingsAccessedDynamically_; } |
michael@0 | 1013 | bool funHasExtensibleScope() const { |
michael@0 | 1014 | return funHasExtensibleScope_; |
michael@0 | 1015 | } |
michael@0 | 1016 | bool funNeedsDeclEnvObject() const { |
michael@0 | 1017 | return funNeedsDeclEnvObject_; |
michael@0 | 1018 | } |
michael@0 | 1019 | bool funHasAnyAliasedFormal() const { |
michael@0 | 1020 | return funHasAnyAliasedFormal_; |
michael@0 | 1021 | } |
michael@0 | 1022 | |
michael@0 | 1023 | bool hasSingletons() const { return hasSingletons_; } |
michael@0 | 1024 | bool treatAsRunOnce() const { |
michael@0 | 1025 | return treatAsRunOnce_; |
michael@0 | 1026 | } |
michael@0 | 1027 | bool hasRunOnce() const { return hasRunOnce_; } |
michael@0 | 1028 | bool hasBeenCloned() const { return hasBeenCloned_; } |
michael@0 | 1029 | bool hasBeenInlined() const { return hasBeenInlined_; } |
michael@0 | 1030 | |
michael@0 | 1031 | void setTreatAsRunOnce() { treatAsRunOnce_ = true; } |
michael@0 | 1032 | void setHasRunOnce() { hasRunOnce_ = true; } |
michael@0 | 1033 | void setHasBeenCloned() { hasBeenCloned_ = true; } |
michael@0 | 1034 | void setHasBeenInlined() { hasBeenInlined_ = true; } |
michael@0 | 1035 | |
michael@0 | 1036 | bool isActiveEval() const { return isActiveEval_; } |
michael@0 | 1037 | bool isCachedEval() const { return isCachedEval_; } |
michael@0 | 1038 | bool directlyInsideEval() const { return directlyInsideEval_; } |
michael@0 | 1039 | |
michael@0 | 1040 | void cacheForEval() { |
michael@0 | 1041 | JS_ASSERT(isActiveEval() && !isCachedEval()); |
michael@0 | 1042 | isActiveEval_ = false; |
michael@0 | 1043 | isCachedEval_ = true; |
michael@0 | 1044 | } |
michael@0 | 1045 | |
michael@0 | 1046 | void uncacheForEval() { |
michael@0 | 1047 | JS_ASSERT(isCachedEval() && !isActiveEval()); |
michael@0 | 1048 | isCachedEval_ = false; |
michael@0 | 1049 | isActiveEval_ = true; |
michael@0 | 1050 | } |
michael@0 | 1051 | |
michael@0 | 1052 | void setActiveEval() { isActiveEval_ = true; } |
michael@0 | 1053 | void setDirectlyInsideEval() { directlyInsideEval_ = true; } |
michael@0 | 1054 | |
michael@0 | 1055 | bool usesArgumentsAndApply() const { |
michael@0 | 1056 | return usesArgumentsAndApply_; |
michael@0 | 1057 | } |
michael@0 | 1058 | void setUsesArgumentsAndApply() { usesArgumentsAndApply_ = true; } |
michael@0 | 1059 | |
michael@0 | 1060 | bool shouldCloneAtCallsite() const { |
michael@0 | 1061 | return shouldCloneAtCallsite_; |
michael@0 | 1062 | } |
michael@0 | 1063 | bool shouldInline() const { |
michael@0 | 1064 | return shouldInline_; |
michael@0 | 1065 | } |
michael@0 | 1066 | |
michael@0 | 1067 | void setShouldCloneAtCallsite() { shouldCloneAtCallsite_ = true; } |
michael@0 | 1068 | void setShouldInline() { shouldInline_ = true; } |
michael@0 | 1069 | |
michael@0 | 1070 | bool isCallsiteClone() const { |
michael@0 | 1071 | return isCallsiteClone_; |
michael@0 | 1072 | } |
michael@0 | 1073 | bool isGeneratorExp() const { return isGeneratorExp_; } |
michael@0 | 1074 | |
michael@0 | 1075 | bool failedBoundsCheck() const { |
michael@0 | 1076 | return failedBoundsCheck_; |
michael@0 | 1077 | } |
michael@0 | 1078 | bool failedShapeGuard() const { |
michael@0 | 1079 | return failedShapeGuard_; |
michael@0 | 1080 | } |
michael@0 | 1081 | bool hadFrequentBailouts() const { |
michael@0 | 1082 | return hadFrequentBailouts_; |
michael@0 | 1083 | } |
michael@0 | 1084 | bool uninlineable() const { |
michael@0 | 1085 | return uninlineable_; |
michael@0 | 1086 | } |
michael@0 | 1087 | bool invalidatedIdempotentCache() const { |
michael@0 | 1088 | return invalidatedIdempotentCache_; |
michael@0 | 1089 | } |
michael@0 | 1090 | |
michael@0 | 1091 | void setFailedBoundsCheck() { failedBoundsCheck_ = true; } |
michael@0 | 1092 | void setFailedShapeGuard() { failedShapeGuard_ = true; } |
michael@0 | 1093 | void setHadFrequentBailouts() { hadFrequentBailouts_ = true; } |
michael@0 | 1094 | void setUninlineable() { uninlineable_ = true; } |
michael@0 | 1095 | void setInvalidatedIdempotentCache() { invalidatedIdempotentCache_ = true; } |
michael@0 | 1096 | |
michael@0 | 1097 | bool hasScriptCounts() const { return hasScriptCounts_; } |
michael@0 | 1098 | |
michael@0 | 1099 | bool hasFreezeConstraints() const { return hasFreezeConstraints_; } |
michael@0 | 1100 | void setHasFreezeConstraints() { hasFreezeConstraints_ = true; } |
michael@0 | 1101 | void clearHasFreezeConstraints() { hasFreezeConstraints_ = false; } |
michael@0 | 1102 | |
michael@0 | 1103 | bool warnedAboutUndefinedProp() const { return warnedAboutUndefinedProp_; } |
michael@0 | 1104 | void setWarnedAboutUndefinedProp() { warnedAboutUndefinedProp_ = true; } |
michael@0 | 1105 | |
michael@0 | 1106 | /* See ContextFlags::funArgumentsHasLocalBinding comment. */ |
michael@0 | 1107 | bool argumentsHasVarBinding() const { |
michael@0 | 1108 | return argsHasVarBinding_; |
michael@0 | 1109 | } |
michael@0 | 1110 | jsbytecode *argumentsBytecode() const { JS_ASSERT(code()[0] == JSOP_ARGUMENTS); return code(); } |
michael@0 | 1111 | void setArgumentsHasVarBinding(); |
michael@0 | 1112 | bool argumentsAliasesFormals() const { |
michael@0 | 1113 | return argumentsHasVarBinding() && !strict(); |
michael@0 | 1114 | } |
michael@0 | 1115 | |
michael@0 | 1116 | js::GeneratorKind generatorKind() const { |
michael@0 | 1117 | return js::GeneratorKindFromBits(generatorKindBits_); |
michael@0 | 1118 | } |
michael@0 | 1119 | bool isGenerator() const { return generatorKind() != js::NotGenerator; } |
michael@0 | 1120 | bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; } |
michael@0 | 1121 | bool isStarGenerator() const { return generatorKind() == js::StarGenerator; } |
michael@0 | 1122 | void setGeneratorKind(js::GeneratorKind kind) { |
michael@0 | 1123 | // A script only gets its generator kind set as part of initialization, |
michael@0 | 1124 | // so it can only transition from not being a generator. |
michael@0 | 1125 | JS_ASSERT(!isGenerator()); |
michael@0 | 1126 | generatorKindBits_ = GeneratorKindAsBits(kind); |
michael@0 | 1127 | } |
michael@0 | 1128 | |
michael@0 | 1129 | /* |
michael@0 | 1130 | * As an optimization, even when argsHasLocalBinding, the function prologue |
michael@0 | 1131 | * may not need to create an arguments object. This is determined by |
michael@0 | 1132 | * needsArgsObj which is set by AnalyzeArgumentsUsage before running |
michael@0 | 1133 | * the script the first time. When !needsArgsObj, the prologue may simply |
michael@0 | 1134 | * write MagicValue(JS_OPTIMIZED_ARGUMENTS) to 'arguments's slot and any |
michael@0 | 1135 | * uses of 'arguments' will be guaranteed to handle this magic value. |
michael@0 | 1136 | * So avoid spurious arguments object creation, we maintain the invariant |
michael@0 | 1137 | * that needsArgsObj is only called after the script has been analyzed. |
michael@0 | 1138 | */ |
michael@0 | 1139 | bool analyzedArgsUsage() const { return !needsArgsAnalysis_; } |
michael@0 | 1140 | inline bool ensureHasAnalyzedArgsUsage(JSContext *cx); |
michael@0 | 1141 | bool needsArgsObj() const { |
michael@0 | 1142 | JS_ASSERT(analyzedArgsUsage()); |
michael@0 | 1143 | return needsArgsObj_; |
michael@0 | 1144 | } |
michael@0 | 1145 | void setNeedsArgsObj(bool needsArgsObj); |
michael@0 | 1146 | static bool argumentsOptimizationFailed(JSContext *cx, js::HandleScript script); |
michael@0 | 1147 | |
michael@0 | 1148 | /* |
michael@0 | 1149 | * Arguments access (via JSOP_*ARG* opcodes) must access the canonical |
michael@0 | 1150 | * location for the argument. If an arguments object exists AND this is a |
michael@0 | 1151 | * non-strict function (where 'arguments' aliases formals), then all access |
michael@0 | 1152 | * must go through the arguments object. Otherwise, the local slot is the |
michael@0 | 1153 | * canonical location for the arguments. Note: if a formal is aliased |
michael@0 | 1154 | * through the scope chain, then script->formalIsAliased and JSOP_*ARG* |
michael@0 | 1155 | * opcodes won't be emitted at all. |
michael@0 | 1156 | */ |
michael@0 | 1157 | bool argsObjAliasesFormals() const { |
michael@0 | 1158 | return needsArgsObj() && !strict(); |
michael@0 | 1159 | } |
michael@0 | 1160 | |
michael@0 | 1161 | bool hasAnyIonScript() const { |
michael@0 | 1162 | return hasIonScript() || hasParallelIonScript(); |
michael@0 | 1163 | } |
michael@0 | 1164 | |
michael@0 | 1165 | bool hasIonScript() const { |
michael@0 | 1166 | bool res = ion && ion != ION_DISABLED_SCRIPT && ion != ION_COMPILING_SCRIPT; |
michael@0 | 1167 | MOZ_ASSERT_IF(res, baseline); |
michael@0 | 1168 | return res; |
michael@0 | 1169 | } |
michael@0 | 1170 | bool canIonCompile() const { |
michael@0 | 1171 | return ion != ION_DISABLED_SCRIPT; |
michael@0 | 1172 | } |
michael@0 | 1173 | |
michael@0 | 1174 | bool isIonCompilingOffThread() const { |
michael@0 | 1175 | return ion == ION_COMPILING_SCRIPT; |
michael@0 | 1176 | } |
michael@0 | 1177 | |
michael@0 | 1178 | js::jit::IonScript *ionScript() const { |
michael@0 | 1179 | JS_ASSERT(hasIonScript()); |
michael@0 | 1180 | return ion; |
michael@0 | 1181 | } |
michael@0 | 1182 | js::jit::IonScript *maybeIonScript() const { |
michael@0 | 1183 | return ion; |
michael@0 | 1184 | } |
michael@0 | 1185 | js::jit::IonScript *const *addressOfIonScript() const { |
michael@0 | 1186 | return &ion; |
michael@0 | 1187 | } |
michael@0 | 1188 | void setIonScript(js::jit::IonScript *ionScript) { |
michael@0 | 1189 | if (hasIonScript()) |
michael@0 | 1190 | js::jit::IonScript::writeBarrierPre(tenuredZone(), ion); |
michael@0 | 1191 | ion = ionScript; |
michael@0 | 1192 | MOZ_ASSERT_IF(hasIonScript(), hasBaselineScript()); |
michael@0 | 1193 | updateBaselineOrIonRaw(); |
michael@0 | 1194 | } |
michael@0 | 1195 | |
michael@0 | 1196 | bool hasBaselineScript() const { |
michael@0 | 1197 | bool res = baseline && baseline != BASELINE_DISABLED_SCRIPT; |
michael@0 | 1198 | MOZ_ASSERT_IF(!res, !ion || ion == ION_DISABLED_SCRIPT); |
michael@0 | 1199 | return res; |
michael@0 | 1200 | } |
michael@0 | 1201 | bool canBaselineCompile() const { |
michael@0 | 1202 | return baseline != BASELINE_DISABLED_SCRIPT; |
michael@0 | 1203 | } |
michael@0 | 1204 | js::jit::BaselineScript *baselineScript() const { |
michael@0 | 1205 | JS_ASSERT(hasBaselineScript()); |
michael@0 | 1206 | return baseline; |
michael@0 | 1207 | } |
michael@0 | 1208 | inline void setBaselineScript(JSContext *maybecx, js::jit::BaselineScript *baselineScript); |
michael@0 | 1209 | |
michael@0 | 1210 | void updateBaselineOrIonRaw(); |
michael@0 | 1211 | |
michael@0 | 1212 | bool hasParallelIonScript() const { |
michael@0 | 1213 | return parallelIon && parallelIon != ION_DISABLED_SCRIPT && parallelIon != ION_COMPILING_SCRIPT; |
michael@0 | 1214 | } |
michael@0 | 1215 | |
michael@0 | 1216 | bool canParallelIonCompile() const { |
michael@0 | 1217 | return parallelIon != ION_DISABLED_SCRIPT; |
michael@0 | 1218 | } |
michael@0 | 1219 | |
michael@0 | 1220 | bool isParallelIonCompilingOffThread() const { |
michael@0 | 1221 | return parallelIon == ION_COMPILING_SCRIPT; |
michael@0 | 1222 | } |
michael@0 | 1223 | |
michael@0 | 1224 | js::jit::IonScript *parallelIonScript() const { |
michael@0 | 1225 | JS_ASSERT(hasParallelIonScript()); |
michael@0 | 1226 | return parallelIon; |
michael@0 | 1227 | } |
michael@0 | 1228 | js::jit::IonScript *maybeParallelIonScript() const { |
michael@0 | 1229 | return parallelIon; |
michael@0 | 1230 | } |
michael@0 | 1231 | void setParallelIonScript(js::jit::IonScript *ionScript) { |
michael@0 | 1232 | if (hasParallelIonScript()) |
michael@0 | 1233 | js::jit::IonScript::writeBarrierPre(tenuredZone(), parallelIon); |
michael@0 | 1234 | parallelIon = ionScript; |
michael@0 | 1235 | } |
michael@0 | 1236 | |
michael@0 | 1237 | static size_t offsetOfBaselineScript() { |
michael@0 | 1238 | return offsetof(JSScript, baseline); |
michael@0 | 1239 | } |
michael@0 | 1240 | static size_t offsetOfIonScript() { |
michael@0 | 1241 | return offsetof(JSScript, ion); |
michael@0 | 1242 | } |
michael@0 | 1243 | static size_t offsetOfParallelIonScript() { |
michael@0 | 1244 | return offsetof(JSScript, parallelIon); |
michael@0 | 1245 | } |
michael@0 | 1246 | static size_t offsetOfBaselineOrIonRaw() { |
michael@0 | 1247 | return offsetof(JSScript, baselineOrIonRaw); |
michael@0 | 1248 | } |
michael@0 | 1249 | static size_t offsetOfBaselineOrIonSkipArgCheck() { |
michael@0 | 1250 | return offsetof(JSScript, baselineOrIonSkipArgCheck); |
michael@0 | 1251 | } |
michael@0 | 1252 | |
michael@0 | 1253 | bool isRelazifiable() const { |
michael@0 | 1254 | return (selfHosted() || lazyScript) && |
michael@0 | 1255 | !isGenerator() && !hasBaselineScript() && !hasAnyIonScript() && !hasBeenInlined(); |
michael@0 | 1256 | } |
michael@0 | 1257 | void setLazyScript(js::LazyScript *lazy) { |
michael@0 | 1258 | lazyScript = lazy; |
michael@0 | 1259 | } |
michael@0 | 1260 | js::LazyScript *maybeLazyScript() { |
michael@0 | 1261 | return lazyScript; |
michael@0 | 1262 | } |
michael@0 | 1263 | |
michael@0 | 1264 | /* |
michael@0 | 1265 | * Original compiled function for the script, if it has a function. |
michael@0 | 1266 | * nullptr for global and eval scripts. |
michael@0 | 1267 | * The delazifying variant ensures that the function isn't lazy. The |
michael@0 | 1268 | * non-delazifying variant must only be used after earlier code has |
michael@0 | 1269 | * called ensureNonLazyCanonicalFunction and while the function can't |
michael@0 | 1270 | * have been relazified. |
michael@0 | 1271 | */ |
michael@0 | 1272 | inline JSFunction *functionDelazifying() const; |
michael@0 | 1273 | JSFunction *functionNonDelazifying() const { |
michael@0 | 1274 | return function_; |
michael@0 | 1275 | } |
michael@0 | 1276 | inline void setFunction(JSFunction *fun); |
michael@0 | 1277 | /* |
michael@0 | 1278 | * De-lazifies the canonical function. Must be called before entering code |
michael@0 | 1279 | * that expects the function to be non-lazy. |
michael@0 | 1280 | */ |
michael@0 | 1281 | inline void ensureNonLazyCanonicalFunction(JSContext *cx); |
michael@0 | 1282 | |
michael@0 | 1283 | /* |
michael@0 | 1284 | * Donor provided itself to callsite clone; null if this is non-clone. |
michael@0 | 1285 | */ |
michael@0 | 1286 | JSFunction *donorFunction() const; |
michael@0 | 1287 | void setIsCallsiteClone(JSObject *fun); |
michael@0 | 1288 | |
michael@0 | 1289 | JSFlatString *sourceData(JSContext *cx); |
michael@0 | 1290 | |
michael@0 | 1291 | static bool loadSource(JSContext *cx, js::ScriptSource *ss, bool *worked); |
michael@0 | 1292 | |
michael@0 | 1293 | void setSourceObject(JSObject *object); |
michael@0 | 1294 | JSObject *sourceObject() const { |
michael@0 | 1295 | return sourceObject_; |
michael@0 | 1296 | } |
michael@0 | 1297 | js::ScriptSourceObject &scriptSourceUnwrap() const; |
michael@0 | 1298 | js::ScriptSource *scriptSource() const; |
michael@0 | 1299 | JSPrincipals *originPrincipals() const { return scriptSource()->originPrincipals(); } |
michael@0 | 1300 | const char *filename() const { return scriptSource()->filename(); } |
michael@0 | 1301 | |
michael@0 | 1302 | public: |
michael@0 | 1303 | |
michael@0 | 1304 | /* Return whether this script was compiled for 'eval' */ |
michael@0 | 1305 | bool isForEval() { return isCachedEval() || isActiveEval(); } |
michael@0 | 1306 | |
michael@0 | 1307 | #ifdef DEBUG |
michael@0 | 1308 | unsigned id(); |
michael@0 | 1309 | #else |
michael@0 | 1310 | unsigned id() { return 0; } |
michael@0 | 1311 | #endif |
michael@0 | 1312 | |
michael@0 | 1313 | /* Ensure the script has a TypeScript. */ |
michael@0 | 1314 | inline bool ensureHasTypes(JSContext *cx); |
michael@0 | 1315 | |
michael@0 | 1316 | inline js::GlobalObject &global() const; |
michael@0 | 1317 | js::GlobalObject &uninlinedGlobal() const; |
michael@0 | 1318 | |
michael@0 | 1319 | /* See StaticScopeIter comment. */ |
michael@0 | 1320 | JSObject *enclosingStaticScope() const { |
michael@0 | 1321 | if (isCallsiteClone()) |
michael@0 | 1322 | return nullptr; |
michael@0 | 1323 | return enclosingScopeOrOriginalFunction_; |
michael@0 | 1324 | } |
michael@0 | 1325 | |
michael@0 | 1326 | private: |
michael@0 | 1327 | bool makeTypes(JSContext *cx); |
michael@0 | 1328 | |
michael@0 | 1329 | public: |
michael@0 | 1330 | uint32_t getUseCount() const { |
michael@0 | 1331 | return useCount; |
michael@0 | 1332 | } |
michael@0 | 1333 | uint32_t incUseCount(uint32_t amount = 1) { return useCount += amount; } |
michael@0 | 1334 | uint32_t *addressOfUseCount() { return &useCount; } |
michael@0 | 1335 | static size_t offsetOfUseCount() { return offsetof(JSScript, useCount); } |
michael@0 | 1336 | void resetUseCount() { useCount = 0; } |
michael@0 | 1337 | |
michael@0 | 1338 | public: |
michael@0 | 1339 | bool initScriptCounts(JSContext *cx); |
michael@0 | 1340 | js::PCCounts getPCCounts(jsbytecode *pc); |
michael@0 | 1341 | void addIonCounts(js::jit::IonScriptCounts *ionCounts); |
michael@0 | 1342 | js::jit::IonScriptCounts *getIonCounts(); |
michael@0 | 1343 | js::ScriptCounts releaseScriptCounts(); |
michael@0 | 1344 | void destroyScriptCounts(js::FreeOp *fop); |
michael@0 | 1345 | |
michael@0 | 1346 | jsbytecode *main() { |
michael@0 | 1347 | return code() + mainOffset(); |
michael@0 | 1348 | } |
michael@0 | 1349 | |
michael@0 | 1350 | /* |
michael@0 | 1351 | * computedSizeOfData() is the in-use size of all the data sections. |
michael@0 | 1352 | * sizeOfData() is the size of the block allocated to hold all the data |
michael@0 | 1353 | * sections (which can be larger than the in-use size). |
michael@0 | 1354 | */ |
michael@0 | 1355 | size_t computedSizeOfData() const; |
michael@0 | 1356 | size_t sizeOfData(mozilla::MallocSizeOf mallocSizeOf) const; |
michael@0 | 1357 | size_t sizeOfTypeScript(mozilla::MallocSizeOf mallocSizeOf) const; |
michael@0 | 1358 | |
michael@0 | 1359 | uint32_t numNotes(); /* Number of srcnote slots in the srcnotes section */ |
michael@0 | 1360 | |
michael@0 | 1361 | /* Script notes are allocated right after the code. */ |
michael@0 | 1362 | jssrcnote *notes() { return (jssrcnote *)(code() + length()); } |
michael@0 | 1363 | |
michael@0 | 1364 | bool hasArray(ArrayKind kind) { |
michael@0 | 1365 | return hasArrayBits & (1 << kind); |
michael@0 | 1366 | } |
michael@0 | 1367 | void setHasArray(ArrayKind kind) { hasArrayBits |= (1 << kind); } |
michael@0 | 1368 | void cloneHasArray(JSScript *script) { hasArrayBits = script->hasArrayBits; } |
michael@0 | 1369 | |
michael@0 | 1370 | bool hasConsts() { return hasArray(CONSTS); } |
michael@0 | 1371 | bool hasObjects() { return hasArray(OBJECTS); } |
michael@0 | 1372 | bool hasRegexps() { return hasArray(REGEXPS); } |
michael@0 | 1373 | bool hasTrynotes() { return hasArray(TRYNOTES); } |
michael@0 | 1374 | bool hasBlockScopes() { return hasArray(BLOCK_SCOPES); } |
michael@0 | 1375 | |
michael@0 | 1376 | #define OFF(fooOff, hasFoo, t) (fooOff() + (hasFoo() ? sizeof(t) : 0)) |
michael@0 | 1377 | |
michael@0 | 1378 | size_t constsOffset() { return 0; } |
michael@0 | 1379 | size_t objectsOffset() { return OFF(constsOffset, hasConsts, js::ConstArray); } |
michael@0 | 1380 | size_t regexpsOffset() { return OFF(objectsOffset, hasObjects, js::ObjectArray); } |
michael@0 | 1381 | size_t trynotesOffset() { return OFF(regexpsOffset, hasRegexps, js::ObjectArray); } |
michael@0 | 1382 | size_t blockScopesOffset(){ return OFF(trynotesOffset, hasTrynotes, js::TryNoteArray); } |
michael@0 | 1383 | |
michael@0 | 1384 | size_t dataSize() const { return dataSize_; } |
michael@0 | 1385 | |
michael@0 | 1386 | js::ConstArray *consts() { |
michael@0 | 1387 | JS_ASSERT(hasConsts()); |
michael@0 | 1388 | return reinterpret_cast<js::ConstArray *>(data + constsOffset()); |
michael@0 | 1389 | } |
michael@0 | 1390 | |
michael@0 | 1391 | js::ObjectArray *objects() { |
michael@0 | 1392 | JS_ASSERT(hasObjects()); |
michael@0 | 1393 | return reinterpret_cast<js::ObjectArray *>(data + objectsOffset()); |
michael@0 | 1394 | } |
michael@0 | 1395 | |
michael@0 | 1396 | js::ObjectArray *regexps() { |
michael@0 | 1397 | JS_ASSERT(hasRegexps()); |
michael@0 | 1398 | return reinterpret_cast<js::ObjectArray *>(data + regexpsOffset()); |
michael@0 | 1399 | } |
michael@0 | 1400 | |
michael@0 | 1401 | js::TryNoteArray *trynotes() { |
michael@0 | 1402 | JS_ASSERT(hasTrynotes()); |
michael@0 | 1403 | return reinterpret_cast<js::TryNoteArray *>(data + trynotesOffset()); |
michael@0 | 1404 | } |
michael@0 | 1405 | |
michael@0 | 1406 | js::BlockScopeArray *blockScopes() { |
michael@0 | 1407 | JS_ASSERT(hasBlockScopes()); |
michael@0 | 1408 | return reinterpret_cast<js::BlockScopeArray *>(data + blockScopesOffset()); |
michael@0 | 1409 | } |
michael@0 | 1410 | |
michael@0 | 1411 | bool hasLoops(); |
michael@0 | 1412 | |
michael@0 | 1413 | size_t natoms() const { return natoms_; } |
michael@0 | 1414 | |
michael@0 | 1415 | js::HeapPtrAtom &getAtom(size_t index) const { |
michael@0 | 1416 | JS_ASSERT(index < natoms()); |
michael@0 | 1417 | return atoms[index]; |
michael@0 | 1418 | } |
michael@0 | 1419 | |
michael@0 | 1420 | js::HeapPtrAtom &getAtom(jsbytecode *pc) const { |
michael@0 | 1421 | JS_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t))); |
michael@0 | 1422 | return getAtom(GET_UINT32_INDEX(pc)); |
michael@0 | 1423 | } |
michael@0 | 1424 | |
michael@0 | 1425 | js::PropertyName *getName(size_t index) { |
michael@0 | 1426 | return getAtom(index)->asPropertyName(); |
michael@0 | 1427 | } |
michael@0 | 1428 | |
michael@0 | 1429 | js::PropertyName *getName(jsbytecode *pc) const { |
michael@0 | 1430 | JS_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t))); |
michael@0 | 1431 | return getAtom(GET_UINT32_INDEX(pc))->asPropertyName(); |
michael@0 | 1432 | } |
michael@0 | 1433 | |
michael@0 | 1434 | JSObject *getObject(size_t index) { |
michael@0 | 1435 | js::ObjectArray *arr = objects(); |
michael@0 | 1436 | JS_ASSERT(index < arr->length); |
michael@0 | 1437 | return arr->vector[index]; |
michael@0 | 1438 | } |
michael@0 | 1439 | |
michael@0 | 1440 | size_t innerObjectsStart() { |
michael@0 | 1441 | // The first object contains the caller if savedCallerFun is used. |
michael@0 | 1442 | return savedCallerFun() ? 1 : 0; |
michael@0 | 1443 | } |
michael@0 | 1444 | |
michael@0 | 1445 | JSObject *getObject(jsbytecode *pc) { |
michael@0 | 1446 | JS_ASSERT(containsPC(pc) && containsPC(pc + sizeof(uint32_t))); |
michael@0 | 1447 | return getObject(GET_UINT32_INDEX(pc)); |
michael@0 | 1448 | } |
michael@0 | 1449 | |
michael@0 | 1450 | JSVersion getVersion() const { |
michael@0 | 1451 | return JSVersion(version); |
michael@0 | 1452 | } |
michael@0 | 1453 | |
michael@0 | 1454 | inline JSFunction *getFunction(size_t index); |
michael@0 | 1455 | inline JSFunction *getCallerFunction(); |
michael@0 | 1456 | inline JSFunction *functionOrCallerFunction(); |
michael@0 | 1457 | |
michael@0 | 1458 | inline js::RegExpObject *getRegExp(size_t index); |
michael@0 | 1459 | inline js::RegExpObject *getRegExp(jsbytecode *pc); |
michael@0 | 1460 | |
michael@0 | 1461 | const js::Value &getConst(size_t index) { |
michael@0 | 1462 | js::ConstArray *arr = consts(); |
michael@0 | 1463 | JS_ASSERT(index < arr->length); |
michael@0 | 1464 | return arr->vector[index]; |
michael@0 | 1465 | } |
michael@0 | 1466 | |
michael@0 | 1467 | js::NestedScopeObject *getStaticScope(jsbytecode *pc); |
michael@0 | 1468 | |
michael@0 | 1469 | /* |
michael@0 | 1470 | * The isEmpty method tells whether this script has code that computes any |
michael@0 | 1471 | * result (not return value, result AKA normal completion value) other than |
michael@0 | 1472 | * JSVAL_VOID, or any other effects. |
michael@0 | 1473 | */ |
michael@0 | 1474 | bool isEmpty() const { |
michael@0 | 1475 | if (length() > 3) |
michael@0 | 1476 | return false; |
michael@0 | 1477 | |
michael@0 | 1478 | jsbytecode *pc = code(); |
michael@0 | 1479 | if (noScriptRval() && JSOp(*pc) == JSOP_FALSE) |
michael@0 | 1480 | ++pc; |
michael@0 | 1481 | return JSOp(*pc) == JSOP_RETRVAL; |
michael@0 | 1482 | } |
michael@0 | 1483 | |
michael@0 | 1484 | bool varIsAliased(uint32_t varSlot); |
michael@0 | 1485 | bool formalIsAliased(unsigned argSlot); |
michael@0 | 1486 | bool formalLivesInArgumentsObject(unsigned argSlot); |
michael@0 | 1487 | |
michael@0 | 1488 | private: |
michael@0 | 1489 | /* Change this->stepMode to |newValue|. */ |
michael@0 | 1490 | void setNewStepMode(js::FreeOp *fop, uint32_t newValue); |
michael@0 | 1491 | |
michael@0 | 1492 | bool ensureHasDebugScript(JSContext *cx); |
michael@0 | 1493 | js::DebugScript *debugScript(); |
michael@0 | 1494 | js::DebugScript *releaseDebugScript(); |
michael@0 | 1495 | void destroyDebugScript(js::FreeOp *fop); |
michael@0 | 1496 | |
michael@0 | 1497 | public: |
michael@0 | 1498 | bool hasBreakpointsAt(jsbytecode *pc); |
michael@0 | 1499 | bool hasAnyBreakpointsOrStepMode() { return hasDebugScript_; } |
michael@0 | 1500 | |
michael@0 | 1501 | js::BreakpointSite *getBreakpointSite(jsbytecode *pc) |
michael@0 | 1502 | { |
michael@0 | 1503 | return hasDebugScript_ ? debugScript()->breakpoints[pcToOffset(pc)] : nullptr; |
michael@0 | 1504 | } |
michael@0 | 1505 | |
michael@0 | 1506 | js::BreakpointSite *getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc); |
michael@0 | 1507 | |
michael@0 | 1508 | void destroyBreakpointSite(js::FreeOp *fop, jsbytecode *pc); |
michael@0 | 1509 | |
michael@0 | 1510 | void clearBreakpointsIn(js::FreeOp *fop, js::Debugger *dbg, JSObject *handler); |
michael@0 | 1511 | void clearTraps(js::FreeOp *fop); |
michael@0 | 1512 | |
michael@0 | 1513 | void markTrapClosures(JSTracer *trc); |
michael@0 | 1514 | |
michael@0 | 1515 | /* |
michael@0 | 1516 | * Set or clear the single-step flag. If the flag is set or the count |
michael@0 | 1517 | * (adjusted by changeStepModeCount) is non-zero, then the script is in |
michael@0 | 1518 | * single-step mode. (JSD uses an on/off-style interface; Debugger uses a |
michael@0 | 1519 | * count-style interface.) |
michael@0 | 1520 | */ |
michael@0 | 1521 | bool setStepModeFlag(JSContext *cx, bool step); |
michael@0 | 1522 | |
michael@0 | 1523 | /* |
michael@0 | 1524 | * Increment or decrement the single-step count. If the count is non-zero or |
michael@0 | 1525 | * the flag (set by setStepModeFlag) is set, then the script is in |
michael@0 | 1526 | * single-step mode. (JSD uses an on/off-style interface; Debugger uses a |
michael@0 | 1527 | * count-style interface.) |
michael@0 | 1528 | * |
michael@0 | 1529 | * Only incrementing is fallible, as it could allocate a DebugScript. |
michael@0 | 1530 | */ |
michael@0 | 1531 | bool incrementStepModeCount(JSContext *cx); |
michael@0 | 1532 | void decrementStepModeCount(js::FreeOp *fop); |
michael@0 | 1533 | |
michael@0 | 1534 | bool stepModeEnabled() { return hasDebugScript_ && !!debugScript()->stepMode; } |
michael@0 | 1535 | |
michael@0 | 1536 | #ifdef DEBUG |
michael@0 | 1537 | uint32_t stepModeCount() { return hasDebugScript_ ? (debugScript()->stepMode & stepCountMask) : 0; } |
michael@0 | 1538 | #endif |
michael@0 | 1539 | |
michael@0 | 1540 | void finalize(js::FreeOp *fop); |
michael@0 | 1541 | |
michael@0 | 1542 | static inline js::ThingRootKind rootKind() { return js::THING_ROOT_SCRIPT; } |
michael@0 | 1543 | |
michael@0 | 1544 | void markChildren(JSTracer *trc); |
michael@0 | 1545 | }; |
michael@0 | 1546 | |
michael@0 | 1547 | /* If this fails, add/remove padding within JSScript. */ |
michael@0 | 1548 | static_assert(sizeof(JSScript) % js::gc::CellSize == 0, |
michael@0 | 1549 | "Size of JSScript must be an integral multiple of js::gc::CellSize"); |
michael@0 | 1550 | |
michael@0 | 1551 | namespace js { |
michael@0 | 1552 | |
michael@0 | 1553 | /* |
michael@0 | 1554 | * Iterator over a script's bindings (formals and variables). |
michael@0 | 1555 | * The order of iteration is: |
michael@0 | 1556 | * - first, formal arguments, from index 0 to numArgs |
michael@0 | 1557 | * - next, variables, from index 0 to numLocals |
michael@0 | 1558 | */ |
michael@0 | 1559 | class BindingIter |
michael@0 | 1560 | { |
michael@0 | 1561 | const InternalBindingsHandle bindings_; |
michael@0 | 1562 | uint32_t i_; |
michael@0 | 1563 | |
michael@0 | 1564 | friend class Bindings; |
michael@0 | 1565 | |
michael@0 | 1566 | public: |
michael@0 | 1567 | explicit BindingIter(const InternalBindingsHandle &bindings) : bindings_(bindings), i_(0) {} |
michael@0 | 1568 | explicit BindingIter(const HandleScript &script) : bindings_(script, &script->bindings), i_(0) {} |
michael@0 | 1569 | |
michael@0 | 1570 | bool done() const { return i_ == bindings_->count(); } |
michael@0 | 1571 | operator bool() const { return !done(); } |
michael@0 | 1572 | void operator++(int) { JS_ASSERT(!done()); i_++; } |
michael@0 | 1573 | BindingIter &operator++() { (*this)++; return *this; } |
michael@0 | 1574 | |
michael@0 | 1575 | uint32_t frameIndex() const { |
michael@0 | 1576 | JS_ASSERT(!done()); |
michael@0 | 1577 | return i_ < bindings_->numArgs() ? i_ : i_ - bindings_->numArgs(); |
michael@0 | 1578 | } |
michael@0 | 1579 | |
michael@0 | 1580 | const Binding &operator*() const { JS_ASSERT(!done()); return bindings_->bindingArray()[i_]; } |
michael@0 | 1581 | const Binding *operator->() const { JS_ASSERT(!done()); return &bindings_->bindingArray()[i_]; } |
michael@0 | 1582 | }; |
michael@0 | 1583 | |
michael@0 | 1584 | /* |
michael@0 | 1585 | * This helper function fills the given BindingVector with the sequential |
michael@0 | 1586 | * values of BindingIter. |
michael@0 | 1587 | */ |
michael@0 | 1588 | |
michael@0 | 1589 | typedef Vector<Binding, 32> BindingVector; |
michael@0 | 1590 | |
michael@0 | 1591 | extern bool |
michael@0 | 1592 | FillBindingVector(HandleScript fromScript, BindingVector *vec); |
michael@0 | 1593 | |
michael@0 | 1594 | /* |
michael@0 | 1595 | * Iterator over the aliased formal bindings in ascending index order. This can |
michael@0 | 1596 | * be veiwed as a filtering of BindingIter with predicate |
michael@0 | 1597 | * bi->aliased() && bi->kind() == Binding::ARGUMENT |
michael@0 | 1598 | */ |
michael@0 | 1599 | class AliasedFormalIter |
michael@0 | 1600 | { |
michael@0 | 1601 | const Binding *begin_, *p_, *end_; |
michael@0 | 1602 | unsigned slot_; |
michael@0 | 1603 | |
michael@0 | 1604 | void settle() { |
michael@0 | 1605 | while (p_ != end_ && !p_->aliased()) |
michael@0 | 1606 | p_++; |
michael@0 | 1607 | } |
michael@0 | 1608 | |
michael@0 | 1609 | public: |
michael@0 | 1610 | explicit inline AliasedFormalIter(JSScript *script); |
michael@0 | 1611 | |
michael@0 | 1612 | bool done() const { return p_ == end_; } |
michael@0 | 1613 | operator bool() const { return !done(); } |
michael@0 | 1614 | void operator++(int) { JS_ASSERT(!done()); p_++; slot_++; settle(); } |
michael@0 | 1615 | |
michael@0 | 1616 | const Binding &operator*() const { JS_ASSERT(!done()); return *p_; } |
michael@0 | 1617 | const Binding *operator->() const { JS_ASSERT(!done()); return p_; } |
michael@0 | 1618 | unsigned frameIndex() const { JS_ASSERT(!done()); return p_ - begin_; } |
michael@0 | 1619 | unsigned scopeSlot() const { JS_ASSERT(!done()); return slot_; } |
michael@0 | 1620 | }; |
michael@0 | 1621 | |
michael@0 | 1622 | // Information about a script which may be (or has been) lazily compiled to |
michael@0 | 1623 | // bytecode from its source. |
michael@0 | 1624 | class LazyScript : public gc::BarrieredCell<LazyScript> |
michael@0 | 1625 | { |
michael@0 | 1626 | // If non-nullptr, the script has been compiled and this is a forwarding |
michael@0 | 1627 | // pointer to the result. |
michael@0 | 1628 | HeapPtrScript script_; |
michael@0 | 1629 | |
michael@0 | 1630 | // Original function with which the lazy script is associated. |
michael@0 | 1631 | HeapPtrFunction function_; |
michael@0 | 1632 | |
michael@0 | 1633 | // Function or block chain in which the script is nested, or nullptr. |
michael@0 | 1634 | HeapPtrObject enclosingScope_; |
michael@0 | 1635 | |
michael@0 | 1636 | // ScriptSourceObject, or nullptr if the script in which this is nested |
michael@0 | 1637 | // has not been compiled yet. This is never a CCW; we don't clone |
michael@0 | 1638 | // LazyScripts into other compartments. |
michael@0 | 1639 | HeapPtrObject sourceObject_; |
michael@0 | 1640 | |
michael@0 | 1641 | // Heap allocated table with any free variables or inner functions. |
michael@0 | 1642 | void *table_; |
michael@0 | 1643 | |
michael@0 | 1644 | #if JS_BITS_PER_WORD == 32 |
michael@0 | 1645 | uint32_t padding; |
michael@0 | 1646 | #endif |
michael@0 | 1647 | |
michael@0 | 1648 | struct PackedView { |
michael@0 | 1649 | // Assorted bits that should really be in ScriptSourceObject. |
michael@0 | 1650 | uint32_t version : 8; |
michael@0 | 1651 | |
michael@0 | 1652 | uint32_t numFreeVariables : 24; |
michael@0 | 1653 | uint32_t numInnerFunctions : 23; |
michael@0 | 1654 | |
michael@0 | 1655 | uint32_t generatorKindBits : 2; |
michael@0 | 1656 | |
michael@0 | 1657 | // N.B. These are booleans but need to be uint32_t to pack correctly on MSVC. |
michael@0 | 1658 | uint32_t strict : 1; |
michael@0 | 1659 | uint32_t bindingsAccessedDynamically : 1; |
michael@0 | 1660 | uint32_t hasDebuggerStatement : 1; |
michael@0 | 1661 | uint32_t directlyInsideEval : 1; |
michael@0 | 1662 | uint32_t usesArgumentsAndApply : 1; |
michael@0 | 1663 | uint32_t hasBeenCloned : 1; |
michael@0 | 1664 | uint32_t treatAsRunOnce : 1; |
michael@0 | 1665 | }; |
michael@0 | 1666 | |
michael@0 | 1667 | union { |
michael@0 | 1668 | PackedView p_; |
michael@0 | 1669 | uint64_t packedFields_; |
michael@0 | 1670 | }; |
michael@0 | 1671 | |
michael@0 | 1672 | // Source location for the script. |
michael@0 | 1673 | uint32_t begin_; |
michael@0 | 1674 | uint32_t end_; |
michael@0 | 1675 | uint32_t lineno_; |
michael@0 | 1676 | uint32_t column_; |
michael@0 | 1677 | |
michael@0 | 1678 | LazyScript(JSFunction *fun, void *table, uint64_t packedFields, |
michael@0 | 1679 | uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column); |
michael@0 | 1680 | |
michael@0 | 1681 | // Create a LazyScript without initializing the freeVariables and the |
michael@0 | 1682 | // innerFunctions. To be GC-safe, the caller must initialize both vectors |
michael@0 | 1683 | // with valid atoms and functions. |
michael@0 | 1684 | static LazyScript *CreateRaw(ExclusiveContext *cx, HandleFunction fun, |
michael@0 | 1685 | uint64_t packedData, uint32_t begin, uint32_t end, |
michael@0 | 1686 | uint32_t lineno, uint32_t column); |
michael@0 | 1687 | |
michael@0 | 1688 | public: |
michael@0 | 1689 | // Create a LazyScript without initializing the freeVariables and the |
michael@0 | 1690 | // innerFunctions. To be GC-safe, the caller must initialize both vectors |
michael@0 | 1691 | // with valid atoms and functions. |
michael@0 | 1692 | static LazyScript *CreateRaw(ExclusiveContext *cx, HandleFunction fun, |
michael@0 | 1693 | uint32_t numFreeVariables, uint32_t numInnerFunctions, |
michael@0 | 1694 | JSVersion version, uint32_t begin, uint32_t end, |
michael@0 | 1695 | uint32_t lineno, uint32_t column); |
michael@0 | 1696 | |
michael@0 | 1697 | // Create a LazyScript and initialize the freeVariables and the |
michael@0 | 1698 | // innerFunctions with dummy values to be replaced in a later initialization |
michael@0 | 1699 | // phase. |
michael@0 | 1700 | static LazyScript *Create(ExclusiveContext *cx, HandleFunction fun, |
michael@0 | 1701 | uint64_t packedData, uint32_t begin, uint32_t end, |
michael@0 | 1702 | uint32_t lineno, uint32_t column); |
michael@0 | 1703 | |
michael@0 | 1704 | void initRuntimeFields(uint64_t packedFields); |
michael@0 | 1705 | |
michael@0 | 1706 | inline JSFunction *functionDelazifying(JSContext *cx) const; |
michael@0 | 1707 | JSFunction *functionNonDelazifying() const { |
michael@0 | 1708 | return function_; |
michael@0 | 1709 | } |
michael@0 | 1710 | |
michael@0 | 1711 | void initScript(JSScript *script); |
michael@0 | 1712 | void resetScript(); |
michael@0 | 1713 | JSScript *maybeScript() { |
michael@0 | 1714 | return script_; |
michael@0 | 1715 | } |
michael@0 | 1716 | |
michael@0 | 1717 | JSObject *enclosingScope() const { |
michael@0 | 1718 | return enclosingScope_; |
michael@0 | 1719 | } |
michael@0 | 1720 | ScriptSourceObject *sourceObject() const; |
michael@0 | 1721 | ScriptSource *scriptSource() const { |
michael@0 | 1722 | return sourceObject()->source(); |
michael@0 | 1723 | } |
michael@0 | 1724 | JSPrincipals *originPrincipals() const { |
michael@0 | 1725 | return scriptSource()->originPrincipals(); |
michael@0 | 1726 | } |
michael@0 | 1727 | JSVersion version() const { |
michael@0 | 1728 | JS_STATIC_ASSERT(JSVERSION_UNKNOWN == -1); |
michael@0 | 1729 | return (p_.version == JS_BIT(8) - 1) ? JSVERSION_UNKNOWN : JSVersion(p_.version); |
michael@0 | 1730 | } |
michael@0 | 1731 | |
michael@0 | 1732 | void setParent(JSObject *enclosingScope, ScriptSourceObject *sourceObject); |
michael@0 | 1733 | |
michael@0 | 1734 | uint32_t numFreeVariables() const { |
michael@0 | 1735 | return p_.numFreeVariables; |
michael@0 | 1736 | } |
michael@0 | 1737 | HeapPtrAtom *freeVariables() { |
michael@0 | 1738 | return (HeapPtrAtom *)table_; |
michael@0 | 1739 | } |
michael@0 | 1740 | |
michael@0 | 1741 | uint32_t numInnerFunctions() const { |
michael@0 | 1742 | return p_.numInnerFunctions; |
michael@0 | 1743 | } |
michael@0 | 1744 | HeapPtrFunction *innerFunctions() { |
michael@0 | 1745 | return (HeapPtrFunction *)&freeVariables()[numFreeVariables()]; |
michael@0 | 1746 | } |
michael@0 | 1747 | |
michael@0 | 1748 | GeneratorKind generatorKind() const { return GeneratorKindFromBits(p_.generatorKindBits); } |
michael@0 | 1749 | |
michael@0 | 1750 | bool isGenerator() const { return generatorKind() != NotGenerator; } |
michael@0 | 1751 | |
michael@0 | 1752 | bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; } |
michael@0 | 1753 | |
michael@0 | 1754 | bool isStarGenerator() const { return generatorKind() == StarGenerator; } |
michael@0 | 1755 | |
michael@0 | 1756 | void setGeneratorKind(GeneratorKind kind) { |
michael@0 | 1757 | // A script only gets its generator kind set as part of initialization, |
michael@0 | 1758 | // so it can only transition from NotGenerator. |
michael@0 | 1759 | JS_ASSERT(!isGenerator()); |
michael@0 | 1760 | // Legacy generators cannot currently be lazy. |
michael@0 | 1761 | JS_ASSERT(kind != LegacyGenerator); |
michael@0 | 1762 | p_.generatorKindBits = GeneratorKindAsBits(kind); |
michael@0 | 1763 | } |
michael@0 | 1764 | |
michael@0 | 1765 | bool strict() const { |
michael@0 | 1766 | return p_.strict; |
michael@0 | 1767 | } |
michael@0 | 1768 | void setStrict() { |
michael@0 | 1769 | p_.strict = true; |
michael@0 | 1770 | } |
michael@0 | 1771 | |
michael@0 | 1772 | bool bindingsAccessedDynamically() const { |
michael@0 | 1773 | return p_.bindingsAccessedDynamically; |
michael@0 | 1774 | } |
michael@0 | 1775 | void setBindingsAccessedDynamically() { |
michael@0 | 1776 | p_.bindingsAccessedDynamically = true; |
michael@0 | 1777 | } |
michael@0 | 1778 | |
michael@0 | 1779 | bool hasDebuggerStatement() const { |
michael@0 | 1780 | return p_.hasDebuggerStatement; |
michael@0 | 1781 | } |
michael@0 | 1782 | void setHasDebuggerStatement() { |
michael@0 | 1783 | p_.hasDebuggerStatement = true; |
michael@0 | 1784 | } |
michael@0 | 1785 | |
michael@0 | 1786 | bool directlyInsideEval() const { |
michael@0 | 1787 | return p_.directlyInsideEval; |
michael@0 | 1788 | } |
michael@0 | 1789 | void setDirectlyInsideEval() { |
michael@0 | 1790 | p_.directlyInsideEval = true; |
michael@0 | 1791 | } |
michael@0 | 1792 | |
michael@0 | 1793 | bool usesArgumentsAndApply() const { |
michael@0 | 1794 | return p_.usesArgumentsAndApply; |
michael@0 | 1795 | } |
michael@0 | 1796 | void setUsesArgumentsAndApply() { |
michael@0 | 1797 | p_.usesArgumentsAndApply = true; |
michael@0 | 1798 | } |
michael@0 | 1799 | |
michael@0 | 1800 | bool hasBeenCloned() const { |
michael@0 | 1801 | return p_.hasBeenCloned; |
michael@0 | 1802 | } |
michael@0 | 1803 | void setHasBeenCloned() { |
michael@0 | 1804 | p_.hasBeenCloned = true; |
michael@0 | 1805 | } |
michael@0 | 1806 | |
michael@0 | 1807 | bool treatAsRunOnce() const { |
michael@0 | 1808 | return p_.treatAsRunOnce; |
michael@0 | 1809 | } |
michael@0 | 1810 | void setTreatAsRunOnce() { |
michael@0 | 1811 | p_.treatAsRunOnce = true; |
michael@0 | 1812 | } |
michael@0 | 1813 | |
michael@0 | 1814 | ScriptSource *source() const { |
michael@0 | 1815 | return sourceObject()->source(); |
michael@0 | 1816 | } |
michael@0 | 1817 | uint32_t begin() const { |
michael@0 | 1818 | return begin_; |
michael@0 | 1819 | } |
michael@0 | 1820 | uint32_t end() const { |
michael@0 | 1821 | return end_; |
michael@0 | 1822 | } |
michael@0 | 1823 | uint32_t lineno() const { |
michael@0 | 1824 | return lineno_; |
michael@0 | 1825 | } |
michael@0 | 1826 | uint32_t column() const { |
michael@0 | 1827 | return column_; |
michael@0 | 1828 | } |
michael@0 | 1829 | |
michael@0 | 1830 | bool hasUncompiledEnclosingScript() const; |
michael@0 | 1831 | uint32_t staticLevel(JSContext *cx) const; |
michael@0 | 1832 | |
michael@0 | 1833 | void markChildren(JSTracer *trc); |
michael@0 | 1834 | void finalize(js::FreeOp *fop); |
michael@0 | 1835 | |
michael@0 | 1836 | static inline js::ThingRootKind rootKind() { return js::THING_ROOT_LAZY_SCRIPT; } |
michael@0 | 1837 | |
michael@0 | 1838 | size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) |
michael@0 | 1839 | { |
michael@0 | 1840 | return mallocSizeOf(table_); |
michael@0 | 1841 | } |
michael@0 | 1842 | |
michael@0 | 1843 | uint64_t packedFields() const { |
michael@0 | 1844 | return packedFields_; |
michael@0 | 1845 | } |
michael@0 | 1846 | }; |
michael@0 | 1847 | |
michael@0 | 1848 | /* If this fails, add/remove padding within LazyScript. */ |
michael@0 | 1849 | JS_STATIC_ASSERT(sizeof(LazyScript) % js::gc::CellSize == 0); |
michael@0 | 1850 | |
michael@0 | 1851 | /* |
michael@0 | 1852 | * New-script-hook calling is factored from JSScript::fullyInitFromEmitter() so |
michael@0 | 1853 | * that it and callers of XDRScript() can share this code. In the case of |
michael@0 | 1854 | * callers of XDRScript(), the hook should be invoked only after successful |
michael@0 | 1855 | * decode of any owning function (the fun parameter) or script object (null |
michael@0 | 1856 | * fun). |
michael@0 | 1857 | */ |
michael@0 | 1858 | extern void |
michael@0 | 1859 | CallNewScriptHook(JSContext *cx, JS::HandleScript script, JS::HandleFunction fun); |
michael@0 | 1860 | |
michael@0 | 1861 | extern void |
michael@0 | 1862 | CallDestroyScriptHook(FreeOp *fop, JSScript *script); |
michael@0 | 1863 | |
michael@0 | 1864 | struct SharedScriptData |
michael@0 | 1865 | { |
michael@0 | 1866 | uint32_t length; |
michael@0 | 1867 | uint32_t natoms; |
michael@0 | 1868 | bool marked; |
michael@0 | 1869 | jsbytecode data[1]; |
michael@0 | 1870 | |
michael@0 | 1871 | static SharedScriptData *new_(ExclusiveContext *cx, uint32_t codeLength, |
michael@0 | 1872 | uint32_t srcnotesLength, uint32_t natoms); |
michael@0 | 1873 | |
michael@0 | 1874 | HeapPtrAtom *atoms() { |
michael@0 | 1875 | if (!natoms) |
michael@0 | 1876 | return nullptr; |
michael@0 | 1877 | return reinterpret_cast<HeapPtrAtom *>(data + length - sizeof(JSAtom *) * natoms); |
michael@0 | 1878 | } |
michael@0 | 1879 | |
michael@0 | 1880 | static SharedScriptData *fromBytecode(const jsbytecode *bytecode) { |
michael@0 | 1881 | return (SharedScriptData *)(bytecode - offsetof(SharedScriptData, data)); |
michael@0 | 1882 | } |
michael@0 | 1883 | |
michael@0 | 1884 | private: |
michael@0 | 1885 | SharedScriptData() MOZ_DELETE; |
michael@0 | 1886 | SharedScriptData(const SharedScriptData&) MOZ_DELETE; |
michael@0 | 1887 | }; |
michael@0 | 1888 | |
michael@0 | 1889 | struct ScriptBytecodeHasher |
michael@0 | 1890 | { |
michael@0 | 1891 | struct Lookup |
michael@0 | 1892 | { |
michael@0 | 1893 | jsbytecode *code; |
michael@0 | 1894 | uint32_t length; |
michael@0 | 1895 | |
michael@0 | 1896 | Lookup(SharedScriptData *ssd) : code(ssd->data), length(ssd->length) {} |
michael@0 | 1897 | }; |
michael@0 | 1898 | static HashNumber hash(const Lookup &l) { return mozilla::HashBytes(l.code, l.length); } |
michael@0 | 1899 | static bool match(SharedScriptData *entry, const Lookup &lookup) { |
michael@0 | 1900 | if (entry->length != lookup.length) |
michael@0 | 1901 | return false; |
michael@0 | 1902 | return mozilla::PodEqual<jsbytecode>(entry->data, lookup.code, lookup.length); |
michael@0 | 1903 | } |
michael@0 | 1904 | }; |
michael@0 | 1905 | |
michael@0 | 1906 | typedef HashSet<SharedScriptData*, |
michael@0 | 1907 | ScriptBytecodeHasher, |
michael@0 | 1908 | SystemAllocPolicy> ScriptDataTable; |
michael@0 | 1909 | |
michael@0 | 1910 | extern void |
michael@0 | 1911 | UnmarkScriptData(JSRuntime *rt); |
michael@0 | 1912 | |
michael@0 | 1913 | extern void |
michael@0 | 1914 | SweepScriptData(JSRuntime *rt); |
michael@0 | 1915 | |
michael@0 | 1916 | extern void |
michael@0 | 1917 | FreeScriptData(JSRuntime *rt); |
michael@0 | 1918 | |
michael@0 | 1919 | struct ScriptAndCounts |
michael@0 | 1920 | { |
michael@0 | 1921 | /* This structure is stored and marked from the JSRuntime. */ |
michael@0 | 1922 | JSScript *script; |
michael@0 | 1923 | ScriptCounts scriptCounts; |
michael@0 | 1924 | |
michael@0 | 1925 | PCCounts &getPCCounts(jsbytecode *pc) const { |
michael@0 | 1926 | return scriptCounts.pcCountsVector[script->pcToOffset(pc)]; |
michael@0 | 1927 | } |
michael@0 | 1928 | |
michael@0 | 1929 | jit::IonScriptCounts *getIonCounts() const { |
michael@0 | 1930 | return scriptCounts.ionCounts; |
michael@0 | 1931 | } |
michael@0 | 1932 | }; |
michael@0 | 1933 | |
michael@0 | 1934 | struct GSNCache; |
michael@0 | 1935 | |
michael@0 | 1936 | jssrcnote * |
michael@0 | 1937 | GetSrcNote(GSNCache &cache, JSScript *script, jsbytecode *pc); |
michael@0 | 1938 | |
michael@0 | 1939 | } /* namespace js */ |
michael@0 | 1940 | |
michael@0 | 1941 | extern jssrcnote * |
michael@0 | 1942 | js_GetSrcNote(JSContext *cx, JSScript *script, jsbytecode *pc); |
michael@0 | 1943 | |
michael@0 | 1944 | extern jsbytecode * |
michael@0 | 1945 | js_LineNumberToPC(JSScript *script, unsigned lineno); |
michael@0 | 1946 | |
michael@0 | 1947 | extern JS_FRIEND_API(unsigned) |
michael@0 | 1948 | js_GetScriptLineExtent(JSScript *script); |
michael@0 | 1949 | |
michael@0 | 1950 | namespace js { |
michael@0 | 1951 | |
michael@0 | 1952 | extern unsigned |
michael@0 | 1953 | PCToLineNumber(JSScript *script, jsbytecode *pc, unsigned *columnp = nullptr); |
michael@0 | 1954 | |
michael@0 | 1955 | extern unsigned |
michael@0 | 1956 | PCToLineNumber(unsigned startLine, jssrcnote *notes, jsbytecode *code, jsbytecode *pc, |
michael@0 | 1957 | unsigned *columnp = nullptr); |
michael@0 | 1958 | |
michael@0 | 1959 | /* |
michael@0 | 1960 | * This function returns the file and line number of the script currently |
michael@0 | 1961 | * executing on cx. If there is no current script executing on cx (e.g., a |
michael@0 | 1962 | * native called directly through JSAPI (e.g., by setTimeout)), nullptr and 0 |
michael@0 | 1963 | * are returned as the file and line. Additionally, this function avoids the |
michael@0 | 1964 | * full linear scan to compute line number when the caller guarantees that the |
michael@0 | 1965 | * script compilation occurs at a JSOP_EVAL/JSOP_SPREADEVAL. |
michael@0 | 1966 | */ |
michael@0 | 1967 | |
michael@0 | 1968 | enum LineOption { |
michael@0 | 1969 | CALLED_FROM_JSOP_EVAL, |
michael@0 | 1970 | NOT_CALLED_FROM_JSOP_EVAL |
michael@0 | 1971 | }; |
michael@0 | 1972 | |
michael@0 | 1973 | extern void |
michael@0 | 1974 | DescribeScriptedCallerForCompilation(JSContext *cx, MutableHandleScript maybeScript, |
michael@0 | 1975 | const char **file, unsigned *linenop, |
michael@0 | 1976 | uint32_t *pcOffset, JSPrincipals **origin, |
michael@0 | 1977 | LineOption opt = NOT_CALLED_FROM_JSOP_EVAL); |
michael@0 | 1978 | |
michael@0 | 1979 | bool |
michael@0 | 1980 | CloneFunctionScript(JSContext *cx, HandleFunction original, HandleFunction clone, |
michael@0 | 1981 | NewObjectKind newKind = GenericObject); |
michael@0 | 1982 | |
michael@0 | 1983 | /* |
michael@0 | 1984 | * JSAPI clients are allowed to leave CompileOptions.originPrincipals nullptr in |
michael@0 | 1985 | * which case the JS engine sets options.originPrincipals = origin.principals. |
michael@0 | 1986 | * This normalization step must occur before the originPrincipals get stored in |
michael@0 | 1987 | * the JSScript/ScriptSource. |
michael@0 | 1988 | */ |
michael@0 | 1989 | |
michael@0 | 1990 | static inline JSPrincipals * |
michael@0 | 1991 | NormalizeOriginPrincipals(JSPrincipals *principals, JSPrincipals *originPrincipals) |
michael@0 | 1992 | { |
michael@0 | 1993 | return originPrincipals ? originPrincipals : principals; |
michael@0 | 1994 | } |
michael@0 | 1995 | |
michael@0 | 1996 | } /* namespace js */ |
michael@0 | 1997 | |
michael@0 | 1998 | #endif /* jsscript_h */ |