1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sw=2 et tw=0 ft=c:
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8 #ifndef frontend_ObjLiteral_h
9 #define frontend_ObjLiteral_h
10
11 #include "mozilla/BloomFilter.h" // mozilla::BitBloomFilter
12 #include "mozilla/EnumSet.h"
13 #include "mozilla/Span.h"
14
15 #include "frontend/ParserAtom.h" // ParserAtomsTable, TaggedParserAtomIndex
16 #include "js/AllocPolicy.h"
17 #include "js/GCPolicyAPI.h"
18 #include "js/Value.h"
19 #include "js/Vector.h"
20 #include "util/EnumFlags.h"
21
22 /*
23 * [SMDOC] ObjLiteral (Object Literal) Handling
24 * ============================================
25 *
26 * The `ObjLiteral*` family of classes defines an infastructure to handle
27 * object literals as they are encountered at parse time and translate them
28 * into objects that are attached to the bytecode.
29 *
30 * The object-literal "instructions", whose opcodes are defined in
31 * `ObjLiteralOpcode` below, each specify one key (atom property name, or
32 * numeric index) and one value. An `ObjLiteralWriter` buffers a linear
33 * sequence of such instructions, along with a side-table of atom references.
34 * The writer stores a compact binary format that is then interpreted by the
35 * `ObjLiteralReader` to construct an object according to the instructions.
36 *
37 * This may seem like an odd dance: create an intermediate data structure that
38 * specifies key/value pairs, then later build the object. Why not just do so
39 * directly, as we parse? In fact, we used to do this. However, for several
40 * good reasons, we want to avoid allocating or touching GC objects at all
41 * *during* the parse. We thus use a sequence of ObjLiteral instructions as an
42 * intermediate data structure to carry object literal contents from parse to
43 * the time at which we *can* allocate objects.
44 *
45 * (The original intent was to allow for ObjLiteral instructions to actually be
46 * invoked by a new JS opcode, JSOp::ObjLiteral, thus replacing the more
47 * general opcode sequences sometimes generated to fill in objects and removing
48 * the need to attach actual objects to JSOp::Object or JSOp::NewObject.
49 * However, this was far too invasive and led to performance regressions, so
50 * currently ObjLiteral only carries literals as far as the end of the parse
51 * pipeline, when all GC things are allocated.)
52 *
53 * ObjLiteral data structures are used to represent object literals whenever
54 * they are "compatible". See
55 * BytecodeEmitter::isPropertyListObjLiteralCompatible for the precise
56 * conditions; in brief, we can represent object literals with "primitive"
57 * (numeric, boolean, string, null/undefined) values, and "normal"
58 * (non-computed) object names. We can also represent arrays with the same
59 * value restrictions. We cannot represent nested objects. We use ObjLiteral in
60 * two different ways:
61 *
62 * - To build a template object, when we can support the properties but not the
63 * keys.
64 * - To build the actual result object, when we support the properties and the
65 * keys and this is a JSOp::Object case (see below).
66 *
67 * Design and Performance Considerations
68 * -------------------------------------
69 *
70 * As a brief overview, there are a number of opcodes that allocate objects:
71 *
72 * - JSOp::NewInit allocates a new empty `{}` object.
73 *
74 * - JSOp::NewObject, with an object as an argument (held by the script data
75 * side-tables), allocates a new object with `undefined` property values but
76 * with a defined set of properties. The given object is used as a
77 * *template*.
78 *
79 * - JSOp::Object, with an object as argument, instructs the runtime to
80 * literally return the object argument as the result. This is thus only an
81 * "allocation" in the sense that the object was originally allocated when
82 * the script data / bytecode was created. It is only used when we know for
83 * sure that the script, and this program point within the script, will run
84 * *once*. (See the `treatAsRunOnce` flag on JSScript.)
85 *
86 * An operation occurs in a "singleton context", according to the parser, if it
87 * will only ever execute once. In particular, this happens when (i) the script
88 * is a "run-once" script, which is usually the case for e.g. top-level scripts
89 * of web-pages (they run on page load, but no function or handle wraps or
90 * refers to the script so it can't be invoked again), and (ii) the operation
91 * itself is not within a loop or function in that run-once script.
92 *
93 * When we encounter an object literal, we decide which opcode to use, and we
94 * construct the ObjLiteral and the bytecode using its result appropriately:
95 *
96 * - If in a singleton context, and if we support the values, we use
97 * JSOp::Object and we build the ObjLiteral instructions with values.
98 * - Otherwise, if we support the keys but not the values, or if we are not
99 * in a singleton context, we use JSOp::NewObject. In this case, the initial
100 * opcode only creates an object with empty values, so BytecodeEmitter then
101 * generates bytecode to set the values appropriately.
102 * - Otherwise, we generate JSOp::NewInit and bytecode to add properties one at
103 * a time. This will always work, but is the slowest and least
104 * memory-efficient option.
105 */
106
107 namespace js {
108
109 class LifoAlloc;
110 class JSONPrinter;
111
112 namespace frontend {
113 struct CompilationAtomCache;
114 struct CompilationStencil;
115 class StencilXDR;
116 } // namespace frontend
117
118 // Object-literal instruction opcodes. An object literal is constructed by a
119 // straight-line sequence of these ops, each adding one property to the
120 // object.
121 enum class ObjLiteralOpcode : uint8_t {
122 INVALID = 0,
123
124 ConstValue = 1, // numeric types only.
125 ConstAtom = 2,
126 Null = 3,
127 Undefined = 4,
128 True = 5,
129 False = 6,
130
131 MAX = False,
132 };
133
134 // Flags that are associated with a sequence of object-literal instructions.
135 // (These become bitflags by wrapping with EnumSet below.)
136 enum class ObjLiteralFlag : uint8_t {
137 // If set, this object is an array.
138 Array = 1 << 0,
139
140 // If set, this is an object literal in a singleton context and property
141 // values are included. See also JSOp::Object.
142 Singleton = 1 << 1,
143
144 // If set, this object contains index property, or duplicate non-index
145 // property.
146 // This flag is valid only if Array flag isn't set.
147 HasIndexOrDuplicatePropName = 1 << 2,
148 };
149
150 using ObjLiteralFlags = EnumFlags<ObjLiteralFlag>;
151
ObjLiteralOpcodeHasValueArg(ObjLiteralOpcode op)152 inline bool ObjLiteralOpcodeHasValueArg(ObjLiteralOpcode op) {
153 return op == ObjLiteralOpcode::ConstValue;
154 }
155
ObjLiteralOpcodeHasAtomArg(ObjLiteralOpcode op)156 inline bool ObjLiteralOpcodeHasAtomArg(ObjLiteralOpcode op) {
157 return op == ObjLiteralOpcode::ConstAtom;
158 }
159
160 struct ObjLiteralReaderBase;
161
162 // Property name (as TaggedParserAtomIndex) or an integer index. Only used for
163 // object-type literals; array literals do not require the index (the sequence
164 // is always dense, with no holes, so the index is implicit). For the latter
165 // case, we have a `None` placeholder.
166 struct ObjLiteralKey {
167 private:
168 uint32_t value_;
169
170 enum ObjLiteralKeyType {
171 None,
172 AtomIndex,
173 ArrayIndex,
174 };
175
176 ObjLiteralKeyType type_;
177
ObjLiteralKeyObjLiteralKey178 ObjLiteralKey(uint32_t value, ObjLiteralKeyType ty)
179 : value_(value), type_(ty) {}
180
181 public:
ObjLiteralKeyObjLiteralKey182 ObjLiteralKey() : ObjLiteralKey(0, None) {}
ObjLiteralKeyObjLiteralKey183 ObjLiteralKey(uint32_t value, bool isArrayIndex)
184 : ObjLiteralKey(value, isArrayIndex ? ArrayIndex : AtomIndex) {}
185 ObjLiteralKey(const ObjLiteralKey& other) = default;
186
fromPropNameObjLiteralKey187 static ObjLiteralKey fromPropName(frontend::TaggedParserAtomIndex atomIndex) {
188 return ObjLiteralKey(atomIndex.rawData(), false);
189 }
fromArrayIndexObjLiteralKey190 static ObjLiteralKey fromArrayIndex(uint32_t index) {
191 return ObjLiteralKey(index, true);
192 }
noneObjLiteralKey193 static ObjLiteralKey none() { return ObjLiteralKey(); }
194
isNoneObjLiteralKey195 bool isNone() const { return type_ == None; }
isAtomIndexObjLiteralKey196 bool isAtomIndex() const { return type_ == AtomIndex; }
isArrayIndexObjLiteralKey197 bool isArrayIndex() const { return type_ == ArrayIndex; }
198
getAtomIndexObjLiteralKey199 frontend::TaggedParserAtomIndex getAtomIndex() const {
200 MOZ_ASSERT(isAtomIndex());
201 return frontend::TaggedParserAtomIndex::fromRaw(value_);
202 }
getArrayIndexObjLiteralKey203 uint32_t getArrayIndex() const {
204 MOZ_ASSERT(isArrayIndex());
205 return value_;
206 }
207
rawIndexObjLiteralKey208 uint32_t rawIndex() const { return value_; }
209 };
210
211 struct ObjLiteralWriterBase {
212 protected:
213 friend struct ObjLiteralReaderBase; // for access to mask and shift.
214 static const uint32_t ATOM_INDEX_MASK = 0x7fffffff;
215 // If set, the atom index field is an array index, not an atom index.
216 static const uint32_t INDEXED_PROP = 0x80000000;
217
218 public:
219 using CodeVector = Vector<uint8_t, 64, js::SystemAllocPolicy>;
220
221 protected:
222 CodeVector code_;
223
224 public:
225 ObjLiteralWriterBase() = default;
226
curOffsetObjLiteralWriterBase227 uint32_t curOffset() const { return code_.length(); }
228
229 private:
pushByteObjLiteralWriterBase230 [[nodiscard]] bool pushByte(JSContext* cx, uint8_t data) {
231 if (!code_.append(data)) {
232 js::ReportOutOfMemory(cx);
233 return false;
234 }
235 return true;
236 }
237
prepareBytesObjLiteralWriterBase238 [[nodiscard]] bool prepareBytes(JSContext* cx, size_t len, uint8_t** p) {
239 size_t offset = code_.length();
240 if (!code_.growByUninitialized(len)) {
241 js::ReportOutOfMemory(cx);
242 return false;
243 }
244 *p = &code_[offset];
245 return true;
246 }
247
248 template <typename T>
pushRawDataObjLiteralWriterBase249 [[nodiscard]] bool pushRawData(JSContext* cx, T data) {
250 uint8_t* p = nullptr;
251 if (!prepareBytes(cx, sizeof(T), &p)) {
252 return false;
253 }
254 memcpy(p, &data, sizeof(T));
255 return true;
256 }
257
258 protected:
pushOpAndNameObjLiteralWriterBase259 [[nodiscard]] bool pushOpAndName(JSContext* cx, ObjLiteralOpcode op,
260 ObjLiteralKey key) {
261 uint8_t opdata = static_cast<uint8_t>(op);
262 uint32_t data = key.rawIndex() | (key.isArrayIndex() ? INDEXED_PROP : 0);
263 return pushByte(cx, opdata) && pushRawData(cx, data);
264 }
265
pushValueArgObjLiteralWriterBase266 [[nodiscard]] bool pushValueArg(JSContext* cx, const JS::Value& value) {
267 MOZ_ASSERT(value.isNumber() || value.isNullOrUndefined() ||
268 value.isBoolean());
269 uint64_t data = value.asRawBits();
270 return pushRawData(cx, data);
271 }
272
pushAtomArgObjLiteralWriterBase273 [[nodiscard]] bool pushAtomArg(JSContext* cx,
274 frontend::TaggedParserAtomIndex atomIndex) {
275 return pushRawData(cx, atomIndex.rawData());
276 }
277 };
278
279 // An object-literal instruction writer. This class, held by the bytecode
280 // emitter, keeps a sequence of object-literal instructions emitted as object
281 // literal expressions are parsed. It allows the user to 'begin' and 'end'
282 // straight-line sequences, returning the offsets for this range of instructions
283 // within the writer.
284 struct ObjLiteralWriter : private ObjLiteralWriterBase {
285 public:
286 ObjLiteralWriter() = default;
287
clearObjLiteralWriter288 void clear() { code_.clear(); }
289
290 using CodeVector = typename ObjLiteralWriterBase::CodeVector;
291
292 bool checkForDuplicatedNames(JSContext* cx);
getCodeObjLiteralWriter293 mozilla::Span<const uint8_t> getCode() const { return code_; }
getFlagsObjLiteralWriter294 ObjLiteralFlags getFlags() const { return flags_; }
getPropertyCountObjLiteralWriter295 uint32_t getPropertyCount() const { return propertyCount_; }
296
beginObjectObjLiteralWriter297 void beginObject(ObjLiteralFlags flags) { flags_ = flags; }
setPropNameObjLiteralWriter298 bool setPropName(JSContext* cx, frontend::ParserAtomsTable& parserAtoms,
299 const frontend::TaggedParserAtomIndex propName) {
300 // Only valid in object-mode.
301 setPropNameNoDuplicateCheck(parserAtoms, propName);
302
303 if (flags_.hasFlag(ObjLiteralFlag::HasIndexOrDuplicatePropName)) {
304 return true;
305 }
306
307 // OK to early return if we've already discovered a potential duplicate.
308 if (mightContainDuplicatePropertyNames_) {
309 return true;
310 }
311
312 // Check bloom filter for duplicate, and add if not already represented.
313 if (propNamesFilter_.mightContain(propName.rawData())) {
314 mightContainDuplicatePropertyNames_ = true;
315 } else {
316 propNamesFilter_.add(propName.rawData());
317 }
318 return true;
319 }
setPropNameNoDuplicateCheckObjLiteralWriter320 void setPropNameNoDuplicateCheck(
321 frontend::ParserAtomsTable& parserAtoms,
322 const frontend::TaggedParserAtomIndex propName) {
323 // Only valid in object-mode.
324 MOZ_ASSERT(!flags_.hasFlag(ObjLiteralFlag::Array));
325 parserAtoms.markUsedByStencil(propName);
326 nextKey_ = ObjLiteralKey::fromPropName(propName);
327 }
setPropIndexObjLiteralWriter328 void setPropIndex(uint32_t propIndex) {
329 // Only valid in object-mode.
330 MOZ_ASSERT(!flags_.hasFlag(ObjLiteralFlag::Array));
331 MOZ_ASSERT(propIndex <= ATOM_INDEX_MASK);
332 nextKey_ = ObjLiteralKey::fromArrayIndex(propIndex);
333 flags_.setFlag(ObjLiteralFlag::HasIndexOrDuplicatePropName);
334 }
beginDenseArrayElementsObjLiteralWriter335 void beginDenseArrayElements() {
336 // Only valid in array-mode.
337 MOZ_ASSERT(flags_.hasFlag(ObjLiteralFlag::Array));
338 // Dense array element sequences do not use the keys; the indices are
339 // implicit.
340 nextKey_ = ObjLiteralKey::none();
341 }
342
propWithConstNumericValueObjLiteralWriter343 [[nodiscard]] bool propWithConstNumericValue(JSContext* cx,
344 const JS::Value& value) {
345 propertyCount_++;
346 MOZ_ASSERT(value.isNumber());
347 return pushOpAndName(cx, ObjLiteralOpcode::ConstValue, nextKey_) &&
348 pushValueArg(cx, value);
349 }
propWithAtomValueObjLiteralWriter350 [[nodiscard]] bool propWithAtomValue(
351 JSContext* cx, frontend::ParserAtomsTable& parserAtoms,
352 const frontend::TaggedParserAtomIndex value) {
353 propertyCount_++;
354 parserAtoms.markUsedByStencil(value);
355 return pushOpAndName(cx, ObjLiteralOpcode::ConstAtom, nextKey_) &&
356 pushAtomArg(cx, value);
357 }
propWithNullValueObjLiteralWriter358 [[nodiscard]] bool propWithNullValue(JSContext* cx) {
359 propertyCount_++;
360 return pushOpAndName(cx, ObjLiteralOpcode::Null, nextKey_);
361 }
propWithUndefinedValueObjLiteralWriter362 [[nodiscard]] bool propWithUndefinedValue(JSContext* cx) {
363 propertyCount_++;
364 return pushOpAndName(cx, ObjLiteralOpcode::Undefined, nextKey_);
365 }
propWithTrueValueObjLiteralWriter366 [[nodiscard]] bool propWithTrueValue(JSContext* cx) {
367 propertyCount_++;
368 return pushOpAndName(cx, ObjLiteralOpcode::True, nextKey_);
369 }
propWithFalseValueObjLiteralWriter370 [[nodiscard]] bool propWithFalseValue(JSContext* cx) {
371 propertyCount_++;
372 return pushOpAndName(cx, ObjLiteralOpcode::False, nextKey_);
373 }
374
arrayIndexInRangeObjLiteralWriter375 static bool arrayIndexInRange(int32_t i) {
376 return i >= 0 && static_cast<uint32_t>(i) <= ATOM_INDEX_MASK;
377 }
378
379 #if defined(DEBUG) || defined(JS_JITSPEW)
380 void dump() const;
381 void dump(JSONPrinter& json,
382 const frontend::CompilationStencil* stencil) const;
383 void dumpFields(JSONPrinter& json,
384 const frontend::CompilationStencil* stencil) const;
385 #endif
386
387 private:
388 // Set to true if we've found possible duplicate names while building.
389 // This field is placed next to `flags_` field, to reduce padding.
390 bool mightContainDuplicatePropertyNames_ = false;
391
392 ObjLiteralFlags flags_;
393 ObjLiteralKey nextKey_;
394 uint32_t propertyCount_ = 0;
395
396 // Duplicate property names detection is performed in the following way:
397 // * while emitting code, add each property names with
398 // `propNamesFilter_`
399 // * if possible duplicate property name is detected, set
400 // `mightContainDuplicatePropertyNames_` to true
401 // * in `checkForDuplicatedNames` method,
402 // if `mightContainDuplicatePropertyNames_` is true,
403 // check the duplicate property names with `HashSet`, and if it exists,
404 // set HasIndexOrDuplicatePropName flag.
405 mozilla::BitBloomFilter<12, frontend::TaggedParserAtomIndex> propNamesFilter_;
406 };
407
408 struct ObjLiteralReaderBase {
409 private:
410 mozilla::Span<const uint8_t> data_;
411 size_t cursor_;
412
readByteObjLiteralReaderBase413 [[nodiscard]] bool readByte(uint8_t* b) {
414 if (cursor_ + 1 > data_.Length()) {
415 return false;
416 }
417 *b = *data_.From(cursor_).data();
418 cursor_ += 1;
419 return true;
420 }
421
readBytesObjLiteralReaderBase422 [[nodiscard]] bool readBytes(size_t size, const uint8_t** p) {
423 if (cursor_ + size > data_.Length()) {
424 return false;
425 }
426 *p = data_.From(cursor_).data();
427 cursor_ += size;
428 return true;
429 }
430
431 template <typename T>
readRawDataObjLiteralReaderBase432 [[nodiscard]] bool readRawData(T* data) {
433 const uint8_t* p = nullptr;
434 if (!readBytes(sizeof(T), &p)) {
435 return false;
436 }
437 memcpy(data, p, sizeof(T));
438 return true;
439 }
440
441 public:
ObjLiteralReaderBaseObjLiteralReaderBase442 explicit ObjLiteralReaderBase(mozilla::Span<const uint8_t> data)
443 : data_(data), cursor_(0) {}
444
readOpAndKeyObjLiteralReaderBase445 [[nodiscard]] bool readOpAndKey(ObjLiteralOpcode* op, ObjLiteralKey* key) {
446 uint8_t opbyte;
447 if (!readByte(&opbyte)) {
448 return false;
449 }
450 if (MOZ_UNLIKELY(opbyte > static_cast<uint8_t>(ObjLiteralOpcode::MAX))) {
451 return false;
452 }
453 *op = static_cast<ObjLiteralOpcode>(opbyte);
454
455 uint32_t data;
456 if (!readRawData(&data)) {
457 return false;
458 }
459 bool isArray = data & ObjLiteralWriterBase::INDEXED_PROP;
460 uint32_t rawIndex = data & ~ObjLiteralWriterBase::INDEXED_PROP;
461 *key = ObjLiteralKey(rawIndex, isArray);
462 return true;
463 }
464
readValueArgObjLiteralReaderBase465 [[nodiscard]] bool readValueArg(JS::Value* value) {
466 uint64_t data;
467 if (!readRawData(&data)) {
468 return false;
469 }
470 *value = JS::Value::fromRawBits(data);
471 return true;
472 }
473
readAtomArgObjLiteralReaderBase474 [[nodiscard]] bool readAtomArg(frontend::TaggedParserAtomIndex* atomIndex) {
475 return readRawData(atomIndex->rawDataRef());
476 }
477
cursorObjLiteralReaderBase478 size_t cursor() const { return cursor_; }
479 };
480
481 // A single object-literal instruction, creating one property on an object.
482 struct ObjLiteralInsn {
483 private:
484 ObjLiteralOpcode op_;
485 ObjLiteralKey key_;
486 union Arg {
Arg(uint64_t raw_)487 explicit Arg(uint64_t raw_) : raw(raw_) {}
488
489 JS::Value constValue;
490 frontend::TaggedParserAtomIndex atomIndex;
491 uint64_t raw;
492 } arg_;
493
494 public:
ObjLiteralInsnObjLiteralInsn495 ObjLiteralInsn() : op_(ObjLiteralOpcode::INVALID), arg_(0) {}
ObjLiteralInsnObjLiteralInsn496 ObjLiteralInsn(ObjLiteralOpcode op, ObjLiteralKey key)
497 : op_(op), key_(key), arg_(0) {
498 MOZ_ASSERT(!hasConstValue());
499 MOZ_ASSERT(!hasAtomIndex());
500 }
ObjLiteralInsnObjLiteralInsn501 ObjLiteralInsn(ObjLiteralOpcode op, ObjLiteralKey key, const JS::Value& value)
502 : op_(op), key_(key), arg_(0) {
503 MOZ_ASSERT(hasConstValue());
504 MOZ_ASSERT(!hasAtomIndex());
505 arg_.constValue = value;
506 }
ObjLiteralInsnObjLiteralInsn507 ObjLiteralInsn(ObjLiteralOpcode op, ObjLiteralKey key,
508 frontend::TaggedParserAtomIndex atomIndex)
509 : op_(op), key_(key), arg_(0) {
510 MOZ_ASSERT(!hasConstValue());
511 MOZ_ASSERT(hasAtomIndex());
512 arg_.atomIndex = atomIndex;
513 }
ObjLiteralInsnObjLiteralInsn514 ObjLiteralInsn(const ObjLiteralInsn& other) : ObjLiteralInsn() {
515 *this = other;
516 }
517 ObjLiteralInsn& operator=(const ObjLiteralInsn& other) {
518 op_ = other.op_;
519 key_ = other.key_;
520 arg_.raw = other.arg_.raw;
521 return *this;
522 }
523
isValidObjLiteralInsn524 bool isValid() const {
525 return op_ > ObjLiteralOpcode::INVALID && op_ <= ObjLiteralOpcode::MAX;
526 }
527
getOpObjLiteralInsn528 ObjLiteralOpcode getOp() const {
529 MOZ_ASSERT(isValid());
530 return op_;
531 }
getKeyObjLiteralInsn532 const ObjLiteralKey& getKey() const {
533 MOZ_ASSERT(isValid());
534 return key_;
535 }
536
hasConstValueObjLiteralInsn537 bool hasConstValue() const {
538 MOZ_ASSERT(isValid());
539 return ObjLiteralOpcodeHasValueArg(op_);
540 }
hasAtomIndexObjLiteralInsn541 bool hasAtomIndex() const {
542 MOZ_ASSERT(isValid());
543 return ObjLiteralOpcodeHasAtomArg(op_);
544 }
545
getConstValueObjLiteralInsn546 JS::Value getConstValue() const {
547 MOZ_ASSERT(isValid());
548 MOZ_ASSERT(hasConstValue());
549 return arg_.constValue;
550 }
getAtomIndexObjLiteralInsn551 frontend::TaggedParserAtomIndex getAtomIndex() const {
552 MOZ_ASSERT(isValid());
553 MOZ_ASSERT(hasAtomIndex());
554 return arg_.atomIndex;
555 };
556 };
557
558 // A reader that parses a sequence of object-literal instructions out of the
559 // encoded form.
560 struct ObjLiteralReader : private ObjLiteralReaderBase {
561 public:
ObjLiteralReaderObjLiteralReader562 explicit ObjLiteralReader(mozilla::Span<const uint8_t> data)
563 : ObjLiteralReaderBase(data) {}
564
readInsnObjLiteralReader565 [[nodiscard]] bool readInsn(ObjLiteralInsn* insn) {
566 ObjLiteralOpcode op;
567 ObjLiteralKey key;
568 if (!readOpAndKey(&op, &key)) {
569 return false;
570 }
571 if (ObjLiteralOpcodeHasValueArg(op)) {
572 JS::Value value;
573 if (!readValueArg(&value)) {
574 return false;
575 }
576 *insn = ObjLiteralInsn(op, key, value);
577 return true;
578 }
579 if (ObjLiteralOpcodeHasAtomArg(op)) {
580 frontend::TaggedParserAtomIndex atomIndex;
581 if (!readAtomArg(&atomIndex)) {
582 return false;
583 }
584 *insn = ObjLiteralInsn(op, key, atomIndex);
585 return true;
586 }
587 *insn = ObjLiteralInsn(op, key);
588 return true;
589 }
590 };
591
592 // A class to modify the code, while keeping the structure.
593 struct ObjLiteralModifier : private ObjLiteralReaderBase {
594 mozilla::Span<uint8_t> mutableData_;
595
596 public:
ObjLiteralModifierObjLiteralModifier597 explicit ObjLiteralModifier(mozilla::Span<uint8_t> data)
598 : ObjLiteralReaderBase(data), mutableData_(data) {}
599
600 private:
601 // Map `atom` with `map`, and write to `atomCursor` of `mutableData_`.
602 template <typename MapT>
mapOneAtomObjLiteralModifier603 void mapOneAtom(MapT map, frontend::TaggedParserAtomIndex atom,
604 size_t atomCursor) {
605 auto atomIndex = map(atom);
606 memcpy(mutableData_.data() + atomCursor, atomIndex.rawDataRef(),
607 sizeof(frontend::TaggedParserAtomIndex));
608 }
609
610 // Map atoms in single instruction.
611 // Return true if it successfully maps.
612 // Return false if there's no more instruction.
613 template <typename MapT>
mapInsnAtomObjLiteralModifier614 bool mapInsnAtom(MapT map) {
615 ObjLiteralOpcode op;
616 ObjLiteralKey key;
617
618 size_t opCursor = cursor();
619 if (!readOpAndKey(&op, &key)) {
620 return false;
621 }
622 if (key.isAtomIndex()) {
623 static constexpr size_t OpLength = 1;
624 size_t atomCursor = opCursor + OpLength;
625 mapOneAtom(map, key.getAtomIndex(), atomCursor);
626 }
627
628 if (ObjLiteralOpcodeHasValueArg(op)) {
629 JS::Value value;
630 if (!readValueArg(&value)) {
631 return false;
632 }
633 } else if (ObjLiteralOpcodeHasAtomArg(op)) {
634 size_t atomCursor = cursor();
635
636 frontend::TaggedParserAtomIndex atomIndex;
637 if (!readAtomArg(&atomIndex)) {
638 return false;
639 }
640
641 mapOneAtom(map, atomIndex, atomCursor);
642 }
643
644 return true;
645 }
646
647 public:
648 // Map TaggedParserAtomIndex inside the code in place, with given function.
649 template <typename MapT>
mapAtomObjLiteralModifier650 void mapAtom(MapT map) {
651 while (mapInsnAtom(map)) {
652 }
653 }
654 };
655
656 class ObjLiteralStencil {
657 friend class frontend::StencilXDR;
658
659 mozilla::Span<uint8_t> code_;
660 ObjLiteralFlags flags_;
661 uint32_t propertyCount_ = 0;
662
663 public:
664 ObjLiteralStencil() = default;
665
ObjLiteralStencil(uint8_t * code,size_t length,const ObjLiteralFlags & flags,uint32_t propertyCount)666 ObjLiteralStencil(uint8_t* code, size_t length, const ObjLiteralFlags& flags,
667 uint32_t propertyCount)
668 : code_(mozilla::Span(code, length)),
669 flags_(flags),
670 propertyCount_(propertyCount) {}
671
672 JSObject* create(JSContext* cx,
673 const frontend::CompilationAtomCache& atomCache) const;
674
code()675 mozilla::Span<const uint8_t> code() const { return code_; }
flags()676 ObjLiteralFlags flags() const { return flags_; }
propertyCount()677 uint32_t propertyCount() const { return propertyCount_; }
678
679 #ifdef DEBUG
680 bool isContainedIn(const LifoAlloc& alloc) const;
681 #endif
682
683 #if defined(DEBUG) || defined(JS_JITSPEW)
684 void dump() const;
685 void dump(JSONPrinter& json,
686 const frontend::CompilationStencil* stencil) const;
687 void dumpFields(JSONPrinter& json,
688 const frontend::CompilationStencil* stencil) const;
689
690 #endif
691 };
692
693 } // namespace js
694 #endif // frontend_ObjLiteral_h
695