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 #ifndef wasm_TypedObject_h
8 #define wasm_TypedObject_h
9
10 #include "mozilla/CheckedInt.h"
11 #include "mozilla/Maybe.h"
12
13 #include "gc/Allocator.h"
14 #include "vm/ArrayBufferObject.h"
15 #include "vm/JSObject.h"
16 #include "wasm/WasmTypes.h"
17
18 namespace js {
19
20 /* The prototype for typed objects. */
21 class TypedProto : public NativeObject {
22 public:
23 static const JSClass class_;
24 static TypedProto* create(JSContext* cx);
25 };
26
27 class TypedObject;
28
29 class RttValue : public NativeObject {
30 public:
31 static const JSClass class_;
32
33 enum Slot {
34 Handle = 0, // Type handle index
35 Kind = 1, // Kind of type
36 Size = 2, // Size of struct, or size of array element
37 Proto = 3, // Prototype for instances, if any
38 Parent = 4, // Parent rtt for runtime casting
39 // Maximum number of slots
40 SlotCount = 5,
41 };
42
43 static RttValue* createFromHandle(JSContext* cx, wasm::TypeHandle handle);
44 static RttValue* createFromParent(JSContext* cx,
45 js::Handle<RttValue*> parent);
46
handle()47 wasm::TypeHandle handle() const {
48 return wasm::TypeHandle(uint32_t(getReservedSlot(Slot::Handle).toInt32()));
49 }
50
kind()51 wasm::TypeDefKind kind() const {
52 return wasm::TypeDefKind(getReservedSlot(Slot::Kind).toInt32());
53 }
54
size()55 size_t size() const { return getReservedSlot(Slot::Size).toInt32(); }
56
typedProto()57 TypedProto& typedProto() const {
58 return getReservedSlot(Slot::Proto).toObject().as<TypedProto>();
59 }
60
parent()61 RttValue* parent() const {
62 return (RttValue*)getReservedSlot(Slot::Parent).toObjectOrNull();
63 }
64
65 const wasm::TypeDef& getType(JSContext* cx) const;
66
67 [[nodiscard]] bool lookupProperty(JSContext* cx,
68 js::Handle<TypedObject*> object, jsid id,
69 uint32_t* offset, wasm::FieldType* type);
hasProperty(JSContext * cx,js::Handle<TypedObject * > object,jsid id)70 [[nodiscard]] bool hasProperty(JSContext* cx, js::Handle<TypedObject*> object,
71 jsid id) {
72 uint32_t offset;
73 wasm::FieldType type;
74 return lookupProperty(cx, object, id, &offset, &type);
75 }
76 };
77
78 using HandleRttValue = Handle<RttValue*>;
79 using RootedRttValue = Rooted<RttValue*>;
80
81 /* Base type for typed objects. */
82 class TypedObject : public JSObject {
83 protected:
84 GCPtr<RttValue*> rttValue_;
85
86 static const ObjectOps objectOps_;
87
88 [[nodiscard]] static bool obj_lookupProperty(JSContext* cx, HandleObject obj,
89 HandleId id,
90 MutableHandleObject objp,
91 PropertyResult* propp);
92
93 [[nodiscard]] static bool obj_defineProperty(JSContext* cx, HandleObject obj,
94 HandleId id,
95 Handle<PropertyDescriptor> desc,
96 ObjectOpResult& result);
97
98 [[nodiscard]] static bool obj_hasProperty(JSContext* cx, HandleObject obj,
99 HandleId id, bool* foundp);
100
101 [[nodiscard]] static bool obj_getProperty(JSContext* cx, HandleObject obj,
102 HandleValue receiver, HandleId id,
103 MutableHandleValue vp);
104
105 [[nodiscard]] static bool obj_setProperty(JSContext* cx, HandleObject obj,
106 HandleId id, HandleValue v,
107 HandleValue receiver,
108 ObjectOpResult& result);
109
110 [[nodiscard]] static bool obj_getOwnPropertyDescriptor(
111 JSContext* cx, HandleObject obj, HandleId id,
112 MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc);
113
114 [[nodiscard]] static bool obj_deleteProperty(JSContext* cx, HandleObject obj,
115 HandleId id,
116 ObjectOpResult& result);
117
118 bool loadValue(JSContext* cx, size_t offset, wasm::FieldType type,
119 MutableHandleValue vp);
120
121 uint8_t* typedMem() const;
122
123 template <typename V>
124 void visitReferences(JSContext* cx, V& visitor);
125
126 void initDefault();
127
128 public:
129 // Creates a new struct typed object initialized to zero.
130 static TypedObject* createStruct(JSContext* cx, HandleRttValue rtt,
131 gc::InitialHeap heap = gc::DefaultHeap);
132
133 // Creates a new array typed object initialized to zero of specified length.
134 static TypedObject* createArray(JSContext* cx, HandleRttValue rtt,
135 uint32_t length,
136 gc::InitialHeap heap = gc::DefaultHeap);
137
138 // Internal create used by JSObject
139 static JS::Result<TypedObject*, JS::OOM> create(JSContext* cx,
140 js::gc::AllocKind kind,
141 js::gc::InitialHeap heap,
142 js::HandleShape shape);
143
typedProto()144 TypedProto& typedProto() const {
145 // Typed objects' prototypes can't be modified.
146 return staticPrototype()->as<TypedProto>();
147 }
rttValue()148 RttValue& rttValue() const {
149 MOZ_ASSERT(rttValue_);
150 return *rttValue_;
151 }
152
153 [[nodiscard]] bool isRuntimeSubtype(js::Handle<RttValue*> rtt) const;
154
offsetOfRttValue()155 static constexpr size_t offsetOfRttValue() {
156 return offsetof(TypedObject, rttValue_);
157 }
158
159 [[nodiscard]] static bool obj_newEnumerate(JSContext* cx, HandleObject obj,
160 MutableHandleIdVector properties,
161 bool enumerableOnly);
162 };
163
164 using HandleTypedObject = Handle<TypedObject*>;
165 using RootedTypedObject = Rooted<TypedObject*>;
166
167 class OutlineTypedObject : public TypedObject {
168 // Owned data pointer
169 uint8_t* data_;
170
171 static OutlineTypedObject* create(JSContext* cx, HandleRttValue rtt,
172 size_t byteLength,
173 gc::InitialHeap heap = gc::DefaultHeap);
174
setArrayLength(uint32_t length)175 void setArrayLength(uint32_t length) { *(uint32_t*)(data_) = length; }
176
177 public:
178 static const JSClass class_;
179
180 static OutlineTypedObject* createStruct(JSContext* cx, HandleRttValue rtt,
181 gc::InitialHeap heap);
182 static OutlineTypedObject* createArray(JSContext* cx, HandleRttValue rtt,
183 uint32_t length, gc::InitialHeap heap);
184
185 // JIT accessors.
offsetOfData()186 static size_t offsetOfData() { return offsetof(OutlineTypedObject, data_); }
187
offsetOfArrayLength()188 static constexpr size_t offsetOfArrayLength() { return 0; }
189 using ArrayLength = uint32_t;
190
outOfLineTypedMem()191 uint8_t* outOfLineTypedMem() const { return data_; }
192
arrayLength()193 ArrayLength arrayLength() const {
194 return *(ArrayLength*)(data_ + offsetOfArrayLength());
195 }
196
197 static gc::AllocKind allocKind();
198
199 static void obj_trace(JSTracer* trc, JSObject* object);
200 static void obj_finalize(JSFreeOp* fop, JSObject* object);
201 };
202
203 // Helper to mark all locations that assume the type of the array length header
204 // for a typed object.
205 #define STATIC_ASSERT_ARRAYLENGTH_IS_U32 \
206 static_assert(1, "ArrayLength is uint32_t")
207
208 // Class for a typed object whose data is allocated inline.
209 class InlineTypedObject : public TypedObject {
210 friend class TypedObject;
211
212 // Start of the inline data, which immediately follows the shape and type.
213 uint8_t data_[1];
214
215 public:
216 static const JSClass class_;
217
218 static const size_t MaxInlineBytes =
219 JSObject::MAX_BYTE_SIZE - sizeof(TypedObject);
220
221 protected:
inlineTypedMem()222 uint8_t* inlineTypedMem() const { return (uint8_t*)&data_; }
223
224 public:
225 static inline gc::AllocKind allocKindForRttValue(RttValue* rtt);
226
canAccommodateType(HandleRttValue rtt)227 static bool canAccommodateType(HandleRttValue rtt) {
228 return rtt->kind() == wasm::TypeDefKind::Struct &&
229 rtt->size() <= MaxInlineBytes;
230 }
231
canAccommodateSize(size_t size)232 static bool canAccommodateSize(size_t size) { return size <= MaxInlineBytes; }
233
inlineTypedMem(const JS::AutoRequireNoGC &)234 uint8_t* inlineTypedMem(const JS::AutoRequireNoGC&) const {
235 return inlineTypedMem();
236 }
237
238 static void obj_trace(JSTracer* trc, JSObject* object);
239 static size_t obj_moved(JSObject* dst, JSObject* src);
240
offsetOfDataStart()241 static size_t offsetOfDataStart() {
242 return offsetof(InlineTypedObject, data_);
243 }
244
245 static InlineTypedObject* createStruct(
246 JSContext* cx, HandleRttValue rtt,
247 gc::InitialHeap heap = gc::DefaultHeap);
248 };
249
IsTypedObjectClass(const JSClass * class_)250 inline bool IsTypedObjectClass(const JSClass* class_) {
251 return class_ == &OutlineTypedObject::class_ ||
252 class_ == &InlineTypedObject::class_;
253 }
254
IsOutlineTypedObjectClass(const JSClass * class_)255 inline bool IsOutlineTypedObjectClass(const JSClass* class_) {
256 return class_ == &OutlineTypedObject::class_;
257 }
258
IsInlineTypedObjectClass(const JSClass * class_)259 inline bool IsInlineTypedObjectClass(const JSClass* class_) {
260 return class_ == &InlineTypedObject::class_;
261 }
262
263 } // namespace js
264
265 template <>
266 inline bool JSObject::is<js::TypedObject>() const {
267 return js::IsTypedObjectClass(getClass());
268 }
269
270 template <>
271 inline bool JSObject::is<js::OutlineTypedObject>() const {
272 return js::IsOutlineTypedObjectClass(getClass());
273 }
274
275 template <>
276 inline bool JSObject::is<js::InlineTypedObject>() const {
277 return js::IsInlineTypedObjectClass(getClass());
278 }
279
280 #endif /* wasm_TypedObject_h */
281