|
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 builtin_TypedObject_h |
|
8 #define builtin_TypedObject_h |
|
9 |
|
10 #include "jsobj.h" |
|
11 |
|
12 #include "builtin/TypedObjectConstants.h" |
|
13 #include "vm/ArrayBufferObject.h" |
|
14 |
|
15 /* |
|
16 * ------------- |
|
17 * Typed Objects |
|
18 * ------------- |
|
19 * |
|
20 * Typed objects are a special kind of JS object where the data is |
|
21 * given well-structured form. To use a typed object, users first |
|
22 * create *type objects* (no relation to the type objects used in TI) |
|
23 * that define the type layout. For example, a statement like: |
|
24 * |
|
25 * var PointType = new StructType({x: uint8, y: uint8}); |
|
26 * |
|
27 * would create a type object PointType that is a struct with |
|
28 * two fields, each of uint8 type. |
|
29 * |
|
30 * This comment typically assumes familiary with the API. For more |
|
31 * info on the API itself, see the Harmony wiki page at |
|
32 * http://wiki.ecmascript.org/doku.php?id=harmony:typed_objects or the |
|
33 * ES6 spec (not finalized at the time of this writing). |
|
34 * |
|
35 * - Initialization: |
|
36 * |
|
37 * Currently, all "globals" related to typed objects are packaged |
|
38 * within a single "module" object `TypedObject`. This module has its |
|
39 * own js::Class and when that class is initialized, we also create |
|
40 * and define all other values (in `js_InitTypedObjectModuleClass()`). |
|
41 * |
|
42 * - Type objects, meta type objects, and type representations: |
|
43 * |
|
44 * There are a number of pre-defined type objects, one for each |
|
45 * scalar type (`uint8` etc). Each of these has its own class_, |
|
46 * defined in `DefineNumericClass()`. |
|
47 * |
|
48 * There are also meta type objects (`ArrayType`, `StructType`). |
|
49 * These constructors are not themselves type objects but rather the |
|
50 * means for the *user* to construct new typed objects. |
|
51 * |
|
52 * Each type object is associated with a *type representation* (see |
|
53 * TypeRepresentation.h). Type representations are canonical versions |
|
54 * of type objects. We attach them to TI type objects and (eventually) |
|
55 * use them for shape guards etc. They are purely internal to the |
|
56 * engine and are not exposed to end users (though self-hosted code |
|
57 * sometimes accesses them). |
|
58 * |
|
59 * - Typed objects: |
|
60 * |
|
61 * A typed object is an instance of a *type object* (note the past |
|
62 * participle). There is one class for *transparent* typed objects and |
|
63 * one for *opaque* typed objects. These classes are equivalent in |
|
64 * basically every way, except that requesting the backing buffer of |
|
65 * an opaque typed object yields null. We use distinct js::Classes to |
|
66 * avoid the need for an extra slot in every typed object. |
|
67 * |
|
68 * Note that whether a typed object is opaque is not directly |
|
69 * connected to its type. That is, opaque types are *always* |
|
70 * represented by opaque typed objects, but you may have opaque typed |
|
71 * objects for transparent types too. This can occur for two reasons: |
|
72 * (1) a transparent type may be embedded within an opaque type or (2) |
|
73 * users can choose to convert transparent typed objects into opaque |
|
74 * ones to avoid giving access to the buffer itself. |
|
75 * |
|
76 * Typed objects (no matter their class) are non-native objects that |
|
77 * fully override the property accessors etc. The overridden accessor |
|
78 * methods are the same in each and are defined in methods of |
|
79 * TypedObject. |
|
80 * |
|
81 * Typed objects may be attached or unattached. An unattached typed |
|
82 * object has no memory associated with it; it is basically a null |
|
83 * pointer. When first created, objects are always attached, but they |
|
84 * can become unattached if their buffer is neutered (note that this |
|
85 * implies that typed objects of opaque types can never be unattached). |
|
86 * |
|
87 * When a new typed object instance is created, fresh memory is |
|
88 * allocated and set as that typed object's private field. The object |
|
89 * is then considered the *owner* of that memory: when the object is |
|
90 * collected, its finalizer will free the memory. The fact that an |
|
91 * object `o` owns its memory is indicated by setting its reserved |
|
92 * slot JS_TYPEDOBJ_SLOT_OWNER to `o` (a trivial cycle, in other |
|
93 * words). |
|
94 * |
|
95 * Later, *derived* typed objects can be created, typically via an |
|
96 * access like `o.f` where `f` is some complex (non-scalar) type, but |
|
97 * also explicitly via Handle objects. In those cases, the memory |
|
98 * pointer of the derived object is set to alias the owner's memory |
|
99 * pointer, and the owner slot for the derived object is set to the |
|
100 * owner object, thus ensuring that the owner is not collected while |
|
101 * the derived object is alive. We always maintain the invariant that |
|
102 * JS_TYPEDOBJ_SLOT_OWNER is the true owner of the memory, meaning |
|
103 * that there is a shallow tree. This prevents an access pattern like |
|
104 * `a.b.c.d` from keeping all the intermediate objects alive. |
|
105 */ |
|
106 |
|
107 namespace js { |
|
108 |
|
109 /* |
|
110 * Helper method for converting a double into other scalar |
|
111 * types in the same way that JavaScript would. In particular, |
|
112 * simple C casting from double to int32_t gets things wrong |
|
113 * for values like 0xF0000000. |
|
114 */ |
|
115 template <typename T> |
|
116 static T ConvertScalar(double d) |
|
117 { |
|
118 if (TypeIsFloatingPoint<T>()) { |
|
119 return T(d); |
|
120 } else if (TypeIsUnsigned<T>()) { |
|
121 uint32_t n = ToUint32(d); |
|
122 return T(n); |
|
123 } else { |
|
124 int32_t n = ToInt32(d); |
|
125 return T(n); |
|
126 } |
|
127 } |
|
128 |
|
129 /* |
|
130 * The prototype for a typed object. Currently, carries a link to the |
|
131 * type descriptor. Eventually will carry most of the type information |
|
132 * we want. |
|
133 */ |
|
134 class TypedProto : public JSObject |
|
135 { |
|
136 public: |
|
137 static const Class class_; |
|
138 |
|
139 inline void initTypeDescrSlot(TypeDescr &descr); |
|
140 |
|
141 TypeDescr &typeDescr() const { |
|
142 return getReservedSlot(JS_TYPROTO_SLOT_DESCR).toObject().as<TypeDescr>(); |
|
143 } |
|
144 }; |
|
145 |
|
146 class TypeDescr : public JSObject |
|
147 { |
|
148 public: |
|
149 // This is *intentionally* not defined so as to produce link |
|
150 // errors if a is<FooTypeDescr>() etc goes wrong. Otherwise, the |
|
151 // default implementation resolves this to a reference to |
|
152 // FooTypeDescr::class_ which resolves to |
|
153 // JSObject::class_. Debugging the resulting errors leads to much |
|
154 // fun and rejoicing. |
|
155 static const Class class_; |
|
156 |
|
157 enum Kind { |
|
158 Scalar = JS_TYPEREPR_SCALAR_KIND, |
|
159 Reference = JS_TYPEREPR_REFERENCE_KIND, |
|
160 X4 = JS_TYPEREPR_X4_KIND, |
|
161 Struct = JS_TYPEREPR_STRUCT_KIND, |
|
162 SizedArray = JS_TYPEREPR_SIZED_ARRAY_KIND, |
|
163 UnsizedArray = JS_TYPEREPR_UNSIZED_ARRAY_KIND, |
|
164 }; |
|
165 |
|
166 static bool isSized(Kind kind) { |
|
167 return kind > JS_TYPEREPR_MAX_UNSIZED_KIND; |
|
168 } |
|
169 |
|
170 JSAtom &stringRepr() const { |
|
171 return getReservedSlot(JS_DESCR_SLOT_STRING_REPR).toString()->asAtom(); |
|
172 } |
|
173 |
|
174 TypeDescr::Kind kind() const { |
|
175 return (TypeDescr::Kind) getReservedSlot(JS_DESCR_SLOT_KIND).toInt32(); |
|
176 } |
|
177 |
|
178 bool opaque() const { |
|
179 return getReservedSlot(JS_DESCR_SLOT_OPAQUE).toBoolean(); |
|
180 } |
|
181 |
|
182 bool transparent() const { |
|
183 return !opaque(); |
|
184 } |
|
185 |
|
186 int32_t alignment() { |
|
187 return getReservedSlot(JS_DESCR_SLOT_ALIGNMENT).toInt32(); |
|
188 } |
|
189 }; |
|
190 |
|
191 typedef Handle<TypeDescr*> HandleTypeDescr; |
|
192 |
|
193 class SizedTypeDescr : public TypeDescr |
|
194 { |
|
195 public: |
|
196 int32_t size() { |
|
197 return getReservedSlot(JS_DESCR_SLOT_SIZE).toInt32(); |
|
198 } |
|
199 |
|
200 void initInstances(const JSRuntime *rt, uint8_t *mem, size_t length); |
|
201 void traceInstances(JSTracer *trace, uint8_t *mem, size_t length); |
|
202 }; |
|
203 |
|
204 typedef Handle<SizedTypeDescr*> HandleSizedTypeDescr; |
|
205 |
|
206 class SimpleTypeDescr : public SizedTypeDescr |
|
207 { |
|
208 }; |
|
209 |
|
210 // Type for scalar type constructors like `uint8`. All such type |
|
211 // constructors share a common js::Class and JSFunctionSpec. Scalar |
|
212 // types are non-opaque (their storage is visible unless combined with |
|
213 // an opaque reference type.) |
|
214 class ScalarTypeDescr : public SimpleTypeDescr |
|
215 { |
|
216 public: |
|
217 // Must match order of JS_FOR_EACH_SCALAR_TYPE_REPR below |
|
218 enum Type { |
|
219 TYPE_INT8 = JS_SCALARTYPEREPR_INT8, |
|
220 TYPE_UINT8 = JS_SCALARTYPEREPR_UINT8, |
|
221 TYPE_INT16 = JS_SCALARTYPEREPR_INT16, |
|
222 TYPE_UINT16 = JS_SCALARTYPEREPR_UINT16, |
|
223 TYPE_INT32 = JS_SCALARTYPEREPR_INT32, |
|
224 TYPE_UINT32 = JS_SCALARTYPEREPR_UINT32, |
|
225 TYPE_FLOAT32 = JS_SCALARTYPEREPR_FLOAT32, |
|
226 TYPE_FLOAT64 = JS_SCALARTYPEREPR_FLOAT64, |
|
227 |
|
228 /* |
|
229 * Special type that's a uint8_t, but assignments are clamped to 0 .. 255. |
|
230 * Treat the raw data type as a uint8_t. |
|
231 */ |
|
232 TYPE_UINT8_CLAMPED = JS_SCALARTYPEREPR_UINT8_CLAMPED, |
|
233 }; |
|
234 static const int32_t TYPE_MAX = TYPE_UINT8_CLAMPED + 1; |
|
235 |
|
236 static const TypeDescr::Kind Kind = TypeDescr::Scalar; |
|
237 static const bool Opaque = false; |
|
238 static int32_t size(Type t); |
|
239 static int32_t alignment(Type t); |
|
240 static const char *typeName(Type type); |
|
241 |
|
242 static const Class class_; |
|
243 static const JSFunctionSpec typeObjectMethods[]; |
|
244 |
|
245 ScalarTypeDescr::Type type() const { |
|
246 return (ScalarTypeDescr::Type) getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32(); |
|
247 } |
|
248 |
|
249 static bool call(JSContext *cx, unsigned argc, Value *vp); |
|
250 }; |
|
251 |
|
252 // Enumerates the cases of ScalarTypeDescr::Type which have |
|
253 // unique C representation. In particular, omits Uint8Clamped since it |
|
254 // is just a Uint8. |
|
255 #define JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(macro_) \ |
|
256 macro_(ScalarTypeDescr::TYPE_INT8, int8_t, int8) \ |
|
257 macro_(ScalarTypeDescr::TYPE_UINT8, uint8_t, uint8) \ |
|
258 macro_(ScalarTypeDescr::TYPE_INT16, int16_t, int16) \ |
|
259 macro_(ScalarTypeDescr::TYPE_UINT16, uint16_t, uint16) \ |
|
260 macro_(ScalarTypeDescr::TYPE_INT32, int32_t, int32) \ |
|
261 macro_(ScalarTypeDescr::TYPE_UINT32, uint32_t, uint32) \ |
|
262 macro_(ScalarTypeDescr::TYPE_FLOAT32, float, float32) \ |
|
263 macro_(ScalarTypeDescr::TYPE_FLOAT64, double, float64) |
|
264 |
|
265 // Must be in same order as the enum ScalarTypeDescr::Type: |
|
266 #define JS_FOR_EACH_SCALAR_TYPE_REPR(macro_) \ |
|
267 JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(macro_) \ |
|
268 macro_(ScalarTypeDescr::TYPE_UINT8_CLAMPED, uint8_t, uint8Clamped) |
|
269 |
|
270 // Type for reference type constructors like `Any`, `String`, and |
|
271 // `Object`. All such type constructors share a common js::Class and |
|
272 // JSFunctionSpec. All these types are opaque. |
|
273 class ReferenceTypeDescr : public SimpleTypeDescr |
|
274 { |
|
275 public: |
|
276 // Must match order of JS_FOR_EACH_REFERENCE_TYPE_REPR below |
|
277 enum Type { |
|
278 TYPE_ANY = JS_REFERENCETYPEREPR_ANY, |
|
279 TYPE_OBJECT = JS_REFERENCETYPEREPR_OBJECT, |
|
280 TYPE_STRING = JS_REFERENCETYPEREPR_STRING, |
|
281 }; |
|
282 static const int32_t TYPE_MAX = TYPE_STRING + 1; |
|
283 static const char *typeName(Type type); |
|
284 |
|
285 static const TypeDescr::Kind Kind = TypeDescr::Reference; |
|
286 static const bool Opaque = true; |
|
287 static const Class class_; |
|
288 static int32_t size(Type t); |
|
289 static int32_t alignment(Type t); |
|
290 static const JSFunctionSpec typeObjectMethods[]; |
|
291 |
|
292 ReferenceTypeDescr::Type type() const { |
|
293 return (ReferenceTypeDescr::Type) getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32(); |
|
294 } |
|
295 |
|
296 const char *typeName() const { |
|
297 return typeName(type()); |
|
298 } |
|
299 |
|
300 static bool call(JSContext *cx, unsigned argc, Value *vp); |
|
301 }; |
|
302 |
|
303 #define JS_FOR_EACH_REFERENCE_TYPE_REPR(macro_) \ |
|
304 macro_(ReferenceTypeDescr::TYPE_ANY, HeapValue, Any) \ |
|
305 macro_(ReferenceTypeDescr::TYPE_OBJECT, HeapPtrObject, Object) \ |
|
306 macro_(ReferenceTypeDescr::TYPE_STRING, HeapPtrString, string) |
|
307 |
|
308 // Type descriptors whose instances are objects and hence which have |
|
309 // an associated `prototype` property. |
|
310 class ComplexTypeDescr : public SizedTypeDescr |
|
311 { |
|
312 public: |
|
313 // Returns the prototype that instances of this type descriptor |
|
314 // will have. |
|
315 TypedProto &instancePrototype() const { |
|
316 return getReservedSlot(JS_DESCR_SLOT_TYPROTO).toObject().as<TypedProto>(); |
|
317 } |
|
318 }; |
|
319 |
|
320 /* |
|
321 * Type descriptors `float32x4` and `int32x4` |
|
322 */ |
|
323 class X4TypeDescr : public ComplexTypeDescr |
|
324 { |
|
325 public: |
|
326 enum Type { |
|
327 TYPE_INT32 = JS_X4TYPEREPR_INT32, |
|
328 TYPE_FLOAT32 = JS_X4TYPEREPR_FLOAT32, |
|
329 }; |
|
330 |
|
331 static const TypeDescr::Kind Kind = TypeDescr::X4; |
|
332 static const bool Opaque = false; |
|
333 static const Class class_; |
|
334 static int32_t size(Type t); |
|
335 static int32_t alignment(Type t); |
|
336 |
|
337 X4TypeDescr::Type type() const { |
|
338 return (X4TypeDescr::Type) getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32(); |
|
339 } |
|
340 |
|
341 static bool call(JSContext *cx, unsigned argc, Value *vp); |
|
342 static bool is(const Value &v); |
|
343 }; |
|
344 |
|
345 #define JS_FOR_EACH_X4_TYPE_REPR(macro_) \ |
|
346 macro_(X4TypeDescr::TYPE_INT32, int32_t, int32) \ |
|
347 macro_(X4TypeDescr::TYPE_FLOAT32, float, float32) |
|
348 |
|
349 bool IsTypedObjectClass(const Class *clasp); // Defined below |
|
350 bool IsTypedObjectArray(JSObject& obj); |
|
351 |
|
352 bool CreateUserSizeAndAlignmentProperties(JSContext *cx, HandleTypeDescr obj); |
|
353 |
|
354 /* |
|
355 * Properties and methods of the `ArrayType` meta type object. There |
|
356 * is no `class_` field because `ArrayType` is just a native |
|
357 * constructor function. |
|
358 */ |
|
359 class ArrayMetaTypeDescr : public JSObject |
|
360 { |
|
361 private: |
|
362 friend class UnsizedArrayTypeDescr; |
|
363 |
|
364 // Helper for creating a new ArrayType object, either sized or unsized. |
|
365 // The template parameter `T` should be either `UnsizedArrayTypeDescr` |
|
366 // or `SizedArrayTypeDescr`. |
|
367 // |
|
368 // - `arrayTypePrototype` - prototype for the new object to be created, |
|
369 // either ArrayType.prototype or |
|
370 // unsizedArrayType.__proto__ depending on |
|
371 // whether this is a sized or unsized array |
|
372 // - `elementType` - type object for the elements in the array |
|
373 // - `stringRepr` - canonical string representation for the array |
|
374 // - `size` - length of the array (0 if unsized) |
|
375 template<class T> |
|
376 static T *create(JSContext *cx, |
|
377 HandleObject arrayTypePrototype, |
|
378 HandleSizedTypeDescr elementType, |
|
379 HandleAtom stringRepr, |
|
380 int32_t size); |
|
381 |
|
382 public: |
|
383 // Properties and methods to be installed on ArrayType.prototype, |
|
384 // and hence inherited by all array type objects: |
|
385 static const JSPropertySpec typeObjectProperties[]; |
|
386 static const JSFunctionSpec typeObjectMethods[]; |
|
387 |
|
388 // Properties and methods to be installed on ArrayType.prototype.prototype, |
|
389 // and hence inherited by all array *typed* objects: |
|
390 static const JSPropertySpec typedObjectProperties[]; |
|
391 static const JSFunctionSpec typedObjectMethods[]; |
|
392 |
|
393 // This is the function that gets called when the user |
|
394 // does `new ArrayType(elem)`. It produces an array type object. |
|
395 static bool construct(JSContext *cx, unsigned argc, Value *vp); |
|
396 }; |
|
397 |
|
398 /* |
|
399 * Type descriptor created by `new ArrayType(typeObj)` |
|
400 * |
|
401 * These have a prototype, and hence *could* be a subclass of |
|
402 * `ComplexTypeDescr`, but it would require some reshuffling of the |
|
403 * hierarchy, and it's not worth the trouble since they will be going |
|
404 * away as part of bug 973238. |
|
405 */ |
|
406 class UnsizedArrayTypeDescr : public TypeDescr |
|
407 { |
|
408 public: |
|
409 static const Class class_; |
|
410 static const TypeDescr::Kind Kind = TypeDescr::UnsizedArray; |
|
411 |
|
412 // This is the sized method on unsized array type objects. It |
|
413 // produces a sized variant. |
|
414 static bool dimension(JSContext *cx, unsigned int argc, jsval *vp); |
|
415 |
|
416 SizedTypeDescr &elementType() { |
|
417 return getReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE).toObject().as<SizedTypeDescr>(); |
|
418 } |
|
419 }; |
|
420 |
|
421 /* |
|
422 * Type descriptor created by `unsizedArrayTypeObj.dimension()` |
|
423 */ |
|
424 class SizedArrayTypeDescr : public ComplexTypeDescr |
|
425 { |
|
426 public: |
|
427 static const Class class_; |
|
428 static const TypeDescr::Kind Kind = TypeDescr::SizedArray; |
|
429 |
|
430 SizedTypeDescr &elementType() { |
|
431 return getReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE).toObject().as<SizedTypeDescr>(); |
|
432 } |
|
433 |
|
434 int32_t length() { |
|
435 return getReservedSlot(JS_DESCR_SLOT_SIZED_ARRAY_LENGTH).toInt32(); |
|
436 } |
|
437 }; |
|
438 |
|
439 /* |
|
440 * Properties and methods of the `StructType` meta type object. There |
|
441 * is no `class_` field because `StructType` is just a native |
|
442 * constructor function. |
|
443 */ |
|
444 class StructMetaTypeDescr : public JSObject |
|
445 { |
|
446 private: |
|
447 static JSObject *create(JSContext *cx, HandleObject structTypeGlobal, |
|
448 HandleObject fields); |
|
449 |
|
450 public: |
|
451 // Properties and methods to be installed on StructType.prototype, |
|
452 // and hence inherited by all struct type objects: |
|
453 static const JSPropertySpec typeObjectProperties[]; |
|
454 static const JSFunctionSpec typeObjectMethods[]; |
|
455 |
|
456 // Properties and methods to be installed on StructType.prototype.prototype, |
|
457 // and hence inherited by all struct *typed* objects: |
|
458 static const JSPropertySpec typedObjectProperties[]; |
|
459 static const JSFunctionSpec typedObjectMethods[]; |
|
460 |
|
461 // This is the function that gets called when the user |
|
462 // does `new StructType(...)`. It produces a struct type object. |
|
463 static bool construct(JSContext *cx, unsigned argc, Value *vp); |
|
464 }; |
|
465 |
|
466 class StructTypeDescr : public ComplexTypeDescr |
|
467 { |
|
468 public: |
|
469 static const Class class_; |
|
470 |
|
471 // Returns the number of fields defined in this struct. |
|
472 size_t fieldCount(); |
|
473 |
|
474 // Set `*out` to the index of the field named `id` and returns true, |
|
475 // or return false if no such field exists. |
|
476 bool fieldIndex(jsid id, size_t *out); |
|
477 |
|
478 // Return the name of the field at index `index`. |
|
479 JSAtom &fieldName(size_t index); |
|
480 |
|
481 // Return the type descr of the field at index `index`. |
|
482 SizedTypeDescr &fieldDescr(size_t index); |
|
483 |
|
484 // Return the offset of the field at index `index`. |
|
485 int32_t fieldOffset(size_t index); |
|
486 }; |
|
487 |
|
488 typedef Handle<StructTypeDescr*> HandleStructTypeDescr; |
|
489 |
|
490 /* |
|
491 * This object exists in order to encapsulate the typed object types |
|
492 * somewhat, rather than sticking them all into the global object. |
|
493 * Eventually it will go away and become a module. |
|
494 */ |
|
495 class TypedObjectModuleObject : public JSObject { |
|
496 public: |
|
497 enum Slot { |
|
498 ArrayTypePrototype, |
|
499 StructTypePrototype, |
|
500 SlotCount |
|
501 }; |
|
502 |
|
503 static const Class class_; |
|
504 }; |
|
505 |
|
506 /* |
|
507 * Base type for typed objects and handles. Basically any type whose |
|
508 * contents consist of typed memory. |
|
509 */ |
|
510 class TypedObject : public ArrayBufferViewObject |
|
511 { |
|
512 private: |
|
513 static const bool IsTypedObjectClass = true; |
|
514 |
|
515 template<class T> |
|
516 static bool obj_getArrayElement(JSContext *cx, |
|
517 Handle<TypedObject*> typedObj, |
|
518 Handle<TypeDescr*> typeDescr, |
|
519 uint32_t index, |
|
520 MutableHandleValue vp); |
|
521 |
|
522 template<class T> |
|
523 static bool obj_setArrayElement(JSContext *cx, |
|
524 Handle<TypedObject*> typedObj, |
|
525 Handle<TypeDescr*> typeDescr, |
|
526 uint32_t index, |
|
527 MutableHandleValue vp); |
|
528 |
|
529 protected: |
|
530 static void obj_trace(JSTracer *trace, JSObject *object); |
|
531 |
|
532 static bool obj_lookupGeneric(JSContext *cx, HandleObject obj, |
|
533 HandleId id, MutableHandleObject objp, |
|
534 MutableHandleShape propp); |
|
535 |
|
536 static bool obj_lookupProperty(JSContext *cx, HandleObject obj, |
|
537 HandlePropertyName name, |
|
538 MutableHandleObject objp, |
|
539 MutableHandleShape propp); |
|
540 |
|
541 static bool obj_lookupElement(JSContext *cx, HandleObject obj, |
|
542 uint32_t index, MutableHandleObject objp, |
|
543 MutableHandleShape propp); |
|
544 |
|
545 static bool obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v, |
|
546 PropertyOp getter, StrictPropertyOp setter, unsigned attrs); |
|
547 |
|
548 static bool obj_defineProperty(JSContext *cx, HandleObject obj, |
|
549 HandlePropertyName name, HandleValue v, |
|
550 PropertyOp getter, StrictPropertyOp setter, unsigned attrs); |
|
551 |
|
552 static bool obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v, |
|
553 PropertyOp getter, StrictPropertyOp setter, unsigned attrs); |
|
554 |
|
555 static bool obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver, |
|
556 HandleId id, MutableHandleValue vp); |
|
557 |
|
558 static bool obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver, |
|
559 HandlePropertyName name, MutableHandleValue vp); |
|
560 |
|
561 static bool obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver, |
|
562 uint32_t index, MutableHandleValue vp); |
|
563 |
|
564 static bool obj_getUnsizedArrayElement(JSContext *cx, HandleObject obj, HandleObject receiver, |
|
565 uint32_t index, MutableHandleValue vp); |
|
566 |
|
567 static bool obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id, |
|
568 MutableHandleValue vp, bool strict); |
|
569 static bool obj_setProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, |
|
570 MutableHandleValue vp, bool strict); |
|
571 static bool obj_setElement(JSContext *cx, HandleObject obj, uint32_t index, |
|
572 MutableHandleValue vp, bool strict); |
|
573 |
|
574 static bool obj_getGenericAttributes(JSContext *cx, HandleObject obj, |
|
575 HandleId id, unsigned *attrsp); |
|
576 static bool obj_setGenericAttributes(JSContext *cx, HandleObject obj, |
|
577 HandleId id, unsigned *attrsp); |
|
578 |
|
579 static bool obj_deleteProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, |
|
580 bool *succeeded); |
|
581 static bool obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index, |
|
582 bool *succeeded); |
|
583 |
|
584 static bool obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op, |
|
585 MutableHandleValue statep, MutableHandleId idp); |
|
586 |
|
587 public: |
|
588 static size_t offsetOfOwnerSlot(); |
|
589 |
|
590 // Each typed object contains a void* pointer pointing at the |
|
591 // binary data that it represents. (That data may be owned by this |
|
592 // object or this object may alias data owned by someone else.) |
|
593 // This function returns the offset in bytes within the object |
|
594 // where the `void*` pointer can be found. It is intended for use |
|
595 // by the JIT. |
|
596 static size_t offsetOfDataSlot(); |
|
597 |
|
598 // Offset of the byte offset slot. |
|
599 static size_t offsetOfByteOffsetSlot(); |
|
600 |
|
601 // Helper for createUnattached() |
|
602 static TypedObject *createUnattachedWithClass(JSContext *cx, |
|
603 const Class *clasp, |
|
604 HandleTypeDescr type, |
|
605 int32_t length); |
|
606 |
|
607 // Creates an unattached typed object or handle (depending on the |
|
608 // type parameter T). Note that it is only legal for unattached |
|
609 // handles to escape to the end user; for non-handles, the caller |
|
610 // should always invoke one of the `attach()` methods below. |
|
611 // |
|
612 // Arguments: |
|
613 // - type: type object for resulting object |
|
614 // - length: 0 unless this is an array, otherwise the length |
|
615 static TypedObject *createUnattached(JSContext *cx, HandleTypeDescr type, |
|
616 int32_t length); |
|
617 |
|
618 // Creates a typedObj that aliases the memory pointed at by `owner` |
|
619 // at the given offset. The typedObj will be a handle iff type is a |
|
620 // handle and a typed object otherwise. |
|
621 static TypedObject *createDerived(JSContext *cx, |
|
622 HandleSizedTypeDescr type, |
|
623 Handle<TypedObject*> typedContents, |
|
624 int32_t offset); |
|
625 |
|
626 // Creates a new typed object whose memory is freshly allocated |
|
627 // and initialized with zeroes (or, in the case of references, an |
|
628 // appropriate default value). |
|
629 static TypedObject *createZeroed(JSContext *cx, |
|
630 HandleTypeDescr typeObj, |
|
631 int32_t length); |
|
632 |
|
633 // User-accessible constructor (`new TypeDescriptor(...)`) |
|
634 // used for sized types. Note that the callee here is the *type descriptor*, |
|
635 // not the typedObj. |
|
636 static bool constructSized(JSContext *cx, unsigned argc, Value *vp); |
|
637 |
|
638 // As `constructSized`, but for unsized array types. |
|
639 static bool constructUnsized(JSContext *cx, unsigned argc, Value *vp); |
|
640 |
|
641 // Use this method when `buffer` is the owner of the memory. |
|
642 void attach(ArrayBufferObject &buffer, int32_t offset); |
|
643 |
|
644 // Otherwise, use this to attach to memory referenced by another typedObj. |
|
645 void attach(TypedObject &typedObj, int32_t offset); |
|
646 |
|
647 // Invoked when array buffer is transferred elsewhere |
|
648 void neuter(void *newData); |
|
649 |
|
650 int32_t offset() const { |
|
651 return getReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET).toInt32(); |
|
652 } |
|
653 |
|
654 ArrayBufferObject &owner() const { |
|
655 return getReservedSlot(JS_TYPEDOBJ_SLOT_OWNER).toObject().as<ArrayBufferObject>(); |
|
656 } |
|
657 |
|
658 TypeDescr &typeDescr() const { |
|
659 return getReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_DESCR).toObject().as<TypeDescr>(); |
|
660 } |
|
661 |
|
662 uint8_t *typedMem() const { |
|
663 return (uint8_t*) getPrivate(); |
|
664 } |
|
665 |
|
666 int32_t byteLength() const { |
|
667 return getReservedSlot(JS_TYPEDOBJ_SLOT_BYTELENGTH).toInt32(); |
|
668 } |
|
669 |
|
670 int32_t length() const { |
|
671 return getReservedSlot(JS_TYPEDOBJ_SLOT_LENGTH).toInt32(); |
|
672 } |
|
673 |
|
674 int32_t size() const { |
|
675 switch (typeDescr().kind()) { |
|
676 case TypeDescr::Scalar: |
|
677 case TypeDescr::X4: |
|
678 case TypeDescr::Reference: |
|
679 case TypeDescr::Struct: |
|
680 case TypeDescr::SizedArray: |
|
681 return typeDescr().as<SizedTypeDescr>().size(); |
|
682 |
|
683 case TypeDescr::UnsizedArray: { |
|
684 SizedTypeDescr &elementType = typeDescr().as<UnsizedArrayTypeDescr>().elementType(); |
|
685 return elementType.size() * length(); |
|
686 } |
|
687 } |
|
688 MOZ_ASSUME_UNREACHABLE("unhandled typerepresentation kind"); |
|
689 } |
|
690 |
|
691 uint8_t *typedMem(size_t offset) const { |
|
692 // It seems a bit surprising that one might request an offset |
|
693 // == size(), but it can happen when taking the "address of" a |
|
694 // 0-sized value. (In other words, we maintain the invariant |
|
695 // that `offset + size <= size()` -- this is always checked in |
|
696 // the caller's side.) |
|
697 JS_ASSERT(offset <= (size_t) size()); |
|
698 return typedMem() + offset; |
|
699 } |
|
700 }; |
|
701 |
|
702 typedef Handle<TypedObject*> HandleTypedObject; |
|
703 |
|
704 class TransparentTypedObject : public TypedObject |
|
705 { |
|
706 public: |
|
707 static const Class class_; |
|
708 }; |
|
709 |
|
710 typedef Handle<TransparentTypedObject*> HandleTransparentTypedObject; |
|
711 |
|
712 class OpaqueTypedObject : public TypedObject |
|
713 { |
|
714 public: |
|
715 static const Class class_; |
|
716 static const JSFunctionSpec handleStaticMethods[]; |
|
717 }; |
|
718 |
|
719 /* |
|
720 * Usage: NewOpaqueTypedObject(typeObj) |
|
721 * |
|
722 * Constructs a new, unattached instance of `Handle`. |
|
723 */ |
|
724 bool NewOpaqueTypedObject(JSContext *cx, unsigned argc, Value *vp); |
|
725 |
|
726 /* |
|
727 * Usage: NewDerivedTypedObject(typeObj, owner, offset) |
|
728 * |
|
729 * Constructs a new, unattached instance of `Handle`. |
|
730 */ |
|
731 bool NewDerivedTypedObject(JSContext *cx, unsigned argc, Value *vp); |
|
732 |
|
733 /* |
|
734 * Usage: AttachTypedObject(typedObj, newDatum, newOffset) |
|
735 * |
|
736 * Moves `typedObj` to point at the memory referenced by `newDatum` with |
|
737 * the offset `newOffset`. |
|
738 */ |
|
739 bool AttachTypedObject(ThreadSafeContext *cx, unsigned argc, Value *vp); |
|
740 extern const JSJitInfo AttachTypedObjectJitInfo; |
|
741 |
|
742 /* |
|
743 * Usage: SetTypedObjectOffset(typedObj, offset) |
|
744 * |
|
745 * Changes the offset for `typedObj` within its buffer to `offset`. |
|
746 * `typedObj` must already be attached. |
|
747 */ |
|
748 bool intrinsic_SetTypedObjectOffset(JSContext *cx, unsigned argc, Value *vp); |
|
749 bool SetTypedObjectOffset(ThreadSafeContext *, unsigned argc, Value *vp); |
|
750 extern const JSJitInfo intrinsic_SetTypedObjectOffsetJitInfo; |
|
751 |
|
752 /* |
|
753 * Usage: ObjectIsTypeDescr(obj) |
|
754 * |
|
755 * True if `obj` is a type object. |
|
756 */ |
|
757 bool ObjectIsTypeDescr(ThreadSafeContext *cx, unsigned argc, Value *vp); |
|
758 extern const JSJitInfo ObjectIsTypeDescrJitInfo; |
|
759 |
|
760 /* |
|
761 * Usage: ObjectIsTypedObject(obj) |
|
762 * |
|
763 * True if `obj` is a transparent or opaque typed object. |
|
764 */ |
|
765 bool ObjectIsTypedObject(ThreadSafeContext *cx, unsigned argc, Value *vp); |
|
766 extern const JSJitInfo ObjectIsTypedObjectJitInfo; |
|
767 |
|
768 /* |
|
769 * Usage: ObjectIsOpaqueTypedObject(obj) |
|
770 * |
|
771 * True if `obj` is an opaque typed object. |
|
772 */ |
|
773 bool ObjectIsOpaqueTypedObject(ThreadSafeContext *cx, unsigned argc, Value *vp); |
|
774 extern const JSJitInfo ObjectIsOpaqueTypedObjectJitInfo; |
|
775 |
|
776 /* |
|
777 * Usage: ObjectIsTransparentTypedObject(obj) |
|
778 * |
|
779 * True if `obj` is a transparent typed object. |
|
780 */ |
|
781 bool ObjectIsTransparentTypedObject(ThreadSafeContext *cx, unsigned argc, Value *vp); |
|
782 extern const JSJitInfo ObjectIsTransparentTypedObjectJitInfo; |
|
783 |
|
784 /* Predicates on type descriptor objects. In all cases, 'obj' must be a type descriptor. */ |
|
785 |
|
786 bool TypeDescrIsSimpleType(ThreadSafeContext *, unsigned argc, Value *vp); |
|
787 extern const JSJitInfo TypeDescrIsSimpleTypeJitInfo; |
|
788 |
|
789 bool TypeDescrIsArrayType(ThreadSafeContext *, unsigned argc, Value *vp); |
|
790 extern const JSJitInfo TypeDescrIsArrayTypeJitInfo; |
|
791 |
|
792 bool TypeDescrIsSizedArrayType(ThreadSafeContext *, unsigned argc, Value *vp); |
|
793 extern const JSJitInfo TypeDescrIsSizedArrayTypeJitInfo; |
|
794 |
|
795 bool TypeDescrIsUnsizedArrayType(ThreadSafeContext *, unsigned argc, Value *vp); |
|
796 extern const JSJitInfo TypeDescrIsUnsizedArrayTypeJitInfo; |
|
797 |
|
798 /* |
|
799 * Usage: TypedObjectIsAttached(obj) |
|
800 * |
|
801 * Given a TypedObject `obj`, returns true if `obj` is |
|
802 * "attached" (i.e., its data pointer is nullptr). |
|
803 */ |
|
804 bool TypedObjectIsAttached(ThreadSafeContext *cx, unsigned argc, Value *vp); |
|
805 extern const JSJitInfo TypedObjectIsAttachedJitInfo; |
|
806 |
|
807 /* |
|
808 * Usage: ClampToUint8(v) |
|
809 * |
|
810 * Same as the C function ClampDoubleToUint8. `v` must be a number. |
|
811 */ |
|
812 bool ClampToUint8(ThreadSafeContext *cx, unsigned argc, Value *vp); |
|
813 extern const JSJitInfo ClampToUint8JitInfo; |
|
814 |
|
815 /* |
|
816 * Usage: Memcpy(targetDatum, targetOffset, |
|
817 * sourceDatum, sourceOffset, |
|
818 * size) |
|
819 * |
|
820 * Intrinsic function. Copies size bytes from the data for |
|
821 * `sourceDatum` at `sourceOffset` into the data for |
|
822 * `targetDatum` at `targetOffset`. |
|
823 * |
|
824 * Both `sourceDatum` and `targetDatum` must be attached. |
|
825 */ |
|
826 bool Memcpy(ThreadSafeContext *cx, unsigned argc, Value *vp); |
|
827 extern const JSJitInfo MemcpyJitInfo; |
|
828 |
|
829 /* |
|
830 * Usage: GetTypedObjectModule() |
|
831 * |
|
832 * Returns the global "typed object" module, which provides access |
|
833 * to the various builtin type descriptors. These are currently |
|
834 * exported as immutable properties so it is safe for self-hosted code |
|
835 * to access them; eventually this should be linked into the module |
|
836 * system. |
|
837 */ |
|
838 bool GetTypedObjectModule(JSContext *cx, unsigned argc, Value *vp); |
|
839 |
|
840 /* |
|
841 * Usage: GetFloat32x4TypeDescr() |
|
842 * |
|
843 * Returns the float32x4 type object. SIMD pseudo-module must have |
|
844 * been initialized for this to be safe. |
|
845 */ |
|
846 bool GetFloat32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp); |
|
847 |
|
848 /* |
|
849 * Usage: GetInt32x4TypeDescr() |
|
850 * |
|
851 * Returns the int32x4 type object. SIMD pseudo-module must have |
|
852 * been initialized for this to be safe. |
|
853 */ |
|
854 bool GetInt32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp); |
|
855 |
|
856 /* |
|
857 * Usage: Store_int8(targetDatum, targetOffset, value) |
|
858 * ... |
|
859 * Store_uint8(targetDatum, targetOffset, value) |
|
860 * ... |
|
861 * Store_float32(targetDatum, targetOffset, value) |
|
862 * Store_float64(targetDatum, targetOffset, value) |
|
863 * |
|
864 * Intrinsic function. Stores `value` into the memory referenced by |
|
865 * `targetDatum` at the offset `targetOffset`. |
|
866 * |
|
867 * Assumes (and asserts) that: |
|
868 * - `targetDatum` is attached |
|
869 * - `targetOffset` is a valid offset within the bounds of `targetDatum` |
|
870 * - `value` is a number |
|
871 */ |
|
872 #define JS_STORE_SCALAR_CLASS_DEFN(_constant, T, _name) \ |
|
873 class StoreScalar##T { \ |
|
874 public: \ |
|
875 static bool Func(ThreadSafeContext *cx, unsigned argc, Value *vp); \ |
|
876 static const JSJitInfo JitInfo; \ |
|
877 }; |
|
878 |
|
879 /* |
|
880 * Usage: Store_Any(targetDatum, targetOffset, value) |
|
881 * Store_Object(targetDatum, targetOffset, value) |
|
882 * Store_string(targetDatum, targetOffset, value) |
|
883 * |
|
884 * Intrinsic function. Stores `value` into the memory referenced by |
|
885 * `targetDatum` at the offset `targetOffset`. |
|
886 * |
|
887 * Assumes (and asserts) that: |
|
888 * - `targetDatum` is attached |
|
889 * - `targetOffset` is a valid offset within the bounds of `targetDatum` |
|
890 * - `value` is an object (`Store_Object`) or string (`Store_string`). |
|
891 */ |
|
892 #define JS_STORE_REFERENCE_CLASS_DEFN(_constant, T, _name) \ |
|
893 class StoreReference##T { \ |
|
894 private: \ |
|
895 static void store(T* heap, const Value &v); \ |
|
896 \ |
|
897 public: \ |
|
898 static bool Func(ThreadSafeContext *cx, unsigned argc, Value *vp); \ |
|
899 static const JSJitInfo JitInfo; \ |
|
900 }; |
|
901 |
|
902 /* |
|
903 * Usage: LoadScalar(targetDatum, targetOffset, value) |
|
904 * |
|
905 * Intrinsic function. Loads value (which must be an int32 or uint32) |
|
906 * by `scalarTypeRepr` (which must be a type repr obj) and loads the |
|
907 * value at the memory for `targetDatum` at offset `targetOffset`. |
|
908 * `targetDatum` must be attached. |
|
909 */ |
|
910 #define JS_LOAD_SCALAR_CLASS_DEFN(_constant, T, _name) \ |
|
911 class LoadScalar##T { \ |
|
912 public: \ |
|
913 static bool Func(ThreadSafeContext *cx, unsigned argc, Value *vp); \ |
|
914 static const JSJitInfo JitInfo; \ |
|
915 }; |
|
916 |
|
917 /* |
|
918 * Usage: LoadReference(targetDatum, targetOffset, value) |
|
919 * |
|
920 * Intrinsic function. Stores value (which must be an int32 or uint32) |
|
921 * by `scalarTypeRepr` (which must be a type repr obj) and stores the |
|
922 * value at the memory for `targetDatum` at offset `targetOffset`. |
|
923 * `targetDatum` must be attached. |
|
924 */ |
|
925 #define JS_LOAD_REFERENCE_CLASS_DEFN(_constant, T, _name) \ |
|
926 class LoadReference##T { \ |
|
927 private: \ |
|
928 static void load(T* heap, MutableHandleValue v); \ |
|
929 \ |
|
930 public: \ |
|
931 static bool Func(ThreadSafeContext *cx, unsigned argc, Value *vp); \ |
|
932 static const JSJitInfo JitInfo; \ |
|
933 }; |
|
934 |
|
935 // I was using templates for this stuff instead of macros, but ran |
|
936 // into problems with the Unagi compiler. |
|
937 JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_STORE_SCALAR_CLASS_DEFN) |
|
938 JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_LOAD_SCALAR_CLASS_DEFN) |
|
939 JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_STORE_REFERENCE_CLASS_DEFN) |
|
940 JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_LOAD_REFERENCE_CLASS_DEFN) |
|
941 |
|
942 inline bool |
|
943 IsTypedObjectClass(const Class *class_) |
|
944 { |
|
945 return class_ == &TransparentTypedObject::class_ || |
|
946 class_ == &OpaqueTypedObject::class_; |
|
947 } |
|
948 |
|
949 inline bool |
|
950 IsSimpleTypeDescrClass(const Class* clasp) |
|
951 { |
|
952 return clasp == &ScalarTypeDescr::class_ || |
|
953 clasp == &ReferenceTypeDescr::class_; |
|
954 } |
|
955 |
|
956 inline bool |
|
957 IsComplexTypeDescrClass(const Class* clasp) |
|
958 { |
|
959 return clasp == &StructTypeDescr::class_ || |
|
960 clasp == &SizedArrayTypeDescr::class_ || |
|
961 clasp == &X4TypeDescr::class_; |
|
962 } |
|
963 |
|
964 inline bool |
|
965 IsSizedTypeDescrClass(const Class* clasp) |
|
966 { |
|
967 return IsSimpleTypeDescrClass(clasp) || |
|
968 IsComplexTypeDescrClass(clasp); |
|
969 } |
|
970 |
|
971 inline bool |
|
972 IsTypeDescrClass(const Class* clasp) |
|
973 { |
|
974 return IsSizedTypeDescrClass(clasp) || |
|
975 clasp == &UnsizedArrayTypeDescr::class_; |
|
976 } |
|
977 |
|
978 } // namespace js |
|
979 |
|
980 JSObject * |
|
981 js_InitTypedObjectModuleObject(JSContext *cx, JS::HandleObject obj); |
|
982 |
|
983 template <> |
|
984 inline bool |
|
985 JSObject::is<js::SimpleTypeDescr>() const |
|
986 { |
|
987 return IsSimpleTypeDescrClass(getClass()); |
|
988 } |
|
989 |
|
990 template <> |
|
991 inline bool |
|
992 JSObject::is<js::SizedTypeDescr>() const |
|
993 { |
|
994 return IsSizedTypeDescrClass(getClass()); |
|
995 } |
|
996 |
|
997 template <> |
|
998 inline bool |
|
999 JSObject::is<js::ComplexTypeDescr>() const |
|
1000 { |
|
1001 return IsComplexTypeDescrClass(getClass()); |
|
1002 } |
|
1003 |
|
1004 template <> |
|
1005 inline bool |
|
1006 JSObject::is<js::TypeDescr>() const |
|
1007 { |
|
1008 return IsTypeDescrClass(getClass()); |
|
1009 } |
|
1010 |
|
1011 template <> |
|
1012 inline bool |
|
1013 JSObject::is<js::TypedObject>() const |
|
1014 { |
|
1015 return IsTypedObjectClass(getClass()); |
|
1016 } |
|
1017 |
|
1018 template<> |
|
1019 inline bool |
|
1020 JSObject::is<js::SizedArrayTypeDescr>() const |
|
1021 { |
|
1022 return getClass() == &js::SizedArrayTypeDescr::class_; |
|
1023 } |
|
1024 |
|
1025 template<> |
|
1026 inline bool |
|
1027 JSObject::is<js::UnsizedArrayTypeDescr>() const |
|
1028 { |
|
1029 return getClass() == &js::UnsizedArrayTypeDescr::class_; |
|
1030 } |
|
1031 |
|
1032 inline void |
|
1033 js::TypedProto::initTypeDescrSlot(TypeDescr &descr) |
|
1034 { |
|
1035 initReservedSlot(JS_TYPROTO_SLOT_DESCR, ObjectValue(descr)); |
|
1036 } |
|
1037 |
|
1038 #endif /* builtin_TypedObject_h */ |