1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifndef vm_SharedStencil_h
8 #define vm_SharedStencil_h
9
10 #include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_CRASH
11 #include "mozilla/Atomics.h" // mozilla::{Atomic, SequentiallyConsistent}
12 #include "mozilla/CheckedInt.h" // mozilla::CheckedInt
13 #include "mozilla/HashFunctions.h" // mozilla::HahNumber, mozilla::HashBytes
14 #include "mozilla/HashTable.h" // mozilla::HashSet
15 #include "mozilla/MemoryReporting.h" // mozilla::MallocSizeOf
16 #include "mozilla/RefPtr.h" // RefPtr
17 #include "mozilla/Span.h" // mozilla::Span
18
19 #include <stddef.h> // size_t
20 #include <stdint.h> // uint8_t, uint16_t, uint32_t
21
22 #include "frontend/SourceNotes.h" // js::SrcNote
23 #include "frontend/TypedIndex.h" // js::frontend::TypedIndex
24
25 #include "js/AllocPolicy.h" // js::SystemAllocPolicy
26 #include "js/TypeDecls.h" // JSContext,jsbytecode
27 #include "js/UniquePtr.h" // js::UniquePtr
28 #include "util/EnumFlags.h" // js::EnumFlags
29 #include "util/TrailingArray.h" // js::TrailingArray
30 #include "vm/GeneratorAndAsyncKind.h" // GeneratorKind, FunctionAsyncKind
31 #include "vm/StencilEnums.h" // js::{TryNoteKind,ImmutableScriptFlagsEnum,MutableScriptFlagsEnum}
32
33 //
34 // Data structures shared between Stencil and the VM.
35 //
36
37 namespace js {
38
39 namespace frontend {
40 class StencilXDR;
41 } // namespace frontend
42
43 // Index into gcthings array.
44 class GCThingIndexType;
45 class GCThingIndex : public frontend::TypedIndex<GCThingIndexType> {
46 // Delegate constructors;
47 using Base = frontend::TypedIndex<GCThingIndexType>;
48 using Base::Base;
49
50 public:
outermostScopeIndex()51 static constexpr GCThingIndex outermostScopeIndex() {
52 return GCThingIndex(0);
53 }
54
invalid()55 static constexpr GCThingIndex invalid() { return GCThingIndex(UINT32_MAX); }
56
next()57 GCThingIndex next() const { return GCThingIndex(index + 1); }
58 };
59
60 /*
61 * Exception handling record.
62 */
63 struct TryNote {
64 uint32_t kind_; /* one of TryNoteKind */
65 uint32_t stackDepth; /* stack depth upon exception handler entry */
66 uint32_t start; /* start of the try statement or loop relative
67 to script->code() */
68 uint32_t length; /* length of the try statement or loop */
69
TryNoteTryNote70 TryNote(uint32_t kind, uint32_t stackDepth, uint32_t start, uint32_t length)
71 : kind_(kind), stackDepth(stackDepth), start(start), length(length) {}
72
73 TryNote() = default;
74
kindTryNote75 TryNoteKind kind() const { return TryNoteKind(kind_); }
76
isLoopTryNote77 bool isLoop() const {
78 switch (kind()) {
79 case TryNoteKind::Loop:
80 case TryNoteKind::ForIn:
81 case TryNoteKind::ForOf:
82 return true;
83 case TryNoteKind::Catch:
84 case TryNoteKind::Finally:
85 case TryNoteKind::ForOfIterClose:
86 case TryNoteKind::Destructuring:
87 return false;
88 }
89 MOZ_CRASH("Unexpected try note kind");
90 }
91 };
92
93 // A block scope has a range in bytecode: it is entered at some offset, and left
94 // at some later offset. Scopes can be nested. Given an offset, the
95 // ScopeNote containing that offset whose with the highest start value
96 // indicates the block scope. The block scope list is sorted by increasing
97 // start value.
98 //
99 // It is possible to leave a scope nonlocally, for example via a "break"
100 // statement, so there may be short bytecode ranges in a block scope in which we
101 // are popping the block chain in preparation for a goto. These exits are also
102 // nested with respect to outer scopes. The scopes in these exits are indicated
103 // by the "index" field, just like any other block. If a nonlocal exit pops the
104 // last block scope, the index will be NoScopeIndex.
105 //
106 struct ScopeNote {
107 // Sentinel index for no Scope.
108 static constexpr GCThingIndex NoScopeIndex = GCThingIndex::invalid();
109
110 // Sentinel index for no ScopeNote.
111 static const uint32_t NoScopeNoteIndex = UINT32_MAX;
112
113 // Index of the js::Scope in the script's gcthings array, or NoScopeIndex if
114 // there is no block scope in this range.
115 GCThingIndex index;
116
117 // Bytecode offset at which this scope starts relative to script->code().
118 uint32_t start = 0;
119
120 // Length of bytecode span this scope covers.
121 uint32_t length = 0;
122
123 // Index of parent block scope in notes, or NoScopeNoteIndex.
124 uint32_t parent = 0;
125 };
126
127 // Range of characters in scriptSource which contains a script's source,
128 // that is, the range used by the Parser to produce a script.
129 //
130 // For most functions the fields point to the following locations.
131 //
132 // function * foo(a, b) { return a + b; }
133 // ^ ^ ^
134 // | | |
135 // | sourceStart sourceEnd
136 // | |
137 // toStringStart toStringEnd
138 //
139 // For the special case of class constructors, the spec requires us to use an
140 // alternate definition of toStringStart / toStringEnd.
141 //
142 // class C { constructor() { this.field = 42; } }
143 // ^ ^ ^ ^
144 // | | | |
145 // | sourceStart sourceEnd |
146 // | |
147 // toStringStart toStringEnd
148 //
149 // Implicit class constructors use the following definitions.
150 //
151 // class C { someMethod() { } }
152 // ^ ^
153 // | |
154 // sourceStart sourceEnd
155 // | |
156 // toStringStart toStringEnd
157 //
158 // Field initializer lambdas are internal details of the engine, but we still
159 // provide a sensible definition of these values.
160 //
161 // class C { static field = 1 }
162 // class C { field = 1 }
163 // class C { somefield }
164 // ^ ^
165 // | |
166 // sourceStart sourceEnd
167 //
168 // The non-static private class methods (including getters and setters) ALSO
169 // create a hidden initializer lambda in addition to the method itself. These
170 // lambdas are not exposed directly to script.
171 //
172 // class C { #field() { } }
173 // class C { get #field() { } }
174 // class C { async #field() { } }
175 // class C { * #field() { } }
176 // ^ ^
177 // | |
178 // sourceStart sourceEnd
179 //
180 // NOTE: These are counted in Code Units from the start of the script source.
181 //
182 // Also included in the SourceExtent is the line and column numbers of the
183 // sourceStart position. Compilation options may specify the initial line and
184 // column number.
185 //
186 // NOTE: Column number may saturate and must not be used as unique identifier.
187 struct SourceExtent {
188 SourceExtent() = default;
189
SourceExtentSourceExtent190 SourceExtent(uint32_t sourceStart, uint32_t sourceEnd, uint32_t toStringStart,
191 uint32_t toStringEnd, uint32_t lineno, uint32_t column)
192 : sourceStart(sourceStart),
193 sourceEnd(sourceEnd),
194 toStringStart(toStringStart),
195 toStringEnd(toStringEnd),
196 lineno(lineno),
197 column(column) {}
198
makeGlobalExtentSourceExtent199 static SourceExtent makeGlobalExtent(uint32_t len) {
200 return SourceExtent(0, len, 0, len, 1, 0);
201 }
202
makeGlobalExtentSourceExtent203 static SourceExtent makeGlobalExtent(uint32_t len, uint32_t lineno,
204 uint32_t column) {
205 return SourceExtent(0, len, 0, len, lineno, column);
206 }
207
208 uint32_t sourceStart = 0;
209 uint32_t sourceEnd = 0;
210 uint32_t toStringStart = 0;
211 uint32_t toStringEnd = 0;
212
213 // Line and column of |sourceStart_| position.
214 uint32_t lineno = 1; // 1-indexed.
215 uint32_t column = 0; // Count of Code Points
216 };
217
218 class ImmutableScriptFlags : public EnumFlags<ImmutableScriptFlagsEnum> {
219 public:
220 ImmutableScriptFlags() = default;
221
ImmutableScriptFlags(FieldType rawFlags)222 explicit ImmutableScriptFlags(FieldType rawFlags) : EnumFlags(rawFlags) {}
223
FieldType()224 operator FieldType() const { return flags_; }
225 };
226
227 class MutableScriptFlags : public EnumFlags<MutableScriptFlagsEnum> {
228 public:
229 MutableScriptFlags() = default;
230
231 MutableScriptFlags& operator&=(const FieldType rhs) {
232 flags_ &= rhs;
233 return *this;
234 }
235
236 MutableScriptFlags& operator|=(const FieldType rhs) {
237 flags_ |= rhs;
238 return *this;
239 }
240
FieldType()241 operator FieldType() const { return flags_; }
242 };
243
244 #define GENERIC_FLAGS_READ_ONLY(Field, Enum) \
245 [[nodiscard]] bool hasFlag(Enum flag) const { return Field.hasFlag(flag); }
246
247 #define GENERIC_FLAGS_READ_WRITE(Field, Enum) \
248 [[nodiscard]] bool hasFlag(Enum flag) const { return Field.hasFlag(flag); } \
249 void setFlag(Enum flag, bool b = true) { Field.setFlag(flag, b); } \
250 void clearFlag(Enum flag) { Field.clearFlag(flag); }
251
252 #define GENERIC_FLAG_GETTER(enumName, lowerName, name) \
253 bool lowerName() const { return hasFlag(enumName::name); }
254
255 #define GENERIC_FLAG_GETTER_SETTER(enumName, lowerName, name) \
256 GENERIC_FLAG_GETTER(enumName, lowerName, name) \
257 void set##name() { setFlag(enumName::name); } \
258 void set##name(bool b) { setFlag(enumName::name, b); } \
259 void clear##name() { clearFlag(enumName::name); }
260
261 #define IMMUTABLE_SCRIPT_FLAGS_WITH_ACCESSORS(_) \
262 _(ImmutableFlags, isForEval, IsForEval) \
263 _(ImmutableFlags, isModule, IsModule) \
264 _(ImmutableFlags, isFunction, IsFunction) \
265 _(ImmutableFlags, selfHosted, SelfHosted) \
266 _(ImmutableFlags, forceStrict, ForceStrict) \
267 _(ImmutableFlags, hasNonSyntacticScope, HasNonSyntacticScope) \
268 _(ImmutableFlags, noScriptRval, NoScriptRval) \
269 _(ImmutableFlags, treatAsRunOnce, TreatAsRunOnce) \
270 _(ImmutableFlags, strict, Strict) \
271 _(ImmutableFlags, hasModuleGoal, HasModuleGoal) \
272 _(ImmutableFlags, hasInnerFunctions, HasInnerFunctions) \
273 _(ImmutableFlags, hasDirectEval, HasDirectEval) \
274 _(ImmutableFlags, bindingsAccessedDynamically, BindingsAccessedDynamically) \
275 _(ImmutableFlags, hasCallSiteObj, HasCallSiteObj) \
276 _(ImmutableFlags, isAsync, IsAsync) \
277 _(ImmutableFlags, isGenerator, IsGenerator) \
278 _(ImmutableFlags, funHasExtensibleScope, FunHasExtensibleScope) \
279 _(ImmutableFlags, functionHasThisBinding, FunctionHasThisBinding) \
280 _(ImmutableFlags, needsHomeObject, NeedsHomeObject) \
281 _(ImmutableFlags, isDerivedClassConstructor, IsDerivedClassConstructor) \
282 _(ImmutableFlags, isSyntheticFunction, IsSyntheticFunction) \
283 _(ImmutableFlags, useMemberInitializers, UseMemberInitializers) \
284 _(ImmutableFlags, hasRest, HasRest) \
285 _(ImmutableFlags, needsFunctionEnvironmentObjects, \
286 NeedsFunctionEnvironmentObjects) \
287 _(ImmutableFlags, functionHasExtraBodyVarScope, \
288 FunctionHasExtraBodyVarScope) \
289 _(ImmutableFlags, shouldDeclareArguments, ShouldDeclareArguments) \
290 _(ImmutableFlags, needsArgsObj, NeedsArgsObj) \
291 _(ImmutableFlags, hasMappedArgsObj, HasMappedArgsObj) \
292 _(ImmutableFlags, isInlinableLargeFunction, IsInlinableLargeFunction) \
293 \
294 GeneratorKind generatorKind() const { \
295 return isGenerator() ? GeneratorKind::Generator \
296 : GeneratorKind::NotGenerator; \
297 } \
298 \
299 FunctionAsyncKind asyncKind() const { \
300 return isAsync() ? FunctionAsyncKind::AsyncFunction \
301 : FunctionAsyncKind::SyncFunction; \
302 } \
303 \
304 bool isRelazifiable() const { \
305 /* \
306 ** A script may not be relazifiable if parts of it can be entrained in \
307 ** interesting ways: \
308 ** - Scripts with inner-functions or direct-eval (which can add \
309 ** inner-functions) should not be relazified as their Scopes may be \
310 ** part of another scope-chain. \
311 ** - Generators and async functions may be re-entered in complex ways so \
312 ** don't discard bytecode. The JIT resume code assumes this. \
313 ** - Functions with template literals must always return the same object \
314 ** instance so must not discard it by relazifying. \
315 */ \
316 return !hasInnerFunctions() && !hasDirectEval() && !isGenerator() && \
317 !isAsync() && !hasCallSiteObj(); \
318 }
319
320 #define RO_IMMUTABLE_SCRIPT_FLAGS(Field) \
321 using ImmutableFlags = ImmutableScriptFlagsEnum; \
322 \
323 GENERIC_FLAGS_READ_ONLY(Field, ImmutableFlags) \
324 IMMUTABLE_SCRIPT_FLAGS_WITH_ACCESSORS(GENERIC_FLAG_GETTER)
325
326 #define MUTABLE_SCRIPT_FLAGS_WITH_ACCESSORS(_) \
327 _(MutableFlags, hasRunOnce, HasRunOnce) \
328 _(MutableFlags, hasScriptCounts, HasScriptCounts) \
329 _(MutableFlags, hasDebugScript, HasDebugScript) \
330 _(MutableFlags, allowRelazify, AllowRelazify) \
331 _(MutableFlags, spewEnabled, SpewEnabled) \
332 _(MutableFlags, needsFinalWarmUpCount, NeedsFinalWarmUpCount) \
333 _(MutableFlags, failedBoundsCheck, FailedBoundsCheck) \
334 _(MutableFlags, hadLICMInvalidation, HadLICMInvalidation) \
335 _(MutableFlags, hadReorderingBailout, HadReorderingBailout) \
336 _(MutableFlags, hadEagerTruncationBailout, HadEagerTruncationBailout) \
337 _(MutableFlags, hadUnboxFoldingBailout, HadUnboxFoldingBailout) \
338 _(MutableFlags, baselineDisabled, BaselineDisabled) \
339 _(MutableFlags, ionDisabled, IonDisabled) \
340 _(MutableFlags, uninlineable, Uninlineable) \
341 _(MutableFlags, failedLexicalCheck, FailedLexicalCheck) \
342 _(MutableFlags, hadSpeculativePhiBailout, HadSpeculativePhiBailout)
343
344 #define RW_MUTABLE_SCRIPT_FLAGS(Field) \
345 using MutableFlags = MutableScriptFlagsEnum; \
346 \
347 GENERIC_FLAGS_READ_WRITE(Field, MutableFlags) \
348 MUTABLE_SCRIPT_FLAGS_WITH_ACCESSORS(GENERIC_FLAG_GETTER_SETTER)
349
350 // [SMDOC] JSScript data layout (immutable)
351 //
352 // ImmutableScriptData stores variable-length script data that may be shared
353 // between scripts with the same bytecode, even across different GC Zones.
354 // Abstractly this structure consists of multiple (optional) arrays that are
355 // exposed as mozilla::Span<T>. These arrays exist in a single heap allocation.
356 //
357 // Under the hood, ImmutableScriptData is a fixed-size header class followed
358 // the various array bodies interleaved with metadata to compactly encode the
359 // bounds. These arrays have varying requirements for alignment, performance,
360 // and jit-friendliness which leads to the complex indexing system below.
361 //
362 // Note: The '----' separators are for readability only.
363 //
364 // ----
365 // <ImmutableScriptData itself>
366 // ----
367 // (REQUIRED) Flags structure
368 // (REQUIRED) Array of jsbytecode constituting code()
369 // (REQUIRED) Array of SrcNote constituting notes()
370 // ----
371 // (OPTIONAL) Array of uint32_t optional-offsets
372 // optArrayOffset:
373 // ----
374 // L0:
375 // (OPTIONAL) Array of uint32_t constituting resumeOffsets()
376 // L1:
377 // (OPTIONAL) Array of ScopeNote constituting scopeNotes()
378 // L2:
379 // (OPTIONAL) Array of TryNote constituting tryNotes()
380 // L3:
381 // ----
382 //
383 // NOTE: The notes() array must have been null-padded such that
384 // flags/code/notes together have uint32_t alignment.
385 //
386 // The labels shown are recorded as byte-offsets relative to 'this'. This is to
387 // reduce memory as well as make ImmutableScriptData easier to share across
388 // processes.
389 //
390 // The L0/L1/L2/L3 labels indicate the start and end of the optional arrays.
391 // Some of these labels may refer to the same location if the array between
392 // them is empty. Each unique label position has an offset stored in the
393 // optional-offsets table. Note that we also avoid entries for labels that
394 // match 'optArrayOffset'. This saves memory when arrays are empty.
395 //
396 // The flags() data indicates (for each optional array) which entry from the
397 // optional-offsets table marks the *end* of array. The array starts where the
398 // previous array ends (with the first array beginning at 'optArrayOffset').
399 // The optional-offset table is addressed at negative indices from
400 // 'optArrayOffset'.
401 //
402 // In general, the length of each array is computed from subtracting the start
403 // offset of the array from the start offset of the subsequent array. The
404 // notable exception is that bytecode length is stored explicitly.
alignas(uint32_t)405 class alignas(uint32_t) ImmutableScriptData final : public TrailingArray {
406 private:
407 Offset optArrayOffset_ = 0;
408
409 // Length of bytecode
410 uint32_t codeLength_ = 0;
411
412 public:
413 // Offset of main entry point from code, after predef'ing prologue.
414 uint32_t mainOffset = 0;
415
416 // Fixed frame slots.
417 uint32_t nfixed = 0;
418
419 // Slots plus maximum stack depth.
420 uint32_t nslots = 0;
421
422 // Index into the gcthings array of the body scope.
423 GCThingIndex bodyScopeIndex;
424
425 // Number of IC entries to allocate in JitScript for Baseline ICs.
426 uint32_t numICEntries = 0;
427
428 // ES6 function length.
429 uint16_t funLength = 0;
430
431 // NOTE: The raw bytes of this structure are used for hashing so use explicit
432 // padding values as needed for predicatable results across compilers.
433 uint16_t padding = 0;
434
435 private:
436 struct Flags {
437 uint8_t resumeOffsetsEndIndex : 2;
438 uint8_t scopeNotesEndIndex : 2;
439 uint8_t tryNotesEndIndex : 2;
440 uint8_t _unused : 2;
441 };
442 static_assert(sizeof(Flags) == sizeof(uint8_t),
443 "Structure packing is broken");
444
445 // Offsets (in bytes) from 'this' to each component array. The delta between
446 // each offset and the next offset is the size of each array and is defined
447 // even if an array is empty.
448 Offset flagOffset() const { return offsetOfCode() - sizeof(Flags); }
449 Offset codeOffset() const { return offsetOfCode(); }
450 Offset noteOffset() const { return offsetOfCode() + codeLength_; }
451 Offset optionalOffsetsOffset() const {
452 // Determine the location to beginning of optional-offsets array by looking
453 // at index for try-notes.
454 //
455 // optionalOffsetsOffset():
456 // (OPTIONAL) tryNotesEndOffset
457 // (OPTIONAL) scopeNotesEndOffset
458 // (OPTIONAL) resumeOffsetsEndOffset
459 // optArrayOffset_:
460 // ....
461 unsigned numOffsets = flags().tryNotesEndIndex;
462 MOZ_ASSERT(numOffsets >= flags().scopeNotesEndIndex);
463 MOZ_ASSERT(numOffsets >= flags().resumeOffsetsEndIndex);
464
465 return optArrayOffset_ - (numOffsets * sizeof(Offset));
466 }
467 Offset resumeOffsetsOffset() const { return optArrayOffset_; }
468 Offset scopeNotesOffset() const {
469 return getOptionalOffset(flags().resumeOffsetsEndIndex);
470 }
471 Offset tryNotesOffset() const {
472 return getOptionalOffset(flags().scopeNotesEndIndex);
473 }
474 Offset endOffset() const {
475 return getOptionalOffset(flags().tryNotesEndIndex);
476 }
477
478 void initOptionalArrays(Offset* cursor, uint32_t numResumeOffsets,
479 uint32_t numScopeNotes, uint32_t numTryNotes);
480
481 // Initialize to GC-safe state
482 ImmutableScriptData(uint32_t codeLength, uint32_t noteLength,
483 uint32_t numResumeOffsets, uint32_t numScopeNotes,
484 uint32_t numTryNotes);
485
486 void setOptionalOffset(int index, Offset offset) {
487 MOZ_ASSERT(index > 0);
488 MOZ_ASSERT(offset != optArrayOffset_, "Do not store implicit offset");
489 offsetToPointer<Offset>(optArrayOffset_)[-index] = offset;
490 }
491 Offset getOptionalOffset(int index) const {
492 // The index 0 represents (implicitly) the offset 'optArrayOffset_'.
493 if (index == 0) {
494 return optArrayOffset_;
495 }
496
497 ImmutableScriptData* this_ = const_cast<ImmutableScriptData*>(this);
498 return this_->offsetToPointer<Offset>(optArrayOffset_)[-index];
499 }
500
501 public:
502 static js::UniquePtr<ImmutableScriptData> new_(
503 JSContext* cx, uint32_t mainOffset, uint32_t nfixed, uint32_t nslots,
504 GCThingIndex bodyScopeIndex, uint32_t numICEntries, bool isFunction,
505 uint16_t funLength, mozilla::Span<const jsbytecode> code,
506 mozilla::Span<const SrcNote> notes,
507 mozilla::Span<const uint32_t> resumeOffsets,
508 mozilla::Span<const ScopeNote> scopeNotes,
509 mozilla::Span<const TryNote> tryNotes);
510
511 static js::UniquePtr<ImmutableScriptData> new_(
512 JSContext* cx, uint32_t codeLength, uint32_t noteLength,
513 uint32_t numResumeOffsets, uint32_t numScopeNotes, uint32_t numTryNotes);
514
515 static js::UniquePtr<ImmutableScriptData> new_(JSContext* cx,
516 uint32_t totalSize);
517
518 uint32_t computedSize();
519
520 private:
521 static mozilla::CheckedInt<uint32_t> sizeFor(uint32_t codeLength,
522 uint32_t noteLength,
523 uint32_t numResumeOffsets,
524 uint32_t numScopeNotes,
525 uint32_t numTryNotes);
526
527 public:
528 // The code() and note() arrays together maintain an target alignment by
529 // padding the source notes with null. This allows arrays with stricter
530 // alignment requirements to follow them.
531 static constexpr size_t CodeNoteAlign = sizeof(uint32_t);
532
533 // Compute number of null notes to pad out source notes with.
534 static uint32_t ComputeNotePadding(uint32_t codeLength, uint32_t noteLength) {
535 uint32_t flagLength = sizeof(Flags);
536 uint32_t nullLength =
537 CodeNoteAlign - (flagLength + codeLength + noteLength) % CodeNoteAlign;
538
539 // The source notes must have at least one null-terminator.
540 MOZ_ASSERT(nullLength >= 1);
541
542 return nullLength;
543 }
544
545 // Span over all raw bytes in this struct and its trailing arrays.
546 mozilla::Span<const uint8_t> immutableData() const {
547 size_t allocSize = endOffset();
548 return mozilla::Span{reinterpret_cast<const uint8_t*>(this), allocSize};
549 }
550
551 private:
552 Flags& flagsRef() { return *offsetToPointer<Flags>(flagOffset()); }
553 const Flags& flags() const {
554 return const_cast<ImmutableScriptData*>(this)->flagsRef();
555 }
556
557 public:
558 uint32_t codeLength() const { return codeLength_; }
559 jsbytecode* code() { return offsetToPointer<jsbytecode>(codeOffset()); }
560 mozilla::Span<jsbytecode> codeSpan() { return {code(), codeLength()}; }
561
562 uint32_t noteLength() const {
563 return numElements<SrcNote>(noteOffset(), optionalOffsetsOffset());
564 }
565 SrcNote* notes() { return offsetToPointer<SrcNote>(noteOffset()); }
566 mozilla::Span<SrcNote> notesSpan() { return {notes(), noteLength()}; }
567
568 mozilla::Span<uint32_t> resumeOffsets() {
569 return mozilla::Span{offsetToPointer<uint32_t>(resumeOffsetsOffset()),
570 offsetToPointer<uint32_t>(scopeNotesOffset())};
571 }
572 mozilla::Span<ScopeNote> scopeNotes() {
573 return mozilla::Span{offsetToPointer<ScopeNote>(scopeNotesOffset()),
574 offsetToPointer<ScopeNote>(tryNotesOffset())};
575 }
576 mozilla::Span<TryNote> tryNotes() {
577 return mozilla::Span{offsetToPointer<TryNote>(tryNotesOffset()),
578 offsetToPointer<TryNote>(endOffset())};
579 }
580
581 // Expose offsets to the JITs.
582 static constexpr size_t offsetOfCode() {
583 return sizeof(ImmutableScriptData) + sizeof(Flags);
584 }
585 static constexpr size_t offsetOfResumeOffsetsOffset() {
586 // Resume-offsets are the first optional array if they exist. Locate the
587 // array with the 'optArrayOffset_' field.
588 static_assert(sizeof(Offset) == sizeof(uint32_t),
589 "JIT expect Offset to be uint32_t");
590 return offsetof(ImmutableScriptData, optArrayOffset_);
591 }
592 static constexpr size_t offsetOfNfixed() {
593 return offsetof(ImmutableScriptData, nfixed);
594 }
595 static constexpr size_t offsetOfNslots() {
596 return offsetof(ImmutableScriptData, nslots);
597 }
598 static constexpr size_t offsetOfFunLength() {
599 return offsetof(ImmutableScriptData, funLength);
600 }
601
602 // ImmutableScriptData has trailing data so isn't copyable or movable.
603 ImmutableScriptData(const ImmutableScriptData&) = delete;
604 ImmutableScriptData& operator=(const ImmutableScriptData&) = delete;
605 };
606
607 // Wrapper type for ImmutableScriptData to allow sharing across a JSRuntime.
608 //
609 // Note: This is distinct from ImmutableScriptData because it contains a mutable
610 // ref-count while the ImmutableScriptData may live in read-only memory.
611 //
612 // Note: This is *not* directly inlined into the SharedImmutableScriptDataTable
613 // because scripts point directly to object and table resizing moves
614 // entries. This allows for fast finalization by decrementing the
615 // ref-count directly without doing a hash-table lookup.
616 class SharedImmutableScriptData {
617 // This class is reference counted as follows: each pointer from a JSScript
618 // counts as one reference plus there may be one reference from the shared
619 // script data table.
620 mozilla::Atomic<uint32_t, mozilla::SequentiallyConsistent> refCount_ = {};
621
622 public:
623 bool isExternal = false;
624
625 private:
626 ImmutableScriptData* isd_ = nullptr;
627
628 // End of fields.
629
630 friend class ::JSScript;
631 friend class js::frontend::StencilXDR;
632
633 public:
634 SharedImmutableScriptData() = default;
635
~SharedImmutableScriptData()636 ~SharedImmutableScriptData() { reset(); }
637
638 private:
reset()639 void reset() {
640 if (isd_ && !isExternal) {
641 js_delete(isd_);
642 }
643 isd_ = nullptr;
644 }
645
646 public:
647 // Hash over the contents of SharedImmutableScriptData and its
648 // ImmutableScriptData.
649 struct Hasher;
650
refCount()651 uint32_t refCount() const { return refCount_; }
AddRef()652 void AddRef() { refCount_++; }
Release()653 void Release() {
654 MOZ_ASSERT(refCount_ != 0);
655 uint32_t remain = --refCount_;
656 if (remain == 0) {
657 reset();
658 js_free(this);
659 }
660 }
661
offsetOfISD()662 static constexpr size_t offsetOfISD() {
663 return offsetof(SharedImmutableScriptData, isd_);
664 }
665
666 private:
667 static SharedImmutableScriptData* create(JSContext* cx);
668
669 public:
670 static SharedImmutableScriptData* createWith(
671 JSContext* cx, js::UniquePtr<ImmutableScriptData>&& isd);
672
sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)673 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
674 size_t isdSize = isExternal ? 0 : mallocSizeOf(isd_);
675 return mallocSizeOf(this) + isdSize;
676 }
677
678 // SharedImmutableScriptData has trailing data so isn't copyable or movable.
679 SharedImmutableScriptData(const SharedImmutableScriptData&) = delete;
680 SharedImmutableScriptData& operator=(const SharedImmutableScriptData&) =
681 delete;
682
683 static bool shareScriptData(JSContext* cx,
684 RefPtr<SharedImmutableScriptData>& sisd);
685
immutableDataLength()686 size_t immutableDataLength() const { return isd_->immutableData().Length(); }
nfixed()687 uint32_t nfixed() const { return isd_->nfixed; }
688
get()689 ImmutableScriptData* get() { return isd_; }
690
setOwn(js::UniquePtr<ImmutableScriptData> && isd)691 void setOwn(js::UniquePtr<ImmutableScriptData>&& isd) {
692 MOZ_ASSERT(!isd_);
693 isd_ = isd.release();
694 isExternal = false;
695 }
696
setExternal(ImmutableScriptData * isd)697 void setExternal(ImmutableScriptData* isd) {
698 MOZ_ASSERT(!isd_);
699 isd_ = isd;
700 isExternal = true;
701 }
702 };
703
704 // Matches SharedImmutableScriptData objects that have the same atoms as well as
705 // contain the same bytes in their ImmutableScriptData.
706 struct SharedImmutableScriptData::Hasher {
707 using Lookup = RefPtr<SharedImmutableScriptData>;
708
hashHasher709 static mozilla::HashNumber hash(const Lookup& l) {
710 mozilla::Span<const uint8_t> immutableData = l->isd_->immutableData();
711 return mozilla::HashBytes(immutableData.data(), immutableData.size());
712 }
713
matchHasher714 static bool match(SharedImmutableScriptData* entry, const Lookup& lookup) {
715 return (entry->isd_->immutableData() == lookup->isd_->immutableData());
716 }
717 };
718
719 using SharedImmutableScriptDataTable =
720 mozilla::HashSet<SharedImmutableScriptData*,
721 SharedImmutableScriptData::Hasher, SystemAllocPolicy>;
722
723 struct MemberInitializers {
724 static constexpr size_t NumBits = 31;
725 static constexpr uint32_t MaxInitializers = BitMask(NumBits);
726
727 #ifdef DEBUG
728 bool valid = false;
729 #endif
730
731 bool hasPrivateBrand : 1;
732
733 // This struct will eventually have a vector of constant values for optimizing
734 // field initializers.
735 uint32_t numMemberInitializers : NumBits;
736
MemberInitializersMemberInitializers737 MemberInitializers(bool hasPrivateBrand, uint32_t numMemberInitializers)
738 :
739 #ifdef DEBUG
740 valid(true),
741 #endif
742 hasPrivateBrand(hasPrivateBrand),
743 numMemberInitializers(numMemberInitializers) {
744 MOZ_ASSERT(
745 this->numMemberInitializers == numMemberInitializers,
746 "numMemberInitializers should easily fit in the 31-bit bitfield");
747 }
748
InvalidMemberInitializers749 static MemberInitializers Invalid() { return MemberInitializers(); }
750
751 // Singleton to use for class constructors that do not have to initialize any
752 // fields. This is used when we elide the trivial data but still need a valid
753 // set to stop scope walking.
EmptyMemberInitializers754 static const MemberInitializers& Empty() {
755 static const MemberInitializers zeroInitializers(false, 0);
756 return zeroInitializers;
757 }
758
serializeMemberInitializers759 uint32_t serialize() const {
760 return (hasPrivateBrand << NumBits) | numMemberInitializers;
761 }
762
deserializeMemberInitializers763 static MemberInitializers deserialize(uint32_t bits) {
764 return MemberInitializers((bits & Bit(NumBits)) != 0,
765 bits & BitMask(NumBits));
766 }
767
768 private:
MemberInitializersMemberInitializers769 MemberInitializers()
770 :
771 #ifdef DEBUG
772 valid(false),
773 #endif
774 hasPrivateBrand(false),
775 numMemberInitializers(0) {
776 }
777 };
778
779 } // namespace js
780
781 #endif /* vm_SharedStencil_h */
782