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/EndianUtils.h"
12 #include "mozilla/EnumSet.h"
13 #include "mozilla/Span.h"
14 
15 #include "js/AllocPolicy.h"
16 #include "js/GCPolicyAPI.h"
17 #include "js/Value.h"
18 #include "js/Vector.h"
19 
20 /*
21  * [SMDOC] ObjLiteral (Object Literal) Handling
22  * ============================================
23  *
24  * The `ObjLiteral*` family of classes defines an infastructure to handle
25  * object literals as they are encountered at parse time and translate them
26  * into objects that are attached to the bytecode.
27  *
28  * The object-literal "instructions", whose opcodes are defined in
29  * `ObjLiteralOpcode` below, each specify one key (atom property name, or
30  * numeric index) and one value. An `ObjLiteralWriter` buffers a linear
31  * sequence of such instructions, along with a side-table of atom references.
32  * The writer stores a compact binary format that is then interpreted by the
33  * `ObjLiteralReader` to construct an object according to the instructions.
34  *
35  * This may seem like an odd dance: create an intermediate data structure that
36  * specifies key/value pairs, then later build the object. Why not just do so
37  * directly, as we parse? In fact, we used to do this. However, for several
38  * good reasons, we want to avoid allocating or touching GC objects at all
39  * *during* the parse. We thus use a sequence of ObjLiteral instructions as an
40  * intermediate data structure to carry object literal contents from parse to
41  * the time at which we *can* allocate objects.
42  *
43  * (The original intent was to allow for ObjLiteral instructions to actually be
44  * invoked by a new JS opcode, JSOp::ObjLiteral, thus replacing the more
45  * general opcode sequences sometimes generated to fill in objects and removing
46  * the need to attach actual objects to JSOp::Object or JSOp::NewObject.
47  * However, this was far too invasive and led to performance regressions, so
48  * currently ObjLiteral only carries literals as far as the end of the parse
49  * pipeline, when all GC things are allocated.)
50  *
51  * ObjLiteral data structures are used to represent object literals whenever
52  * they are "compatible". See
53  * BytecodeEmitter::isPropertyListObjLiteralCompatible for the precise
54  * conditions; in brief, we can represent object literals with "primitive"
55  * (numeric, boolean, string, null/undefined) values, and "normal"
56  * (non-computed) object names. We can also represent arrays with the same
57  * value restrictions. We cannot represent nested objects. We use ObjLiteral in
58  * two different ways:
59  *
60  * - To build a template object, when we can support the properties but not the
61  *   keys.
62  * - To build the actual result object, when we support the properties and the
63  *   keys and this is a JSOp::Object case (see below).
64  *
65  * Design and Performance Considerations
66  * -------------------------------------
67  *
68  * As a brief overview, there are a number of opcodes that allocate objects:
69  *
70  * - JSOp::NewInit allocates a new empty `{}` object.
71  *
72  * - JSOp::NewObject, with an object as an argument (held by the script data
73  *   side-tables), allocates a new object with `undefined` property values but
74  *   with a defined set of properties. The given object is used as a
75  *   *template*.
76  *
77  * - JSOp::NewObjectWithGroup (added as part of this ObjLiteral work), same as
78  *   above but uses the ObjectGroup of the template object for the new object,
79  *   rather than trying to apply a set of heuristics to choose a group.
80  *
81  * - JSOp::Object, with an object as argument, instructs the runtime to
82  *   literally return the object argument as the result. This is thus only an
83  *   "allocation" in the sense that the object was originally allocated when
84  *   the script data / bytecode was created. It is only used when we know for
85  *   sure that the script, and this program point within the script, will run
86  *   *once*. (See the `treatAsRunOnce` flag on JSScript.)
87  *
88  * Before we go further, we also define "singleton context" and "singleton
89  * group". An operation occurs in a "singleton context", according to the
90  * parser, if it will only ever execute once. In particular, this happens when
91  * (i) the script is a "run-once" script, which is usually the case for e.g.
92  * top-level scripts of web-pages (they run on page load, but no function or
93  * handle wraps or refers to the script so it can't be invoked again),
94  * and (ii) the operation itself is not within a loop or function in that
95  * run-once script. "Singleton group", on the other hand, refers to an
96  * ObjectGroup (used by Type Inference) that represents only one object, and
97  * has a special flag set to mark it as such. Usually we want to give singleton
98  * groups to object allocations that happen in a singleton context (because
99  * there will only ever be one of the object), hence the connection between
100  * these terms.
101  *
102  * When we encounter an object literal, we decide which opcode to use, and we
103  * construct the ObjLiteral and the bytecode using its result appropriately:
104  *
105  * - If in a singleton context, and if we support the values, we use
106  *   JSOp::Object and we build the ObjLiteral instructions with values.
107  * - Otherwise, if we support the keys but not the values, or if we are not
108  *   in a singleton context, we use JSOp::NewObject or JSOp::NewObjectWithGroup,
109  *   depending on the "inner singleton" status (see below). In this case, the
110  *   initial opcode only creates an object with empty values, so
111  *   BytecodeEmitter then generates bytecode to set the values
112  *   appropriately.
113  * - Otherwise, we generate JSOp::NewInit and bytecode to add properties one at
114  *   a time. This will always work, but is the slowest and least
115  *   memory-efficient option.
116  *
117  * We need to take special care to ensure that the ObjectGroup of the resulting
118  * object is chosen "correctly". Failure to do so can result in all sorts
119  * of performance and/or memory regressions. In brief, we want to use a
120  * singleton group whenever an object is allocated in a singleton context.
121  * However, there is a special "inner singleton" context that deserves special
122  * mention. When a program has a nested tree of objects, the old
123  * (pre-ObjLiteral) world would perform a group lookup by shape (list of
124  * property IDs) for all non-root objects, so in the following snippet, the
125  * inner objects would share a group:
126  *
127  *     var list = [{a: 0}, {a: 1}];
128  *     var obj = { first: {a: 0}, second: {a: 1} };
129  *
130  * In the generated bytecode, the inner objects are created first, then placed
131  * in the relevant properties of the outer objects/arrays using
132  * INITPROP/INITELEM. Thus to a naïve analysis, it appears that the inner
133  * objects are singletons. But heuristically it is better if they are not. So
134  * we pass down an `isInner` boolean while recursively traversing the
135  * parse-node tree and generating bytecode. If we encounter an object literal
136  * that is in singleton (run-once) context but also `isInner`, then we set
137  * special flags to ensure its shape is looked up based on properties instead.
138  */
139 
140 namespace js {
141 
142 // Object-literal instruction opcodes. An object literal is constructed by a
143 // straight-line sequence of these ops, each adding one property to the
144 // object.
145 enum class ObjLiteralOpcode : uint8_t {
146   INVALID = 0,
147 
148   ConstValue = 1,  // numeric types only.
149   ConstAtom = 2,
150   Null = 3,
151   Undefined = 4,
152   True = 5,
153   False = 6,
154 
155   MAX = False,
156 };
157 
158 // Flags that are associated with a sequence of object-literal instructions.
159 // (These become bitflags by wrapping with EnumSet below.)
160 enum class ObjLiteralFlag : uint8_t {
161   // If set, this object is an array.
162   Array = 1,
163 
164   // If set, the created object will be created with an object group either
165   // freshly allocated or determined by property names by calling
166   // `ObjectGroup::newPlainObject`.
167   SpecificGroup = 2,
168   // If set, the created object will be created with newType == SingletonObject
169   // rather than TenuredObject.
170   Singleton = 3,
171   // If set, the created array will be created as a COW array rather than a
172   // normal array.
173   ArrayCOW = 4,
174 
175   // No values are provided; the object is meant as a template object.
176   NoValues = 5,
177 
178   // This object is inside a top-level singleton, and so prior to ObjLiteral,
179   // would have been allocated at parse time, but is now allocated in bytecode.
180   // We do special things to get the right group on the template object; this
181   // flag indicates that if JSOp::NewObject copies the object, it should retain
182   // its group.
183   IsInnerSingleton = 6,
184 };
185 
186 using ObjLiteralFlags = mozilla::EnumSet<ObjLiteralFlag>;
187 
ObjLiteralOpcodeHasValueArg(ObjLiteralOpcode op)188 inline bool ObjLiteralOpcodeHasValueArg(ObjLiteralOpcode op) {
189   return op == ObjLiteralOpcode::ConstValue;
190 }
191 
ObjLiteralOpcodeHasAtomArg(ObjLiteralOpcode op)192 inline bool ObjLiteralOpcodeHasAtomArg(ObjLiteralOpcode op) {
193   return op == ObjLiteralOpcode::ConstAtom;
194 }
195 
196 struct ObjLiteralReaderBase;
197 
198 // Property name (as an atom index) or an integer index.  Only used for
199 // object-type literals; array literals do not require the index (the sequence
200 // is always dense, with no holes, so the index is implicit). For the latter
201 // case, we have a `None` placeholder.
202 struct ObjLiteralKey {
203  private:
204   uint32_t value_;
205 
206   enum ObjLiteralKeyType {
207     None,
208     AtomIndex,
209     ArrayIndex,
210   };
211 
212   ObjLiteralKeyType type_;
213 
ObjLiteralKeyObjLiteralKey214   ObjLiteralKey(uint32_t value, ObjLiteralKeyType ty)
215       : value_(value), type_(ty) {}
216 
217  public:
ObjLiteralKeyObjLiteralKey218   ObjLiteralKey() : ObjLiteralKey(0, None) {}
ObjLiteralKeyObjLiteralKey219   ObjLiteralKey(uint32_t value, bool isArrayIndex)
220       : ObjLiteralKey(value, isArrayIndex ? ArrayIndex : AtomIndex) {}
221   ObjLiteralKey(const ObjLiteralKey& other) = default;
222 
fromPropNameObjLiteralKey223   static ObjLiteralKey fromPropName(uint32_t atomIndex) {
224     return ObjLiteralKey(atomIndex, false);
225   }
fromArrayIndexObjLiteralKey226   static ObjLiteralKey fromArrayIndex(uint32_t index) {
227     return ObjLiteralKey(index, true);
228   }
noneObjLiteralKey229   static ObjLiteralKey none() { return ObjLiteralKey(); }
230 
isNoneObjLiteralKey231   bool isNone() const { return type_ == None; }
isAtomIndexObjLiteralKey232   bool isAtomIndex() const { return type_ == AtomIndex; }
isArrayIndexObjLiteralKey233   bool isArrayIndex() const { return type_ == ArrayIndex; }
234 
getAtomIndexObjLiteralKey235   uint32_t getAtomIndex() const {
236     MOZ_ASSERT(isAtomIndex());
237     return value_;
238   }
getArrayIndexObjLiteralKey239   uint32_t getArrayIndex() const {
240     MOZ_ASSERT(isArrayIndex());
241     return value_;
242   }
243 
rawIndexObjLiteralKey244   uint32_t rawIndex() const { return value_; }
245 };
246 
247 struct ObjLiteralWriterBase {
248  protected:
249   friend struct ObjLiteralReaderBase;  // for access to mask and shift.
250   static const uint32_t ATOM_INDEX_MASK = 0x007fffff;
251   // If set, the atom index field is an array index, not an atom index.
252   static const uint32_t INDEXED_PROP = 0x00800000;
253   static const int OP_SHIFT = 24;
254 
255  protected:
256   Vector<uint8_t, 64> code_;
257 
258  public:
ObjLiteralWriterBaseObjLiteralWriterBase259   explicit ObjLiteralWriterBase(JSContext* cx) : code_(cx) {}
260 
curOffsetObjLiteralWriterBase261   uint32_t curOffset() const { return code_.length(); }
262 
prepareBytesObjLiteralWriterBase263   MOZ_MUST_USE bool prepareBytes(size_t len, uint8_t** p) {
264     size_t offset = code_.length();
265     if (!code_.growByUninitialized(len)) {
266       return false;
267     }
268     *p = &code_[offset];
269     return true;
270   }
271 
272   template <typename T>
pushRawDataObjLiteralWriterBase273   MOZ_MUST_USE bool pushRawData(T data) {
274     uint8_t* p = nullptr;
275     if (!prepareBytes(sizeof(T), &p)) {
276       return false;
277     }
278     mozilla::NativeEndian::copyAndSwapToLittleEndian(reinterpret_cast<void*>(p),
279                                                      &data, 1);
280     return true;
281   }
282 
pushOpAndNameObjLiteralWriterBase283   MOZ_MUST_USE bool pushOpAndName(ObjLiteralOpcode op, ObjLiteralKey key) {
284     uint32_t data = (key.rawIndex() & ATOM_INDEX_MASK) |
285                     (key.isArrayIndex() ? INDEXED_PROP : 0) |
286                     (static_cast<uint8_t>(op) << OP_SHIFT);
287     return pushRawData(data);
288   }
289 
pushValueArgObjLiteralWriterBase290   MOZ_MUST_USE bool pushValueArg(const JS::Value& value) {
291     MOZ_ASSERT(value.isNumber() || value.isNullOrUndefined() ||
292                value.isBoolean());
293     uint64_t data = value.asRawBits();
294     return pushRawData(data);
295   }
296 
pushAtomArgObjLiteralWriterBase297   MOZ_MUST_USE bool pushAtomArg(uint32_t atomIndex) {
298     return pushRawData(atomIndex);
299   }
300 };
301 
302 // An object-literal instruction writer. This class, held by the bytecode
303 // emitter, keeps a sequence of object-literal instructions emitted as object
304 // literal expressions are parsed. It allows the user to 'begin' and 'end'
305 // straight-line sequences, returning the offsets for this range of instructions
306 // within the writer.
307 struct ObjLiteralWriter : private ObjLiteralWriterBase {
308  public:
ObjLiteralWriterObjLiteralWriter309   explicit ObjLiteralWriter(JSContext* cx)
310       : ObjLiteralWriterBase(cx), flags_() {}
311 
clearObjLiteralWriter312   void clear() { code_.clear(); }
313 
getCodeObjLiteralWriter314   mozilla::Span<const uint8_t> getCode() const { return code_; }
getFlagsObjLiteralWriter315   ObjLiteralFlags getFlags() const { return flags_; }
316 
beginObjectObjLiteralWriter317   void beginObject(ObjLiteralFlags flags) { flags_ = flags; }
setPropNameObjLiteralWriter318   void setPropName(uint32_t propName) {
319     // Only valid in object-mode.
320     MOZ_ASSERT(!flags_.contains(ObjLiteralFlag::Array));
321     MOZ_ASSERT(propName <= ATOM_INDEX_MASK);
322     nextKey_ = ObjLiteralKey::fromPropName(propName);
323   }
setPropIndexObjLiteralWriter324   void setPropIndex(uint32_t propIndex) {
325     // Only valid in object-mode.
326     MOZ_ASSERT(!flags_.contains(ObjLiteralFlag::Array));
327     MOZ_ASSERT(propIndex <= ATOM_INDEX_MASK);
328     nextKey_ = ObjLiteralKey::fromArrayIndex(propIndex);
329   }
beginDenseArrayElementsObjLiteralWriter330   void beginDenseArrayElements() {
331     // Only valid in array-mode.
332     MOZ_ASSERT(flags_.contains(ObjLiteralFlag::Array));
333     // Dense array element sequences do not use the keys; the indices are
334     // implicit.
335     nextKey_ = ObjLiteralKey::none();
336   }
337 
propWithConstNumericValueObjLiteralWriter338   MOZ_MUST_USE bool propWithConstNumericValue(const JS::Value& value) {
339     MOZ_ASSERT(value.isNumber());
340     return pushOpAndName(ObjLiteralOpcode::ConstValue, nextKey_) &&
341            pushValueArg(value);
342   }
propWithAtomValueObjLiteralWriter343   MOZ_MUST_USE bool propWithAtomValue(uint32_t value) {
344     return pushOpAndName(ObjLiteralOpcode::ConstAtom, nextKey_) &&
345            pushAtomArg(value);
346   }
propWithNullValueObjLiteralWriter347   MOZ_MUST_USE bool propWithNullValue() {
348     return pushOpAndName(ObjLiteralOpcode::Null, nextKey_);
349   }
propWithUndefinedValueObjLiteralWriter350   MOZ_MUST_USE bool propWithUndefinedValue() {
351     return pushOpAndName(ObjLiteralOpcode::Undefined, nextKey_);
352   }
propWithTrueValueObjLiteralWriter353   MOZ_MUST_USE bool propWithTrueValue() {
354     return pushOpAndName(ObjLiteralOpcode::True, nextKey_);
355   }
propWithFalseValueObjLiteralWriter356   MOZ_MUST_USE bool propWithFalseValue() {
357     return pushOpAndName(ObjLiteralOpcode::False, nextKey_);
358   }
359 
arrayIndexInRangeObjLiteralWriter360   static bool arrayIndexInRange(int32_t i) {
361     return i >= 0 && static_cast<uint32_t>(i) <= ATOM_INDEX_MASK;
362   }
363 
364  private:
365   ObjLiteralFlags flags_;
366   ObjLiteralKey nextKey_;
367 };
368 
369 struct ObjLiteralReaderBase {
370  private:
371   mozilla::Span<const uint8_t> data_;
372   size_t cursor_;
373 
readBytesObjLiteralReaderBase374   MOZ_MUST_USE bool readBytes(size_t size, const uint8_t** p) {
375     if (cursor_ + size > data_.Length()) {
376       return false;
377     }
378     *p = data_.From(cursor_).data();
379     cursor_ += size;
380     return true;
381   }
382 
383   template <typename T>
readRawDataObjLiteralReaderBase384   MOZ_MUST_USE bool readRawData(T* data) {
385     const uint8_t* p = nullptr;
386     if (!readBytes(sizeof(T), &p)) {
387       return false;
388     }
389     mozilla::NativeEndian::copyAndSwapFromLittleEndian(
390         data, reinterpret_cast<const void*>(p), 1);
391     return true;
392   }
393 
394  public:
ObjLiteralReaderBaseObjLiteralReaderBase395   explicit ObjLiteralReaderBase(mozilla::Span<const uint8_t> data)
396       : data_(data), cursor_(0) {}
397 
readOpAndKeyObjLiteralReaderBase398   MOZ_MUST_USE bool readOpAndKey(ObjLiteralOpcode* op, ObjLiteralKey* key) {
399     uint32_t data;
400     if (!readRawData(&data)) {
401       return false;
402     }
403     uint8_t opbyte =
404         static_cast<uint8_t>(data >> ObjLiteralWriterBase::OP_SHIFT);
405     if (MOZ_UNLIKELY(opbyte > static_cast<uint8_t>(ObjLiteralOpcode::MAX))) {
406       return false;
407     }
408     *op = static_cast<ObjLiteralOpcode>(opbyte);
409     bool isArray = data & ObjLiteralWriterBase::INDEXED_PROP;
410     uint32_t rawIndex = data & ObjLiteralWriterBase::ATOM_INDEX_MASK;
411     *key = ObjLiteralKey(rawIndex, isArray);
412     return true;
413   }
414 
readValueArgObjLiteralReaderBase415   MOZ_MUST_USE bool readValueArg(JS::Value* value) {
416     uint64_t data;
417     if (!readRawData(&data)) {
418       return false;
419     }
420     *value = JS::Value::fromRawBits(data);
421     return true;
422   }
423 
readAtomArgObjLiteralReaderBase424   MOZ_MUST_USE bool readAtomArg(uint32_t* atomIndex) {
425     return readRawData(atomIndex);
426   }
427 };
428 
429 // A single object-literal instruction, creating one property on an object.
430 struct ObjLiteralInsn {
431  private:
432   ObjLiteralOpcode op_;
433   ObjLiteralKey key_;
434   union Arg {
Arg(uint64_t raw_)435     explicit Arg(uint64_t raw_) : raw(raw_) {}
436 
437     JS::Value constValue;
438     uint32_t atomIndex;
439     uint64_t raw;
440   } arg_;
441 
442  public:
ObjLiteralInsnObjLiteralInsn443   ObjLiteralInsn() : op_(ObjLiteralOpcode::INVALID), arg_(0) {}
ObjLiteralInsnObjLiteralInsn444   ObjLiteralInsn(ObjLiteralOpcode op, ObjLiteralKey key)
445       : op_(op), key_(key), arg_(0) {
446     MOZ_ASSERT(!hasConstValue());
447     MOZ_ASSERT(!hasAtomIndex());
448   }
ObjLiteralInsnObjLiteralInsn449   ObjLiteralInsn(ObjLiteralOpcode op, ObjLiteralKey key, const JS::Value& value)
450       : op_(op), key_(key), arg_(0) {
451     MOZ_ASSERT(hasConstValue());
452     MOZ_ASSERT(!hasAtomIndex());
453     arg_.constValue = value;
454   }
ObjLiteralInsnObjLiteralInsn455   ObjLiteralInsn(ObjLiteralOpcode op, ObjLiteralKey key, uint32_t atomIndex)
456       : op_(op), key_(key), arg_(0) {
457     MOZ_ASSERT(!hasConstValue());
458     MOZ_ASSERT(hasAtomIndex());
459     arg_.atomIndex = atomIndex;
460   }
ObjLiteralInsnObjLiteralInsn461   ObjLiteralInsn(const ObjLiteralInsn& other) : ObjLiteralInsn() {
462     *this = other;
463   }
464   ObjLiteralInsn& operator=(const ObjLiteralInsn& other) {
465     op_ = other.op_;
466     key_ = other.key_;
467     arg_.raw = other.arg_.raw;
468     return *this;
469   }
470 
isValidObjLiteralInsn471   bool isValid() const {
472     return op_ > ObjLiteralOpcode::INVALID && op_ <= ObjLiteralOpcode::MAX;
473   }
474 
getOpObjLiteralInsn475   ObjLiteralOpcode getOp() const {
476     MOZ_ASSERT(isValid());
477     return op_;
478   }
getKeyObjLiteralInsn479   const ObjLiteralKey& getKey() const {
480     MOZ_ASSERT(isValid());
481     return key_;
482   }
483 
hasConstValueObjLiteralInsn484   bool hasConstValue() const {
485     MOZ_ASSERT(isValid());
486     return ObjLiteralOpcodeHasValueArg(op_);
487   }
hasAtomIndexObjLiteralInsn488   bool hasAtomIndex() const {
489     MOZ_ASSERT(isValid());
490     return ObjLiteralOpcodeHasAtomArg(op_);
491   }
492 
getConstValueObjLiteralInsn493   JS::Value getConstValue() const {
494     MOZ_ASSERT(isValid());
495     MOZ_ASSERT(hasConstValue());
496     return arg_.constValue;
497   }
getAtomIndexObjLiteralInsn498   uint32_t getAtomIndex() const {
499     MOZ_ASSERT(isValid());
500     MOZ_ASSERT(hasAtomIndex());
501     return arg_.atomIndex;
502   };
503 };
504 
505 // A reader that parses a sequence of object-literal instructions out of the
506 // encoded form.
507 struct ObjLiteralReader : private ObjLiteralReaderBase {
508  public:
ObjLiteralReaderObjLiteralReader509   explicit ObjLiteralReader(mozilla::Span<const uint8_t> data)
510       : ObjLiteralReaderBase(data) {}
511 
readInsnObjLiteralReader512   MOZ_MUST_USE bool readInsn(ObjLiteralInsn* insn) {
513     ObjLiteralOpcode op;
514     ObjLiteralKey key;
515     if (!readOpAndKey(&op, &key)) {
516       return false;
517     }
518     if (ObjLiteralOpcodeHasValueArg(op)) {
519       JS::Value value;
520       if (!readValueArg(&value)) {
521         return false;
522       }
523       *insn = ObjLiteralInsn(op, key, value);
524       return true;
525     }
526     if (ObjLiteralOpcodeHasAtomArg(op)) {
527       uint32_t atomIndex;
528       if (!readAtomArg(&atomIndex)) {
529         return false;
530       }
531       *insn = ObjLiteralInsn(op, key, atomIndex);
532       return true;
533     }
534     *insn = ObjLiteralInsn(op, key);
535     return true;
536   }
537 };
538 
539 typedef Vector<JSAtom*, 4> ObjLiteralAtomVector;
540 
541 JSObject* InterpretObjLiteral(JSContext* cx, const ObjLiteralAtomVector& atoms,
542                               const mozilla::Span<const uint8_t> insns,
543                               ObjLiteralFlags flags);
544 
InterpretObjLiteral(JSContext * cx,const ObjLiteralAtomVector & atoms,const ObjLiteralWriter & writer)545 inline JSObject* InterpretObjLiteral(JSContext* cx,
546                                      const ObjLiteralAtomVector& atoms,
547                                      const ObjLiteralWriter& writer) {
548   return InterpretObjLiteral(cx, atoms, writer.getCode(), writer.getFlags());
549 }
550 
551 class ObjLiteralCreationData {
552  private:
553   ObjLiteralWriter writer_;
554   ObjLiteralAtomVector atoms_;
555 
556  public:
ObjLiteralCreationData(JSContext * cx)557   explicit ObjLiteralCreationData(JSContext* cx) : writer_(cx), atoms_(cx) {}
558 
writer()559   ObjLiteralWriter& writer() { return writer_; }
560 
addAtom(JSAtom * atom,uint32_t * index)561   bool addAtom(JSAtom* atom, uint32_t* index) {
562     *index = atoms_.length();
563     return atoms_.append(atom);
564   }
565 
566   JSObject* create(JSContext* cx) const;
567 };
568 
569 }  // namespace js
570 #endif  // frontend_ObjLiteral_h
571