Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
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_UnboxedObject_h
8 : #define vm_UnboxedObject_h
9 :
10 : #include "jsgc.h"
11 : #include "jsobj.h"
12 :
13 : #include "gc/Zone.h"
14 : #include "vm/Runtime.h"
15 : #include "vm/TypeInference.h"
16 :
17 : namespace js {
18 :
19 : // Memory required for an unboxed value of a given type. Returns zero for types
20 : // which can't be used for unboxed objects.
21 : static inline size_t
22 133 : UnboxedTypeSize(JSValueType type)
23 : {
24 133 : switch (type) {
25 75 : case JSVAL_TYPE_BOOLEAN: return 1;
26 0 : case JSVAL_TYPE_INT32: return 4;
27 0 : case JSVAL_TYPE_DOUBLE: return 8;
28 12 : case JSVAL_TYPE_STRING: return sizeof(void*);
29 36 : case JSVAL_TYPE_OBJECT: return sizeof(void*);
30 10 : default: return 0;
31 : }
32 : }
33 :
34 : static inline bool
35 3 : UnboxedTypeNeedsPreBarrier(JSValueType type)
36 : {
37 3 : return type == JSVAL_TYPE_STRING || type == JSVAL_TYPE_OBJECT;
38 : }
39 :
40 : static inline bool
41 10 : UnboxedTypeNeedsPostBarrier(JSValueType type)
42 : {
43 10 : return type == JSVAL_TYPE_OBJECT;
44 : }
45 :
46 : // Class tracking information specific to unboxed objects.
47 : class UnboxedLayout : public mozilla::LinkedListElement<UnboxedLayout>
48 : {
49 : public:
50 : struct Property {
51 : PropertyName* name;
52 : uint32_t offset;
53 : JSValueType type;
54 :
55 54 : Property()
56 54 : : name(nullptr), offset(UINT32_MAX), type(JSVAL_TYPE_MAGIC)
57 54 : {}
58 : };
59 :
60 : typedef Vector<Property, 0, SystemAllocPolicy> PropertyVector;
61 :
62 : private:
63 : Zone* zone_;
64 :
65 : // If objects in this group have ever been converted to native objects,
66 : // these store the corresponding native group and initial shape for such
67 : // objects. Type information for this object is reflected in nativeGroup.
68 : GCPtrObjectGroup nativeGroup_;
69 : GCPtrShape nativeShape_;
70 :
71 : // Any script/pc which the associated group is created for.
72 : GCPtrScript allocationScript_;
73 : jsbytecode* allocationPc_;
74 :
75 : // If nativeGroup is set and this object originally had a TypeNewScript or
76 : // was keyed to an allocation site, this points to the group which replaced
77 : // this one. This link is only needed to keep the replacement group from
78 : // being GC'ed. If it were GC'ed and a new one regenerated later, that new
79 : // group might have a different allocation kind from this group.
80 : GCPtrObjectGroup replacementGroup_;
81 :
82 : // The following members are only used for unboxed plain objects.
83 :
84 : // All properties on objects with this layout, in enumeration order.
85 : PropertyVector properties_;
86 :
87 : // Byte size of the data for objects with this layout.
88 : size_t size_;
89 :
90 : // Any 'new' script information associated with this layout.
91 : TypeNewScript* newScript_;
92 :
93 : // List for use in tracing objects with this layout. This has the same
94 : // structure as the trace list on a TypeDescr.
95 : int32_t* traceList_;
96 :
97 : // If this layout has been used to construct script or JSON constant
98 : // objects, this code might be filled in to more quickly fill in objects
99 : // from an array of values.
100 : GCPtrJitCode constructorCode_;
101 :
102 : // The following members are only used for unboxed arrays.
103 :
104 : // The type of array elements.
105 : JSValueType elementType_;
106 :
107 : public:
108 19 : explicit UnboxedLayout(Zone* zone)
109 19 : : zone_(zone), nativeGroup_(nullptr), nativeShape_(nullptr),
110 : allocationScript_(nullptr), allocationPc_(nullptr), replacementGroup_(nullptr),
111 : size_(0), newScript_(nullptr), traceList_(nullptr), constructorCode_(nullptr),
112 19 : elementType_(JSVAL_TYPE_MAGIC)
113 19 : {}
114 :
115 : Zone* zone() const { return zone_; }
116 :
117 19 : bool initProperties(const PropertyVector& properties, size_t size) {
118 19 : size_ = size;
119 19 : return properties_.appendAll(properties);
120 : }
121 :
122 0 : void initArray(JSValueType elementType) {
123 0 : elementType_ = elementType;
124 0 : }
125 :
126 0 : ~UnboxedLayout() {
127 0 : if (newScript_)
128 0 : newScript_->clear();
129 0 : js_delete(newScript_);
130 0 : js_free(traceList_);
131 :
132 0 : nativeGroup_.init(nullptr);
133 0 : nativeShape_.init(nullptr);
134 0 : replacementGroup_.init(nullptr);
135 0 : constructorCode_.init(nullptr);
136 0 : }
137 :
138 24 : bool isArray() const {
139 24 : return elementType_ != JSVAL_TYPE_MAGIC;
140 : }
141 :
142 : void detachFromCompartment();
143 :
144 2445 : const PropertyVector& properties() const {
145 2445 : return properties_;
146 : }
147 :
148 4 : TypeNewScript* newScript() const {
149 4 : return newScript_;
150 : }
151 :
152 : void setNewScript(TypeNewScript* newScript, bool writeBarrier = true);
153 :
154 8 : JSScript* allocationScript() const {
155 8 : return allocationScript_;
156 : }
157 :
158 4 : jsbytecode* allocationPc() const {
159 4 : return allocationPc_;
160 : }
161 :
162 19 : void setAllocationSite(JSScript* script, jsbytecode* pc) {
163 19 : allocationScript_ = script;
164 19 : allocationPc_ = pc;
165 19 : }
166 :
167 388 : const int32_t* traceList() const {
168 388 : return traceList_;
169 : }
170 :
171 19 : void setTraceList(int32_t* traceList) {
172 19 : traceList_ = traceList;
173 19 : }
174 :
175 11684 : const Property* lookup(JSAtom* atom) const {
176 30981 : for (size_t i = 0; i < properties_.length(); i++) {
177 27224 : if (properties_[i].name == atom)
178 7927 : return &properties_[i];
179 : }
180 3757 : return nullptr;
181 : }
182 :
183 11684 : const Property* lookup(jsid id) const {
184 11684 : if (JSID_IS_STRING(id))
185 11684 : return lookup(JSID_TO_ATOM(id));
186 0 : return nullptr;
187 : }
188 :
189 545 : size_t size() const {
190 545 : return size_;
191 : }
192 :
193 19 : ObjectGroup* nativeGroup() const {
194 19 : return nativeGroup_;
195 : }
196 :
197 4 : Shape* nativeShape() const {
198 4 : return nativeShape_;
199 : }
200 :
201 0 : jit::JitCode* constructorCode() const {
202 0 : return constructorCode_;
203 : }
204 :
205 0 : void setConstructorCode(jit::JitCode* code) {
206 0 : constructorCode_ = code;
207 0 : }
208 :
209 0 : JSValueType elementType() const {
210 0 : return elementType_;
211 : }
212 :
213 : inline gc::AllocKind getAllocKind() const;
214 :
215 : void trace(JSTracer* trc);
216 :
217 : size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
218 :
219 : static bool makeNativeGroup(JSContext* cx, ObjectGroup* group);
220 : static bool makeConstructorCode(JSContext* cx, HandleObjectGroup group);
221 : };
222 :
223 : class UnboxedObject : public JSObject
224 : {
225 : protected:
226 : static JS::Result<UnboxedObject*, JS::OOM&>
227 : createInternal(JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
228 : js::HandleObjectGroup group);
229 : };
230 :
231 : // Class for expando objects holding extra properties given to an unboxed plain
232 : // object. These objects behave identically to normal native plain objects, and
233 : // have a separate Class to distinguish them for memory usage reporting.
234 : class UnboxedExpandoObject : public NativeObject
235 : {
236 : public:
237 : static const Class class_;
238 : };
239 :
240 : // Class for a plain object using an unboxed representation. The physical
241 : // layout of these objects is identical to that of an InlineTypedObject, though
242 : // these objects use an UnboxedLayout instead of a TypeDescr to keep track of
243 : // how their properties are stored.
244 : class UnboxedPlainObject : public UnboxedObject
245 : {
246 : // Optional object which stores extra properties on this object. This is
247 : // not automatically barriered to avoid problems if the object is converted
248 : // to a native. See ensureExpando().
249 : UnboxedExpandoObject* expando_;
250 :
251 : // Start of the inline data, which immediately follows the group and extra properties.
252 : uint8_t data_[1];
253 :
254 : public:
255 : static const Class class_;
256 :
257 : static bool obj_lookupProperty(JSContext* cx, HandleObject obj,
258 : HandleId id, MutableHandleObject objp,
259 : MutableHandle<PropertyResult> propp);
260 :
261 : static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
262 : Handle<PropertyDescriptor> desc,
263 : ObjectOpResult& result);
264 :
265 : static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
266 :
267 : static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
268 : HandleId id, MutableHandleValue vp);
269 :
270 : static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
271 : HandleValue receiver, ObjectOpResult& result);
272 :
273 : static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
274 : MutableHandle<PropertyDescriptor> desc);
275 :
276 : static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
277 : ObjectOpResult& result);
278 :
279 : static bool newEnumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
280 : bool enumerableOnly);
281 : static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
282 :
283 : inline const UnboxedLayout& layout() const;
284 :
285 228 : const UnboxedLayout& layoutDontCheckGeneration() const {
286 228 : return group()->unboxedLayoutDontCheckGeneration();
287 : }
288 :
289 590 : uint8_t* data() {
290 590 : return &data_[0];
291 : }
292 :
293 3986 : UnboxedExpandoObject* maybeExpando() const {
294 3986 : return expando_;
295 : }
296 :
297 432 : void initExpando() {
298 432 : expando_ = nullptr;
299 432 : }
300 :
301 : // For use during GC.
302 158 : JSObject** addressOfExpando() {
303 158 : return reinterpret_cast<JSObject**>(&expando_);
304 : }
305 :
306 : bool containsUnboxedOrExpandoProperty(JSContext* cx, jsid id) const;
307 :
308 : static UnboxedExpandoObject* ensureExpando(JSContext* cx, Handle<UnboxedPlainObject*> obj);
309 :
310 : bool setValue(JSContext* cx, const UnboxedLayout::Property& property, const Value& v);
311 : Value getValue(const UnboxedLayout::Property& property, bool maybeUninitialized = false);
312 :
313 : static bool convertToNative(JSContext* cx, JSObject* obj);
314 : static UnboxedPlainObject* create(JSContext* cx, HandleObjectGroup group,
315 : NewObjectKind newKind);
316 : static JSObject* createWithProperties(JSContext* cx, HandleObjectGroup group,
317 : NewObjectKind newKind, IdValuePair* properties);
318 :
319 : void fillAfterConvert(JSContext* cx,
320 : Handle<GCVector<Value>> values, size_t* valueCursor);
321 :
322 : static void trace(JSTracer* trc, JSObject* object);
323 :
324 20 : static size_t offsetOfExpando() {
325 20 : return offsetof(UnboxedPlainObject, expando_);
326 : }
327 :
328 207 : static size_t offsetOfData() {
329 207 : return offsetof(UnboxedPlainObject, data_[0]);
330 : }
331 : };
332 :
333 : // Try to construct an UnboxedLayout for each of the preliminary objects,
334 : // provided they all match the template shape. If successful, converts the
335 : // preliminary objects and their group to the new unboxed representation.
336 : bool
337 : TryConvertToUnboxedLayout(JSContext* cx, AutoEnterAnalysis& enter, Shape* templateShape,
338 : ObjectGroup* group, PreliminaryObjectArray* objects);
339 :
340 : inline gc::AllocKind
341 56 : UnboxedLayout::getAllocKind() const
342 : {
343 56 : MOZ_ASSERT(size());
344 56 : return gc::GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + size());
345 : }
346 :
347 : // Class for an array object using an unboxed representation.
348 : class UnboxedArrayObject : public UnboxedObject
349 : {
350 : // Elements pointer for the object.
351 : uint8_t* elements_;
352 :
353 : // The nominal array length. This always fits in an int32_t.
354 : uint32_t length_;
355 :
356 : // Value indicating the allocated capacity and initialized length of the
357 : // array. The top CapacityBits bits are an index into CapacityArray, which
358 : // indicates the elements capacity. The low InitializedLengthBits store the
359 : // initialized length of the array.
360 : uint32_t capacityIndexAndInitializedLength_;
361 :
362 : // If the elements are inline, they will point here.
363 : uint8_t inlineElements_[1];
364 :
365 : public:
366 : static const uint32_t CapacityBits = 6;
367 : static const uint32_t CapacityShift = 26;
368 :
369 : static const uint32_t CapacityMask = uint32_t(-1) << CapacityShift;
370 : static const uint32_t InitializedLengthMask = (1 << CapacityShift) - 1;
371 :
372 : static const uint32_t MaximumCapacity = InitializedLengthMask;
373 : static const uint32_t MinimumDynamicCapacity = 8;
374 :
375 : static const uint32_t CapacityArray[];
376 :
377 : // Capacity index which indicates the array's length is also its capacity.
378 : static const uint32_t CapacityMatchesLengthIndex = 0;
379 :
380 : private:
381 0 : static inline uint32_t computeCapacity(uint32_t index, uint32_t length) {
382 0 : if (index == CapacityMatchesLengthIndex)
383 0 : return length;
384 0 : return CapacityArray[index];
385 : }
386 :
387 : static uint32_t chooseCapacityIndex(uint32_t capacity, uint32_t length);
388 : static uint32_t exactCapacityIndex(uint32_t capacity);
389 :
390 : public:
391 : static const Class class_;
392 :
393 : static bool obj_lookupProperty(JSContext* cx, HandleObject obj,
394 : HandleId id, MutableHandleObject objp,
395 : MutableHandle<PropertyResult> propp);
396 :
397 : static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
398 : Handle<PropertyDescriptor> desc,
399 : ObjectOpResult& result);
400 :
401 : static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
402 :
403 : static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
404 : HandleId id, MutableHandleValue vp);
405 :
406 : static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
407 : HandleValue receiver, ObjectOpResult& result);
408 :
409 : static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
410 : MutableHandle<PropertyDescriptor> desc);
411 :
412 : static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
413 : ObjectOpResult& result);
414 :
415 : static bool newEnumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
416 : bool enumerableOnly);
417 : static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
418 :
419 : inline const UnboxedLayout& layout() const;
420 :
421 0 : const UnboxedLayout& layoutDontCheckGeneration() const {
422 0 : return group()->unboxedLayoutDontCheckGeneration();
423 : }
424 :
425 0 : JSValueType elementType() const {
426 0 : return layoutDontCheckGeneration().elementType();
427 : }
428 :
429 0 : uint32_t elementSize() const {
430 0 : return UnboxedTypeSize(elementType());
431 : }
432 :
433 : static bool convertToNative(JSContext* cx, JSObject* obj);
434 : static UnboxedArrayObject* create(JSContext* cx, HandleObjectGroup group,
435 : uint32_t length, NewObjectKind newKind,
436 : uint32_t maxLength = MaximumCapacity);
437 :
438 : static bool convertToNativeWithGroup(JSContext* cx, JSObject* obj,
439 : ObjectGroup* group, Shape* shape);
440 : bool convertInt32ToDouble(JSContext* cx, ObjectGroup* group);
441 :
442 : void fillAfterConvert(JSContext* cx,
443 : Handle<GCVector<Value>> values, size_t* valueCursor);
444 :
445 : static void trace(JSTracer* trc, JSObject* object);
446 : static void objectMoved(JSObject* obj, const JSObject* old);
447 : static void finalize(FreeOp* fop, JSObject* obj);
448 :
449 : static size_t objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src,
450 : gc::AllocKind allocKind);
451 :
452 0 : uint8_t* elements() {
453 0 : return elements_;
454 : }
455 :
456 0 : bool hasInlineElements() const {
457 0 : return elements_ == &inlineElements_[0];
458 : }
459 :
460 0 : uint32_t length() const {
461 0 : return length_;
462 : }
463 :
464 0 : uint32_t initializedLength() const {
465 0 : return capacityIndexAndInitializedLength_ & InitializedLengthMask;
466 : }
467 :
468 0 : uint32_t capacityIndex() const {
469 0 : return (capacityIndexAndInitializedLength_ & CapacityMask) >> CapacityShift;
470 : }
471 :
472 0 : uint32_t capacity() const {
473 0 : return computeCapacity(capacityIndex(), length());
474 : }
475 :
476 : bool containsProperty(JSContext* cx, jsid id);
477 :
478 : bool setElement(JSContext* cx, size_t index, const Value& v);
479 : bool initElement(JSContext* cx, size_t index, const Value& v);
480 : void initElementNoTypeChange(size_t index, const Value& v);
481 : Value getElement(size_t index);
482 :
483 : template <JSValueType Type> inline bool setElementSpecific(JSContext* cx, size_t index,
484 : const Value& v);
485 : template <JSValueType Type> inline void setElementNoTypeChangeSpecific(size_t index, const Value& v);
486 : template <JSValueType Type> inline bool initElementSpecific(JSContext* cx, size_t index,
487 : const Value& v);
488 : template <JSValueType Type> inline void initElementNoTypeChangeSpecific(size_t index, const Value& v);
489 : template <JSValueType Type> inline Value getElementSpecific(size_t index);
490 : template <JSValueType Type> inline void triggerPreBarrier(size_t index);
491 :
492 : bool growElements(JSContext* cx, size_t cap);
493 : void shrinkElements(JSContext* cx, size_t cap);
494 :
495 0 : static uint32_t offsetOfElements() {
496 0 : return offsetof(UnboxedArrayObject, elements_);
497 : }
498 0 : static uint32_t offsetOfLength() {
499 0 : return offsetof(UnboxedArrayObject, length_);
500 : }
501 0 : static uint32_t offsetOfCapacityIndexAndInitializedLength() {
502 0 : return offsetof(UnboxedArrayObject, capacityIndexAndInitializedLength_);
503 : }
504 0 : static uint32_t offsetOfInlineElements() {
505 0 : return offsetof(UnboxedArrayObject, inlineElements_);
506 : }
507 :
508 0 : void setLengthInt32(uint32_t length) {
509 0 : MOZ_ASSERT(length <= INT32_MAX);
510 0 : length_ = length;
511 0 : }
512 :
513 : inline void setLength(JSContext* cx, uint32_t len);
514 : inline void setInitializedLength(uint32_t initlen);
515 :
516 0 : inline void setInitializedLengthNoBarrier(uint32_t initlen) {
517 0 : MOZ_ASSERT(initlen <= InitializedLengthMask);
518 0 : capacityIndexAndInitializedLength_ =
519 0 : (capacityIndexAndInitializedLength_ & CapacityMask) | initlen;
520 0 : }
521 :
522 : private:
523 0 : void setInlineElements() {
524 0 : elements_ = &inlineElements_[0];
525 0 : }
526 :
527 0 : void setCapacityIndex(uint32_t index) {
528 0 : MOZ_ASSERT(index <= (CapacityMask >> CapacityShift));
529 0 : capacityIndexAndInitializedLength_ =
530 0 : (index << CapacityShift) | initializedLength();
531 0 : }
532 : };
533 :
534 : } // namespace js
535 :
536 : namespace JS {
537 :
538 : template <>
539 : struct DeletePolicy<js::UnboxedLayout> : public js::GCManagedDeletePolicy<js::UnboxedLayout>
540 : {};
541 :
542 : } /* namespace JS */
543 :
544 : #endif /* vm_UnboxedObject_h */
|