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