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