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