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 #include "wasm/TypedObject-inl.h"
8 
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/Casting.h"
11 #include "mozilla/CheckedInt.h"
12 
13 #include <algorithm>
14 
15 #include "gc/Marking.h"
16 #include "js/CharacterEncoding.h"
17 #include "js/friend/ErrorMessages.h"  // js::GetErrorMessage, JSMSG_*
18 #include "js/PropertySpec.h"
19 #include "js/ScalarType.h"  // js::Scalar::Type
20 #include "js/Vector.h"
21 #include "util/StringBuffer.h"
22 #include "vm/GlobalObject.h"
23 #include "vm/JSFunction.h"
24 #include "vm/JSObject.h"
25 #include "vm/PlainObject.h"  // js::PlainObject
26 #include "vm/Realm.h"
27 #include "vm/SelfHosting.h"
28 #include "vm/StringType.h"
29 #include "vm/TypedArrayObject.h"
30 #include "vm/Uint8Clamped.h"
31 
32 #include "wasm/WasmTypes.h"  // WasmValueBox
33 #include "gc/Marking-inl.h"
34 #include "gc/Nursery-inl.h"
35 #include "gc/StoreBuffer-inl.h"
36 #include "vm/JSAtom-inl.h"
37 #include "vm/JSObject-inl.h"
38 #include "vm/NativeObject-inl.h"
39 #include "vm/Shape-inl.h"
40 
41 using mozilla::AssertedCast;
42 using mozilla::CheckedInt32;
43 using mozilla::IsPowerOfTwo;
44 using mozilla::PodCopy;
45 using mozilla::PointerRangeSize;
46 
47 using namespace js;
48 using namespace wasm;
49 
50 /***************************************************************************
51  * Typed Prototypes
52  *
53  * Every type descriptor has an associated prototype. Instances of
54  * that type descriptor use this as their prototype. Per the spec,
55  * typed object prototypes cannot be mutated.
56  */
57 
58 const JSClass js::TypedProto::class_ = {"TypedProto"};
59 
create(JSContext * cx)60 TypedProto* TypedProto::create(JSContext* cx) {
61   Handle<GlobalObject*> global = cx->global();
62   RootedObject objProto(cx,
63                         GlobalObject::getOrCreateObjectPrototype(cx, global));
64   if (!objProto) {
65     return nullptr;
66   }
67 
68   return NewTenuredObjectWithGivenProto<TypedProto>(cx, objProto);
69 }
70 
71 static const JSClassOps RttValueClassOps = {
72     nullptr,  // addProperty
73     nullptr,  // delProperty
74     nullptr,  // enumerate
75     nullptr,  // newEnumerate
76     nullptr,  // resolve
77     nullptr,  // mayResolve
78     nullptr,  // finalize
79     nullptr,  // call
80     nullptr,  // hasInstance
81     nullptr,  // construct
82     nullptr,  // trace
83 };
84 
85 const JSClass js::RttValue::class_ = {
86     "RttValue", JSCLASS_HAS_RESERVED_SLOTS(RttValue::SlotCount),
87     &RttValueClassOps};
88 
createFromHandle(JSContext * cx,TypeHandle handle)89 RttValue* RttValue::createFromHandle(JSContext* cx, TypeHandle handle) {
90   const TypeDef& type = handle.get(cx->wasm().typeContext.get());
91 
92   Rooted<RttValue*> rtt(cx,
93                         NewTenuredObjectWithGivenProto<RttValue>(cx, nullptr));
94   if (!rtt) {
95     return nullptr;
96   }
97 
98   Rooted<TypedProto*> proto(cx, TypedProto::create(cx));
99   if (!proto) {
100     return nullptr;
101   }
102 
103   rtt->initReservedSlot(RttValue::Handle, Int32Value(handle.index()));
104   rtt->initReservedSlot(RttValue::Kind, Int32Value(uint32_t(type.kind())));
105   if (type.isStructType()) {
106     const StructType& structType = type.structType();
107     rtt->initReservedSlot(RttValue::Size, Int32Value(structType.size_));
108   } else {
109     const ArrayType& arrayType = type.arrayType();
110     rtt->initReservedSlot(RttValue::Size,
111                           Int32Value(arrayType.elementType_.size()));
112   }
113   rtt->initReservedSlot(RttValue::Proto, ObjectValue(*proto));
114   rtt->initReservedSlot(RttValue::Parent, NullValue());
115 
116   if (!cx->zone()->addRttValueObject(cx, rtt)) {
117     ReportOutOfMemory(cx);
118     return nullptr;
119   }
120 
121   return rtt;
122 }
123 
createFromParent(JSContext * cx,HandleRttValue parent)124 RttValue* RttValue::createFromParent(JSContext* cx, HandleRttValue parent) {
125   wasm::TypeHandle parentHandle = parent->handle();
126   Rooted<RttValue*> rtt(cx, createFromHandle(cx, parentHandle));
127   if (!rtt) {
128     return nullptr;
129   }
130   rtt->setReservedSlot(RttValue::Parent, ObjectValue(*parent.get()));
131   return rtt;
132 }
133 
134 /******************************************************************************
135  * Typed objects
136  */
137 
typedMem() const138 uint8_t* TypedObject::typedMem() const {
139   if (is<InlineTypedObject>()) {
140     return as<InlineTypedObject>().inlineTypedMem();
141   }
142   return as<OutlineTypedObject>().outOfLineTypedMem();
143 }
144 
145 template <typename V>
visitReferences(JSContext * cx,V & visitor)146 void TypedObject::visitReferences(JSContext* cx, V& visitor) {
147   RttValue& rtt = rttValue();
148   const auto& typeDef = rtt.getType(cx);
149   uint8_t* base = typedMem();
150 
151   switch (typeDef.kind()) {
152     case TypeDefKind::Struct: {
153       const auto& structType = typeDef.structType();
154       for (const StructField& field : structType.fields_) {
155         if (field.type.isReference()) {
156           visitor.visitReference(base, field.offset);
157         }
158       }
159       break;
160     }
161     case TypeDefKind::Array: {
162       const auto& arrayType = typeDef.arrayType();
163       MOZ_ASSERT(is<OutlineTypedObject>());
164       if (arrayType.elementType_.isReference()) {
165         uint8_t* elemBase = base + OutlineTypedObject::offsetOfArrayLength() +
166                             sizeof(OutlineTypedObject::ArrayLength);
167         uint32_t length = as<OutlineTypedObject>().arrayLength();
168         for (uint32_t i = 0; i < length; i++) {
169           visitor.visitReference(elemBase, i * arrayType.elementType_.size());
170         }
171       }
172       break;
173     }
174     default:
175       MOZ_ASSERT_UNREACHABLE();
176   }
177 }
178 
179 ///////////////////////////////////////////////////////////////////////////
180 // Tracing instances
181 
182 namespace {
183 
184 class MemoryTracingVisitor {
185   JSTracer* trace_;
186 
187  public:
MemoryTracingVisitor(JSTracer * trace)188   explicit MemoryTracingVisitor(JSTracer* trace) : trace_(trace) {}
189 
190   void visitReference(uint8_t* base, size_t offset);
191 };
192 
193 }  // namespace
194 
visitReference(uint8_t * base,size_t offset)195 void MemoryTracingVisitor::visitReference(uint8_t* base, size_t offset) {
196   GCPtrObject* objectPtr = reinterpret_cast<js::GCPtrObject*>(base + offset);
197   TraceNullableEdge(trace_, objectPtr, "reference-obj");
198 }
199 
200 /******************************************************************************
201  * Outline typed objects
202  */
203 
204 /*static*/
create(JSContext * cx,HandleRttValue rtt,size_t byteLength,gc::InitialHeap heap)205 OutlineTypedObject* OutlineTypedObject::create(JSContext* cx,
206                                                HandleRttValue rtt,
207                                                size_t byteLength,
208                                                gc::InitialHeap heap) {
209   AutoSetNewObjectMetadata metadata(cx);
210 
211   RootedObject proto(cx, &rtt->typedProto());
212 
213   NewObjectKind newKind =
214       (heap == gc::TenuredHeap) ? TenuredObject : GenericObject;
215   auto* obj = NewObjectWithGivenProtoAndKinds<OutlineTypedObject>(
216       cx, proto, allocKind(), newKind);
217   if (!obj) {
218     return nullptr;
219   }
220 
221   obj->rttValue_.init(rtt);
222   uint8_t* data = (uint8_t*)js_malloc(byteLength);
223   if (!data) {
224     return nullptr;
225   }
226   obj->data_ = data;
227 
228   return obj;
229 }
230 
231 /*static*/
createStruct(JSContext * cx,HandleRttValue rtt,gc::InitialHeap heap)232 OutlineTypedObject* OutlineTypedObject::createStruct(JSContext* cx,
233                                                      HandleRttValue rtt,
234                                                      gc::InitialHeap heap) {
235   return OutlineTypedObject::create(cx, rtt, rtt->size(), heap);
236 }
237 
238 /*static*/
createArray(JSContext * cx,HandleRttValue rtt,uint32_t length,gc::InitialHeap heap)239 OutlineTypedObject* OutlineTypedObject::createArray(JSContext* cx,
240                                                     HandleRttValue rtt,
241                                                     uint32_t length,
242                                                     gc::InitialHeap heap) {
243   size_t byteLength = offsetOfArrayLength() +
244                       sizeof(OutlineTypedObject::ArrayLength) +
245                       (rtt->size() * length);
246   Rooted<OutlineTypedObject*> obj(
247       cx, OutlineTypedObject::create(cx, rtt, byteLength, heap));
248   if (!obj) {
249     return nullptr;
250   }
251 
252   obj->setArrayLength(length);
253   MOZ_ASSERT(obj->arrayLength() == length);
254   return obj;
255 }
256 
257 /*static*/
createStruct(JSContext * cx,HandleRttValue rtt,gc::InitialHeap heap)258 TypedObject* TypedObject::createStruct(JSContext* cx, HandleRttValue rtt,
259                                        gc::InitialHeap heap) {
260   RootedTypedObject typedObj(cx);
261   uint32_t totalSize = rtt->getType(cx).structType().size_;
262 
263   // If possible, create an object with inline data.
264   if (InlineTypedObject::canAccommodateSize(totalSize)) {
265     AutoSetNewObjectMetadata metadata(cx);
266     typedObj = InlineTypedObject::createStruct(cx, rtt, heap);
267   } else {
268     typedObj = OutlineTypedObject::createStruct(cx, rtt, heap);
269   }
270 
271   if (!typedObj) {
272     return nullptr;
273   }
274 
275   // Initialize the values to their defaults
276   typedObj->initDefault();
277 
278   return typedObj;
279 }
280 
281 /*static*/
createArray(JSContext * cx,HandleRttValue rtt,uint32_t length,gc::InitialHeap heap)282 TypedObject* TypedObject::createArray(JSContext* cx, HandleRttValue rtt,
283                                       uint32_t length, gc::InitialHeap heap) {
284   // Always create arrays outlined
285   RootedTypedObject typedObj(
286       cx, OutlineTypedObject::createArray(cx, rtt, length, heap));
287   if (!typedObj) {
288     return nullptr;
289   }
290 
291   // Initialize the values to their defaults
292   typedObj->initDefault();
293 
294   return typedObj;
295 }
296 
297 /* static */
allocKind()298 gc::AllocKind OutlineTypedObject::allocKind() {
299   return gc::GetGCObjectKindForBytes(sizeof(OutlineTypedObject));
300 }
301 
302 /* static */
obj_trace(JSTracer * trc,JSObject * object)303 void OutlineTypedObject::obj_trace(JSTracer* trc, JSObject* object) {
304   OutlineTypedObject& typedObj = object->as<OutlineTypedObject>();
305 
306   TraceEdge(trc, &typedObj.rttValue_, "OutlineTypedObject_rttvalue");
307 
308   if (!typedObj.data_) {
309     return;
310   }
311 
312   JSContext* cx = trc->runtime()->mainContextFromOwnThread();
313   MemoryTracingVisitor visitor(trc);
314   typedObj.visitReferences(cx, visitor);
315 }
316 
317 /* static */
obj_finalize(JSFreeOp * fop,JSObject * object)318 void OutlineTypedObject::obj_finalize(JSFreeOp* fop, JSObject* object) {
319   OutlineTypedObject& typedObj = object->as<OutlineTypedObject>();
320 
321   if (typedObj.data_) {
322     js_free(typedObj.data_);
323     typedObj.data_ = nullptr;
324   }
325 }
326 
getType(JSContext * cx) const327 const TypeDef& RttValue::getType(JSContext* cx) const {
328   return handle().get(cx->wasm().typeContext.get());
329 }
330 
lookupProperty(JSContext * cx,HandleTypedObject object,jsid id,uint32_t * offset,FieldType * type)331 bool RttValue::lookupProperty(JSContext* cx, HandleTypedObject object, jsid id,
332                               uint32_t* offset, FieldType* type) {
333   const auto& typeDef = getType(cx);
334 
335   switch (typeDef.kind()) {
336     case wasm::TypeDefKind::Struct: {
337       const auto& structType = typeDef.structType();
338       uint32_t index;
339       if (!IdIsIndex(id, &index)) {
340         return false;
341       }
342       if (index >= structType.fields_.length()) {
343         return false;
344       }
345       const StructField& field = structType.fields_[index];
346       *offset = field.offset;
347       *type = field.type;
348       return true;
349     }
350     case wasm::TypeDefKind::Array: {
351       const auto& arrayType = typeDef.arrayType();
352 
353       // Special case for property 'length' that loads the length field at the
354       // beginning of the data buffer
355       if (id.isString() &&
356           id.toString() == cx->runtime()->commonNames->length) {
357         STATIC_ASSERT_ARRAYLENGTH_IS_U32;
358         *type = FieldType::I32;
359         *offset = OutlineTypedObject::offsetOfArrayLength();
360         return true;
361       }
362 
363       // Normal case of indexed properties for loading array elements
364       uint32_t index;
365       if (!IdIsIndex(id, &index)) {
366         return false;
367       }
368       OutlineTypedObject::ArrayLength arrayLength =
369           object->as<OutlineTypedObject>().arrayLength();
370       if (index >= arrayLength) {
371         return false;
372       }
373       *offset = OutlineTypedObject::offsetOfArrayLength() +
374                 sizeof(OutlineTypedObject::ArrayLength) +
375                 index * arrayType.elementType_.size();
376       *type = arrayType.elementType_;
377       return true;
378     }
379     default:
380       MOZ_ASSERT_UNREACHABLE();
381       return false;
382   }
383 }
384 
385 /* static */
obj_lookupProperty(JSContext * cx,HandleObject obj,HandleId id,MutableHandleObject objp,PropertyResult * propp)386 bool TypedObject::obj_lookupProperty(JSContext* cx, HandleObject obj,
387                                      HandleId id, MutableHandleObject objp,
388                                      PropertyResult* propp) {
389   RootedTypedObject typedObj(cx, &obj->as<TypedObject>());
390   if (typedObj->rttValue().hasProperty(cx, typedObj, id)) {
391     propp->setTypedObjectProperty();
392     objp.set(obj);
393     return true;
394   }
395 
396   RootedObject proto(cx, obj->staticPrototype());
397   if (!proto) {
398     objp.set(nullptr);
399     propp->setNotFound();
400     return true;
401   }
402 
403   return LookupProperty(cx, proto, id, objp, propp);
404 }
405 
obj_defineProperty(JSContext * cx,HandleObject obj,HandleId id,Handle<PropertyDescriptor> desc,ObjectOpResult & result)406 bool TypedObject::obj_defineProperty(JSContext* cx, HandleObject obj,
407                                      HandleId id,
408                                      Handle<PropertyDescriptor> desc,
409                                      ObjectOpResult& result) {
410   JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
411                            JSMSG_OBJECT_NOT_EXTENSIBLE, "TypedObject");
412   return false;
413 }
414 
obj_hasProperty(JSContext * cx,HandleObject obj,HandleId id,bool * foundp)415 bool TypedObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id,
416                                   bool* foundp) {
417   RootedTypedObject typedObj(cx, &obj->as<TypedObject>());
418   if (typedObj->rttValue().hasProperty(cx, typedObj, id)) {
419     *foundp = true;
420     return true;
421   }
422 
423   RootedObject proto(cx, obj->staticPrototype());
424   if (!proto) {
425     *foundp = false;
426     return true;
427   }
428 
429   return HasProperty(cx, proto, id, foundp);
430 }
431 
obj_getProperty(JSContext * cx,HandleObject obj,HandleValue receiver,HandleId id,MutableHandleValue vp)432 bool TypedObject::obj_getProperty(JSContext* cx, HandleObject obj,
433                                   HandleValue receiver, HandleId id,
434                                   MutableHandleValue vp) {
435   Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
436 
437   uint32_t offset;
438   FieldType type;
439   if (typedObj->rttValue().lookupProperty(cx, typedObj, id, &offset, &type)) {
440     return typedObj->loadValue(cx, offset, type, vp);
441   }
442 
443   RootedObject proto(cx, obj->staticPrototype());
444   if (!proto) {
445     vp.setUndefined();
446     return true;
447   }
448 
449   return GetProperty(cx, proto, receiver, id, vp);
450 }
451 
obj_setProperty(JSContext * cx,HandleObject obj,HandleId id,HandleValue v,HandleValue receiver,ObjectOpResult & result)452 bool TypedObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id,
453                                   HandleValue v, HandleValue receiver,
454                                   ObjectOpResult& result) {
455   RootedTypedObject typedObj(cx, &obj->as<TypedObject>());
456 
457   if (typedObj->rttValue().hasProperty(cx, typedObj, id)) {
458     if (!receiver.isObject() || obj != &receiver.toObject()) {
459       return SetPropertyByDefining(cx, id, v, receiver, result);
460     }
461 
462     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
463                               JSMSG_TYPEDOBJECT_SETTING_IMMUTABLE);
464     return false;
465   }
466 
467   return SetPropertyOnProto(cx, obj, id, v, receiver, result);
468 }
469 
obj_getOwnPropertyDescriptor(JSContext * cx,HandleObject obj,HandleId id,MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc)470 bool TypedObject::obj_getOwnPropertyDescriptor(
471     JSContext* cx, HandleObject obj, HandleId id,
472     MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc) {
473   Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
474 
475   uint32_t offset;
476   FieldType type;
477   if (typedObj->rttValue().lookupProperty(cx, typedObj, id, &offset, &type)) {
478     RootedValue value(cx);
479     if (!typedObj->loadValue(cx, offset, type, &value)) {
480       return false;
481     }
482     desc.set(mozilla::Some(PropertyDescriptor::Data(
483         value,
484         {JS::PropertyAttribute::Enumerable, JS::PropertyAttribute::Writable})));
485     return true;
486   }
487 
488   desc.reset();
489   return true;
490 }
491 
obj_deleteProperty(JSContext * cx,HandleObject obj,HandleId id,ObjectOpResult & result)492 bool TypedObject::obj_deleteProperty(JSContext* cx, HandleObject obj,
493                                      HandleId id, ObjectOpResult& result) {
494   RootedTypedObject typedObj(cx, &obj->as<TypedObject>());
495   if (typedObj->rttValue().hasProperty(cx, typedObj, id)) {
496     return Throw(cx, id, JSMSG_CANT_DELETE);
497   }
498 
499   RootedObject proto(cx, obj->staticPrototype());
500   if (!proto) {
501     return result.succeed();
502   }
503 
504   return DeleteProperty(cx, proto, id, result);
505 }
506 
obj_newEnumerate(JSContext * cx,HandleObject obj,MutableHandleIdVector properties,bool enumerableOnly)507 bool TypedObject::obj_newEnumerate(JSContext* cx, HandleObject obj,
508                                    MutableHandleIdVector properties,
509                                    bool enumerableOnly) {
510   MOZ_ASSERT(obj->is<TypedObject>());
511   Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
512 
513   const auto& rtt = typedObj->rttValue();
514   const auto& typeDef = rtt.getType(cx);
515 
516   size_t indexCount = 0;
517   size_t otherCount = 0;
518   switch (typeDef.kind()) {
519     case wasm::TypeDefKind::Struct: {
520       indexCount = typeDef.structType().fields_.length();
521       break;
522     }
523     case wasm::TypeDefKind::Array: {
524       indexCount = typedObj->as<OutlineTypedObject>().arrayLength();
525       otherCount = 1;
526       break;
527     }
528     default:
529       MOZ_ASSERT_UNREACHABLE();
530   }
531 
532   if (!properties.reserve(indexCount + otherCount)) {
533     return false;
534   }
535   RootedId id(cx);
536   for (size_t index = 0; index < indexCount; index++) {
537     id = INT_TO_JSID(index);
538     properties.infallibleAppend(id);
539   }
540 
541   if (typeDef.kind() == wasm::TypeDefKind::Array) {
542     properties.infallibleAppend(
543         JS::PropertyKey::fromNonIntAtom(cx->runtime()->commonNames->length));
544   }
545 
546   return true;
547 }
548 
loadValue(JSContext * cx,size_t offset,FieldType type,MutableHandleValue vp)549 bool TypedObject::loadValue(JSContext* cx, size_t offset, FieldType type,
550                             MutableHandleValue vp) {
551   // Temporary hack, (ref T) is not exposable to JS yet but some tests would
552   // like to access it so we erase (ref T) with eqref when loading. This is
553   // safe as (ref T) <: eqref and we're not in the writing case where we
554   // would need to perform a type check.
555   if (type.isTypeIndex()) {
556     type = RefType::fromTypeCode(TypeCode::EqRef, true);
557   }
558   if (!type.isExposable()) {
559     JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
560                              JSMSG_WASM_BAD_VAL_TYPE);
561     return false;
562   }
563   return ToJSValue(cx, typedMem() + offset, type, vp);
564 }
565 
initDefault()566 void TypedObject::initDefault() {
567   RttValue& rtt = rttValue();
568   switch (rtt.kind()) {
569     case TypeDefKind::Struct: {
570       memset(typedMem(), 0, rtt.size());
571       break;
572     }
573     case TypeDefKind::Array: {
574       MOZ_ASSERT(is<OutlineTypedObject>());
575       uint32_t length = as<OutlineTypedObject>().arrayLength();
576       memset(typedMem() + sizeof(uint32_t), 0, rtt.size() * length);
577       break;
578     }
579     default:
580       MOZ_ASSERT_UNREACHABLE();
581   }
582 }
583 
584 /******************************************************************************
585  * Inline typed objects
586  */
587 
588 /* static */
createStruct(JSContext * cx,HandleRttValue rtt,gc::InitialHeap heap)589 InlineTypedObject* InlineTypedObject::createStruct(JSContext* cx,
590                                                    HandleRttValue rtt,
591                                                    gc::InitialHeap heap) {
592   MOZ_ASSERT(rtt->kind() == wasm::TypeDefKind::Struct);
593 
594   gc::AllocKind allocKind = allocKindForRttValue(rtt);
595 
596   RootedObject proto(cx, &rtt->typedProto());
597 
598   NewObjectKind newKind =
599       (heap == gc::TenuredHeap) ? TenuredObject : GenericObject;
600   auto* obj = NewObjectWithGivenProtoAndKinds<InlineTypedObject>(
601       cx, proto, allocKind, newKind);
602   if (!obj) {
603     return nullptr;
604   }
605 
606   obj->rttValue_.init(rtt);
607   return obj;
608 }
609 
610 /* static */
obj_trace(JSTracer * trc,JSObject * object)611 void InlineTypedObject::obj_trace(JSTracer* trc, JSObject* object) {
612   InlineTypedObject& typedObj = object->as<InlineTypedObject>();
613 
614   TraceEdge(trc, &typedObj.rttValue_, "InlineTypedObject_rttvalue");
615 
616   JSContext* cx = trc->runtime()->mainContextFromOwnThread();
617   MemoryTracingVisitor visitor(trc);
618   typedObj.visitReferences(cx, visitor);
619 }
620 
621 /* static */
obj_moved(JSObject * dst,JSObject * src)622 size_t InlineTypedObject::obj_moved(JSObject* dst, JSObject* src) { return 0; }
623 
624 /******************************************************************************
625  * Typed object classes
626  */
627 
628 const ObjectOps TypedObject::objectOps_ = {
629     TypedObject::obj_lookupProperty,            // lookupProperty
630     TypedObject::obj_defineProperty,            // defineProperty
631     TypedObject::obj_hasProperty,               // hasProperty
632     TypedObject::obj_getProperty,               // getProperty
633     TypedObject::obj_setProperty,               // setProperty
634     TypedObject::obj_getOwnPropertyDescriptor,  // getOwnPropertyDescriptor
635     TypedObject::obj_deleteProperty,            // deleteProperty
636     nullptr,                                    // getElements
637     nullptr,                                    // funToString
638 };
639 
640 #define DEFINE_TYPEDOBJ_CLASS(Name, Trace, Finalize, Moved, Flags)    \
641   static const JSClassOps Name##ClassOps = {                          \
642       nullptr, /* addProperty */                                      \
643       nullptr, /* delProperty */                                      \
644       nullptr, /* enumerate   */                                      \
645       TypedObject::obj_newEnumerate,                                  \
646       nullptr,  /* resolve     */                                     \
647       nullptr,  /* mayResolve  */                                     \
648       Finalize, /* finalize    */                                     \
649       nullptr,  /* call        */                                     \
650       nullptr,  /* hasInstance */                                     \
651       nullptr,  /* construct   */                                     \
652       Trace,                                                          \
653   };                                                                  \
654   static const ClassExtension Name##ClassExt = {                      \
655       Moved /* objectMovedOp */                                       \
656   };                                                                  \
657   const JSClass Name::class_ = {                                      \
658       #Name,                                                          \
659       JSClass::NON_NATIVE | JSCLASS_DELAY_METADATA_BUILDER | (Flags), \
660       &Name##ClassOps,                                                \
661       JS_NULL_CLASS_SPEC,                                             \
662       &Name##ClassExt,                                                \
663       &TypedObject::objectOps_}
664 
665 DEFINE_TYPEDOBJ_CLASS(OutlineTypedObject, OutlineTypedObject::obj_trace,
666                       OutlineTypedObject::obj_finalize, nullptr,
667                       JSCLASS_FOREGROUND_FINALIZE);
668 DEFINE_TYPEDOBJ_CLASS(InlineTypedObject, InlineTypedObject::obj_trace, nullptr,
669                       InlineTypedObject::obj_moved, 0);
670 
create(JSContext * cx,js::gc::AllocKind kind,js::gc::InitialHeap heap,js::HandleShape shape)671 /* static */ JS::Result<TypedObject*, JS::OOM> TypedObject::create(
672     JSContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
673     js::HandleShape shape) {
674   debugCheckNewObject(shape, kind, heap);
675 
676   const JSClass* clasp = shape->getObjectClass();
677   MOZ_ASSERT(!clasp->isNativeObject());
678   MOZ_ASSERT(::IsTypedObjectClass(clasp));
679 
680   JSObject* obj =
681       js::AllocateObject(cx, kind, /* nDynamicSlots = */ 0, heap, clasp);
682   if (!obj) {
683     return cx->alreadyReportedOOM();
684   }
685 
686   TypedObject* tobj = static_cast<TypedObject*>(obj);
687   tobj->initShape(shape);
688 
689   MOZ_ASSERT(clasp->shouldDelayMetadataBuilder());
690   cx->realm()->setObjectPendingMetadata(cx, tobj);
691 
692   js::gc::gcprobes::CreateObject(tobj);
693 
694   return tobj;
695 }
696 
isRuntimeSubtype(HandleRttValue rtt) const697 bool TypedObject::isRuntimeSubtype(HandleRttValue rtt) const {
698   RttValue* current = &rttValue();
699   while (current != nullptr) {
700     if (current == rtt.get()) {
701       return true;
702     }
703     current = current->parent();
704   }
705   return false;
706 }
707