js/src/builtin/TypedObject.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:042f7e1d92ce
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 #include "builtin/TypedObject.h"
8
9 #include "mozilla/CheckedInt.h"
10
11 #include "jscompartment.h"
12 #include "jsfun.h"
13 #include "jsobj.h"
14 #include "jsutil.h"
15
16 #include "gc/Marking.h"
17 #include "js/Vector.h"
18 #include "vm/GlobalObject.h"
19 #include "vm/ObjectImpl.h"
20 #include "vm/String.h"
21 #include "vm/StringBuffer.h"
22 #include "vm/TypedArrayObject.h"
23
24 #include "jsatominlines.h"
25 #include "jsobjinlines.h"
26
27 #include "vm/Shape-inl.h"
28
29 using mozilla::CheckedInt32;
30 using mozilla::DebugOnly;
31
32 using namespace js;
33
34 const Class js::TypedObjectModuleObject::class_ = {
35 "TypedObject",
36 JSCLASS_HAS_RESERVED_SLOTS(SlotCount) |
37 JSCLASS_HAS_CACHED_PROTO(JSProto_TypedObject),
38 JS_PropertyStub, /* addProperty */
39 JS_DeletePropertyStub, /* delProperty */
40 JS_PropertyStub, /* getProperty */
41 JS_StrictPropertyStub, /* setProperty */
42 JS_EnumerateStub,
43 JS_ResolveStub,
44 JS_ConvertStub
45 };
46
47 static const JSFunctionSpec TypedObjectMethods[] = {
48 JS_SELF_HOSTED_FN("objectType", "TypeOfTypedObject", 1, 0),
49 JS_SELF_HOSTED_FN("storage", "StorageOfTypedObject", 1, 0),
50 JS_FS_END
51 };
52
53 static void
54 ReportCannotConvertTo(JSContext *cx, HandleValue fromValue, const char *toType)
55 {
56 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
57 InformalValueTypeName(fromValue), toType);
58 }
59
60 template<class T>
61 static inline T*
62 ToObjectIf(HandleValue value)
63 {
64 if (!value.isObject())
65 return nullptr;
66
67 if (!value.toObject().is<T>())
68 return nullptr;
69
70 return &value.toObject().as<T>();
71 }
72
73 static inline CheckedInt32 roundUpToAlignment(CheckedInt32 address, int32_t align)
74 {
75 JS_ASSERT(IsPowerOfTwo(align));
76
77 // Note: Be careful to order operators such that we first make the
78 // value smaller and then larger, so that we don't get false
79 // overflow errors due to (e.g.) adding `align` and then
80 // subtracting `1` afterwards when merely adding `align-1` would
81 // not have overflowed. Note that due to the nature of two's
82 // complement representation, if `address` is already aligned,
83 // then adding `align-1` cannot itself cause an overflow.
84
85 return ((address + (align - 1)) / align) * align;
86 }
87
88 /*
89 * Overwrites the contents of `typedObj` at offset `offset` with `val`
90 * converted to the type `typeObj`. This is done by delegating to
91 * self-hosted code. This is used for assignments and initializations.
92 *
93 * For example, consider the final assignment in this snippet:
94 *
95 * var Point = new StructType({x: float32, y: float32});
96 * var Line = new StructType({from: Point, to: Point});
97 * var line = new Line();
98 * line.to = {x: 22, y: 44};
99 *
100 * This would result in a call to `ConvertAndCopyTo`
101 * where:
102 * - typeObj = Point
103 * - typedObj = line
104 * - offset = sizeof(Point) == 8
105 * - val = {x: 22, y: 44}
106 * This would result in loading the value of `x`, converting
107 * it to a float32, and hen storing it at the appropriate offset,
108 * and then doing the same for `y`.
109 *
110 * Note that the type of `typeObj` may not be the
111 * type of `typedObj` but rather some subcomponent of `typedObj`.
112 */
113 static bool
114 ConvertAndCopyTo(JSContext *cx,
115 HandleTypeDescr typeObj,
116 HandleTypedObject typedObj,
117 int32_t offset,
118 HandleValue val)
119 {
120 RootedFunction func(
121 cx, SelfHostedFunction(cx, cx->names().ConvertAndCopyTo));
122 if (!func)
123 return false;
124
125 InvokeArgs args(cx);
126 if (!args.init(4))
127 return false;
128
129 args.setCallee(ObjectValue(*func));
130 args[0].setObject(*typeObj);
131 args[1].setObject(*typedObj);
132 args[2].setInt32(offset);
133 args[3].set(val);
134
135 return Invoke(cx, args);
136 }
137
138 static bool
139 ConvertAndCopyTo(JSContext *cx, HandleTypedObject typedObj, HandleValue val)
140 {
141 Rooted<TypeDescr*> type(cx, &typedObj->typeDescr());
142 return ConvertAndCopyTo(cx, type, typedObj, 0, val);
143 }
144
145 /*
146 * Overwrites the contents of `typedObj` at offset `offset` with `val`
147 * converted to the type `typeObj`
148 */
149 static bool
150 Reify(JSContext *cx,
151 HandleTypeDescr type,
152 HandleTypedObject typedObj,
153 size_t offset,
154 MutableHandleValue to)
155 {
156 RootedFunction func(cx, SelfHostedFunction(cx, cx->names().Reify));
157 if (!func)
158 return false;
159
160 InvokeArgs args(cx);
161 if (!args.init(3))
162 return false;
163
164 args.setCallee(ObjectValue(*func));
165 args[0].setObject(*type);
166 args[1].setObject(*typedObj);
167 args[2].setInt32(offset);
168
169 if (!Invoke(cx, args))
170 return false;
171
172 to.set(args.rval());
173 return true;
174 }
175
176 // Extracts the `prototype` property from `obj`, throwing if it is
177 // missing or not an object.
178 static JSObject *
179 GetPrototype(JSContext *cx, HandleObject obj)
180 {
181 RootedValue prototypeVal(cx);
182 if (!JSObject::getProperty(cx, obj, obj, cx->names().prototype,
183 &prototypeVal))
184 {
185 return nullptr;
186 }
187 if (!prototypeVal.isObject()) {
188 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
189 JSMSG_INVALID_PROTOTYPE);
190 return nullptr;
191 }
192 return &prototypeVal.toObject();
193 }
194
195 /***************************************************************************
196 * Typed Prototypes
197 *
198 * Every type descriptor has an associated prototype. Instances of
199 * that type descriptor use this as their prototype. Per the spec,
200 * typed object prototypes cannot be mutated.
201 */
202
203 const Class js::TypedProto::class_ = {
204 "TypedProto",
205 JSCLASS_HAS_RESERVED_SLOTS(JS_TYPROTO_SLOTS),
206 JS_PropertyStub, /* addProperty */
207 JS_DeletePropertyStub, /* delProperty */
208 JS_PropertyStub, /* getProperty */
209 JS_StrictPropertyStub, /* setProperty */
210 JS_EnumerateStub,
211 JS_ResolveStub,
212 JS_ConvertStub,
213 nullptr,
214 nullptr,
215 nullptr,
216 nullptr,
217 nullptr
218 };
219
220 /***************************************************************************
221 * Scalar type objects
222 *
223 * Scalar type objects like `uint8`, `uint16`, are all instances of
224 * the ScalarTypeDescr class. Like all type objects, they have a reserved
225 * slot pointing to a TypeRepresentation object, which is used to
226 * distinguish which scalar type object this actually is.
227 */
228
229 const Class js::ScalarTypeDescr::class_ = {
230 "Scalar",
231 JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS),
232 JS_PropertyStub, /* addProperty */
233 JS_DeletePropertyStub, /* delProperty */
234 JS_PropertyStub, /* getProperty */
235 JS_StrictPropertyStub, /* setProperty */
236 JS_EnumerateStub,
237 JS_ResolveStub,
238 JS_ConvertStub,
239 nullptr,
240 ScalarTypeDescr::call,
241 nullptr,
242 nullptr,
243 nullptr
244 };
245
246 const JSFunctionSpec js::ScalarTypeDescr::typeObjectMethods[] = {
247 JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
248 {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
249 {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
250 JS_FS_END
251 };
252
253 static int32_t ScalarSizes[] = {
254 #define SCALAR_SIZE(_kind, _type, _name) \
255 sizeof(_type),
256 JS_FOR_EACH_SCALAR_TYPE_REPR(SCALAR_SIZE) 0
257 #undef SCALAR_SIZE
258 };
259
260 int32_t
261 ScalarTypeDescr::size(Type t)
262 {
263 return ScalarSizes[t];
264 }
265
266 int32_t
267 ScalarTypeDescr::alignment(Type t)
268 {
269 return ScalarSizes[t];
270 }
271
272 /*static*/ const char *
273 ScalarTypeDescr::typeName(Type type)
274 {
275 switch (type) {
276 #define NUMERIC_TYPE_TO_STRING(constant_, type_, name_) \
277 case constant_: return #name_;
278 JS_FOR_EACH_SCALAR_TYPE_REPR(NUMERIC_TYPE_TO_STRING)
279 }
280 MOZ_ASSUME_UNREACHABLE("Invalid type");
281 }
282
283 bool
284 ScalarTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
285 {
286 CallArgs args = CallArgsFromVp(argc, vp);
287 if (args.length() < 1) {
288 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
289 args.callee().getClass()->name, "0", "s");
290 return false;
291 }
292
293 Rooted<ScalarTypeDescr *> descr(cx, &args.callee().as<ScalarTypeDescr>());
294 ScalarTypeDescr::Type type = descr->type();
295
296 double number;
297 if (!ToNumber(cx, args[0], &number))
298 return false;
299
300 if (type == ScalarTypeDescr::TYPE_UINT8_CLAMPED)
301 number = ClampDoubleToUint8(number);
302
303 switch (type) {
304 #define SCALARTYPE_CALL(constant_, type_, name_) \
305 case constant_: { \
306 type_ converted = ConvertScalar<type_>(number); \
307 args.rval().setNumber((double) converted); \
308 return true; \
309 }
310
311 JS_FOR_EACH_SCALAR_TYPE_REPR(SCALARTYPE_CALL)
312 #undef SCALARTYPE_CALL
313
314 }
315 return true;
316 }
317
318 /***************************************************************************
319 * Reference type objects
320 *
321 * Reference type objects like `Any` or `Object` basically work the
322 * same way that the scalar type objects do. There is one class with
323 * many instances, and each instance has a reserved slot with a
324 * TypeRepresentation object, which is used to distinguish which
325 * reference type object this actually is.
326 */
327
328 const Class js::ReferenceTypeDescr::class_ = {
329 "Reference",
330 JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS),
331 JS_PropertyStub, /* addProperty */
332 JS_DeletePropertyStub, /* delProperty */
333 JS_PropertyStub, /* getProperty */
334 JS_StrictPropertyStub, /* setProperty */
335 JS_EnumerateStub,
336 JS_ResolveStub,
337 JS_ConvertStub,
338 nullptr,
339 ReferenceTypeDescr::call,
340 nullptr,
341 nullptr,
342 nullptr
343 };
344
345 const JSFunctionSpec js::ReferenceTypeDescr::typeObjectMethods[] = {
346 JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
347 {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
348 {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
349 JS_FS_END
350 };
351
352 static int32_t ReferenceSizes[] = {
353 #define REFERENCE_SIZE(_kind, _type, _name) \
354 sizeof(_type),
355 JS_FOR_EACH_REFERENCE_TYPE_REPR(REFERENCE_SIZE) 0
356 #undef REFERENCE_SIZE
357 };
358
359 int32_t
360 ReferenceTypeDescr::size(Type t)
361 {
362 return ReferenceSizes[t];
363 }
364
365 int32_t
366 ReferenceTypeDescr::alignment(Type t)
367 {
368 return ReferenceSizes[t];
369 }
370
371 /*static*/ const char *
372 ReferenceTypeDescr::typeName(Type type)
373 {
374 switch (type) {
375 #define NUMERIC_TYPE_TO_STRING(constant_, type_, name_) \
376 case constant_: return #name_;
377 JS_FOR_EACH_REFERENCE_TYPE_REPR(NUMERIC_TYPE_TO_STRING)
378 }
379 MOZ_ASSUME_UNREACHABLE("Invalid type");
380 }
381
382 bool
383 js::ReferenceTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
384 {
385 CallArgs args = CallArgsFromVp(argc, vp);
386
387 JS_ASSERT(args.callee().is<ReferenceTypeDescr>());
388 Rooted<ReferenceTypeDescr *> descr(cx, &args.callee().as<ReferenceTypeDescr>());
389
390 if (args.length() < 1) {
391 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
392 JSMSG_MORE_ARGS_NEEDED,
393 descr->typeName(), "0", "s");
394 return false;
395 }
396
397 switch (descr->type()) {
398 case ReferenceTypeDescr::TYPE_ANY:
399 args.rval().set(args[0]);
400 return true;
401
402 case ReferenceTypeDescr::TYPE_OBJECT:
403 {
404 RootedObject obj(cx, ToObject(cx, args[0]));
405 if (!obj)
406 return false;
407 args.rval().setObject(*obj);
408 return true;
409 }
410
411 case ReferenceTypeDescr::TYPE_STRING:
412 {
413 RootedString obj(cx, ToString<CanGC>(cx, args[0]));
414 if (!obj)
415 return false;
416 args.rval().setString(&*obj);
417 return true;
418 }
419 }
420
421 MOZ_ASSUME_UNREACHABLE("Unhandled Reference type");
422 }
423
424 /***************************************************************************
425 * X4 type objects
426 *
427 * Note: these are partially defined in SIMD.cpp
428 */
429
430 static int32_t X4Sizes[] = {
431 #define X4_SIZE(_kind, _type, _name) \
432 sizeof(_type) * 4,
433 JS_FOR_EACH_X4_TYPE_REPR(X4_SIZE) 0
434 #undef X4_SIZE
435 };
436
437 int32_t
438 X4TypeDescr::size(Type t)
439 {
440 return X4Sizes[t];
441 }
442
443 int32_t
444 X4TypeDescr::alignment(Type t)
445 {
446 return X4Sizes[t];
447 }
448
449 /***************************************************************************
450 * ArrayMetaTypeDescr class
451 */
452
453 /*
454 * For code like:
455 *
456 * var A = new TypedObject.ArrayType(uint8, 10);
457 * var S = new TypedObject.StructType({...});
458 *
459 * As usual, the [[Prototype]] of A is
460 * TypedObject.ArrayType.prototype. This permits adding methods to
461 * all ArrayType types, by setting
462 * TypedObject.ArrayType.prototype.methodName = function() { ... }.
463 * The same holds for S with respect to TypedObject.StructType.
464 *
465 * We may also want to add methods to *instances* of an ArrayType:
466 *
467 * var a = new A();
468 * var s = new S();
469 *
470 * As usual, the [[Prototype]] of a is A.prototype. What's
471 * A.prototype? It's an empty object, and you can set
472 * A.prototype.methodName = function() { ... } to add a method to all
473 * A instances. (And the same with respect to s and S.)
474 *
475 * But what if you want to add a method to all ArrayType instances,
476 * not just all A instances? (Or to all StructType instances.) The
477 * [[Prototype]] of the A.prototype empty object is
478 * TypedObject.ArrayType.prototype.prototype (two .prototype levels!).
479 * So just set TypedObject.ArrayType.prototype.prototype.methodName =
480 * function() { ... } to add a method to all ArrayType instances.
481 * (And, again, same with respect to s and S.)
482 *
483 * This function creates the A.prototype/S.prototype object. It takes
484 * as an argument either the TypedObject.ArrayType or the
485 * TypedObject.StructType constructor function, then returns an empty
486 * object with the .prototype.prototype object as its [[Prototype]].
487 */
488 static TypedProto *
489 CreatePrototypeObjectForComplexTypeInstance(JSContext *cx,
490 Handle<TypeDescr*> descr,
491 HandleObject ctorPrototype)
492 {
493 RootedObject ctorPrototypePrototype(cx, GetPrototype(cx, ctorPrototype));
494 if (!ctorPrototypePrototype)
495 return nullptr;
496
497 Rooted<TypedProto*> result(cx);
498 result = NewObjectWithProto<TypedProto>(cx,
499 &*ctorPrototypePrototype,
500 nullptr,
501 TenuredObject);
502 if (!result)
503 return nullptr;
504
505 result->initTypeDescrSlot(*descr);
506 return result;
507 }
508
509 const Class UnsizedArrayTypeDescr::class_ = {
510 "ArrayType",
511 JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS),
512 JS_PropertyStub,
513 JS_DeletePropertyStub,
514 JS_PropertyStub,
515 JS_StrictPropertyStub,
516 JS_EnumerateStub,
517 JS_ResolveStub,
518 JS_ConvertStub,
519 nullptr,
520 nullptr,
521 nullptr,
522 TypedObject::constructUnsized,
523 nullptr
524 };
525
526 const Class SizedArrayTypeDescr::class_ = {
527 "ArrayType",
528 JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS),
529 JS_PropertyStub,
530 JS_DeletePropertyStub,
531 JS_PropertyStub,
532 JS_StrictPropertyStub,
533 JS_EnumerateStub,
534 JS_ResolveStub,
535 JS_ConvertStub,
536 nullptr,
537 nullptr,
538 nullptr,
539 TypedObject::constructSized,
540 nullptr
541 };
542
543 const JSPropertySpec ArrayMetaTypeDescr::typeObjectProperties[] = {
544 JS_PS_END
545 };
546
547 const JSFunctionSpec ArrayMetaTypeDescr::typeObjectMethods[] = {
548 {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
549 JS_FN("dimension", UnsizedArrayTypeDescr::dimension, 1, 0),
550 JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
551 {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
552 JS_SELF_HOSTED_FN("build", "TypedObjectArrayTypeBuild", 3, 0),
553 JS_SELF_HOSTED_FN("buildPar", "TypedObjectArrayTypeBuildPar", 3, 0),
554 JS_SELF_HOSTED_FN("from", "TypedObjectArrayTypeFrom", 3, 0),
555 JS_SELF_HOSTED_FN("fromPar", "TypedObjectArrayTypeFromPar", 3, 0),
556 JS_FS_END
557 };
558
559 const JSPropertySpec ArrayMetaTypeDescr::typedObjectProperties[] = {
560 JS_PS_END
561 };
562
563 const JSFunctionSpec ArrayMetaTypeDescr::typedObjectMethods[] = {
564 {"forEach", {nullptr, nullptr}, 1, 0, "ArrayForEach"},
565 {"redimension", {nullptr, nullptr}, 1, 0, "TypedArrayRedimension"},
566 JS_SELF_HOSTED_FN("map", "TypedArrayMap", 2, 0),
567 JS_SELF_HOSTED_FN("mapPar", "TypedArrayMapPar", 2, 0),
568 JS_SELF_HOSTED_FN("reduce", "TypedArrayReduce", 2, 0),
569 JS_SELF_HOSTED_FN("reducePar", "TypedArrayReducePar", 2, 0),
570 JS_SELF_HOSTED_FN("scatter", "TypedArrayScatter", 4, 0),
571 JS_SELF_HOSTED_FN("scatterPar", "TypedArrayScatterPar", 4, 0),
572 JS_SELF_HOSTED_FN("filter", "TypedArrayFilter", 1, 0),
573 JS_SELF_HOSTED_FN("filterPar", "TypedArrayFilterPar", 1, 0),
574 JS_FS_END
575 };
576
577 bool
578 js::CreateUserSizeAndAlignmentProperties(JSContext *cx, HandleTypeDescr descr)
579 {
580 // If data is transparent, also store the public slots.
581 if (descr->transparent() && descr->is<SizedTypeDescr>()) {
582 Rooted<SizedTypeDescr*> sizedDescr(cx, &descr->as<SizedTypeDescr>());
583
584 // byteLength
585 RootedValue typeByteLength(cx, Int32Value(sizedDescr->size()));
586 if (!JSObject::defineProperty(cx, descr, cx->names().byteLength,
587 typeByteLength,
588 nullptr, nullptr,
589 JSPROP_READONLY | JSPROP_PERMANENT))
590 {
591 return false;
592 }
593
594 // byteAlignment
595 RootedValue typeByteAlignment(cx, Int32Value(sizedDescr->alignment()));
596 if (!JSObject::defineProperty(cx, descr, cx->names().byteAlignment,
597 typeByteAlignment,
598 nullptr, nullptr,
599 JSPROP_READONLY | JSPROP_PERMANENT))
600 {
601 return false;
602 }
603 } else {
604 // byteLength
605 if (!JSObject::defineProperty(cx, descr, cx->names().byteLength,
606 UndefinedHandleValue,
607 nullptr, nullptr,
608 JSPROP_READONLY | JSPROP_PERMANENT))
609 {
610 return false;
611 }
612
613 // byteAlignment
614 if (!JSObject::defineProperty(cx, descr, cx->names().byteAlignment,
615 UndefinedHandleValue,
616 nullptr, nullptr,
617 JSPROP_READONLY | JSPROP_PERMANENT))
618 {
619 return false;
620 }
621 }
622
623 // variable -- true for unsized arrays
624 RootedValue variable(cx, BooleanValue(!descr->is<SizedTypeDescr>()));
625 if (!JSObject::defineProperty(cx, descr, cx->names().variable,
626 variable,
627 nullptr, nullptr,
628 JSPROP_READONLY | JSPROP_PERMANENT))
629 {
630 return false;
631 }
632
633 return true;
634 }
635
636 template<class T>
637 T *
638 ArrayMetaTypeDescr::create(JSContext *cx,
639 HandleObject arrayTypePrototype,
640 HandleSizedTypeDescr elementType,
641 HandleAtom stringRepr,
642 int32_t size)
643 {
644 Rooted<T*> obj(cx);
645 obj = NewObjectWithProto<T>(cx, arrayTypePrototype, nullptr, TenuredObject);
646 if (!obj)
647 return nullptr;
648
649 obj->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(T::Kind));
650 obj->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
651 obj->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(elementType->alignment()));
652 obj->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(size));
653 obj->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(elementType->opaque()));
654 obj->initReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE, ObjectValue(*elementType));
655
656 RootedValue elementTypeVal(cx, ObjectValue(*elementType));
657 if (!JSObject::defineProperty(cx, obj, cx->names().elementType,
658 elementTypeVal, nullptr, nullptr,
659 JSPROP_READONLY | JSPROP_PERMANENT))
660 return nullptr;
661
662 if (!CreateUserSizeAndAlignmentProperties(cx, obj))
663 return nullptr;
664
665 Rooted<TypedProto*> prototypeObj(cx);
666 prototypeObj = CreatePrototypeObjectForComplexTypeInstance(cx, obj, arrayTypePrototype);
667 if (!prototypeObj)
668 return nullptr;
669
670 obj->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj));
671
672 if (!LinkConstructorAndPrototype(cx, obj, prototypeObj))
673 return nullptr;
674
675 return obj;
676 }
677
678 bool
679 ArrayMetaTypeDescr::construct(JSContext *cx, unsigned argc, Value *vp)
680 {
681 CallArgs args = CallArgsFromVp(argc, vp);
682
683 if (!args.isConstructing()) {
684 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
685 JSMSG_NOT_FUNCTION, "ArrayType");
686 return false;
687 }
688
689 RootedObject arrayTypeGlobal(cx, &args.callee());
690
691 // Expect one argument which is a sized type object
692 if (args.length() < 1) {
693 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
694 "ArrayType", "0", "");
695 return false;
696 }
697
698 if (!args[0].isObject() || !args[0].toObject().is<SizedTypeDescr>()) {
699 ReportCannotConvertTo(cx, args[0], "ArrayType element specifier");
700 return false;
701 }
702
703 Rooted<SizedTypeDescr*> elementType(cx);
704 elementType = &args[0].toObject().as<SizedTypeDescr>();
705
706 // Construct a canonical string `new ArrayType(<elementType>)`:
707 StringBuffer contents(cx);
708 contents.append("new ArrayType(");
709 contents.append(&elementType->stringRepr());
710 contents.append(")");
711 RootedAtom stringRepr(cx, contents.finishAtom());
712 if (!stringRepr)
713 return nullptr;
714
715 // Extract ArrayType.prototype
716 RootedObject arrayTypePrototype(cx, GetPrototype(cx, arrayTypeGlobal));
717 if (!arrayTypePrototype)
718 return nullptr;
719
720 // Create the instance of ArrayType
721 Rooted<UnsizedArrayTypeDescr *> obj(cx);
722 obj = create<UnsizedArrayTypeDescr>(cx, arrayTypePrototype, elementType,
723 stringRepr, 0);
724 if (!obj)
725 return false;
726
727 // Add `length` property, which is undefined for an unsized array.
728 if (!JSObject::defineProperty(cx, obj, cx->names().length,
729 UndefinedHandleValue, nullptr, nullptr,
730 JSPROP_READONLY | JSPROP_PERMANENT))
731 return nullptr;
732
733 args.rval().setObject(*obj);
734 return true;
735 }
736
737 /*static*/ bool
738 UnsizedArrayTypeDescr::dimension(JSContext *cx, unsigned int argc, jsval *vp)
739 {
740 // Expect that the `this` pointer is an unsized array type
741 // and the first argument is an integer size.
742 CallArgs args = CallArgsFromVp(argc, vp);
743 if (args.length() != 1 ||
744 !args.thisv().isObject() ||
745 !args.thisv().toObject().is<UnsizedArrayTypeDescr>() ||
746 !args[0].isInt32() ||
747 args[0].toInt32() < 0)
748 {
749 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
750 JSMSG_TYPEDOBJECT_ARRAYTYPE_BAD_ARGS);
751 return false;
752 }
753
754 // Extract arguments.
755 Rooted<UnsizedArrayTypeDescr*> unsizedTypeDescr(cx);
756 unsizedTypeDescr = &args.thisv().toObject().as<UnsizedArrayTypeDescr>();
757 int32_t length = args[0].toInt32();
758 Rooted<SizedTypeDescr*> elementType(cx, &unsizedTypeDescr->elementType());
759
760 // Compute the size.
761 CheckedInt32 size = CheckedInt32(elementType->size()) * length;
762 if (!size.isValid()) {
763 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
764 JSMSG_TYPEDOBJECT_TOO_BIG);
765 return nullptr;
766 }
767
768 // Construct a canonical string `new ArrayType(<elementType>).dimension(N)`:
769 StringBuffer contents(cx);
770 contents.append("new ArrayType(");
771 contents.append(&elementType->stringRepr());
772 contents.append(").dimension(");
773 if (!NumberValueToStringBuffer(cx, NumberValue(length), contents))
774 return false;
775 contents.append(")");
776 RootedAtom stringRepr(cx, contents.finishAtom());
777 if (!stringRepr)
778 return nullptr;
779
780 // Create the sized type object.
781 Rooted<SizedArrayTypeDescr*> obj(cx);
782 obj = ArrayMetaTypeDescr::create<SizedArrayTypeDescr>(cx, unsizedTypeDescr,
783 elementType,
784 stringRepr, size.value());
785 if (!obj)
786 return false;
787
788 obj->initReservedSlot(JS_DESCR_SLOT_SIZED_ARRAY_LENGTH,
789 Int32Value(length));
790
791 // Add `length` property.
792 RootedValue lengthVal(cx, Int32Value(length));
793 if (!JSObject::defineProperty(cx, obj, cx->names().length,
794 lengthVal, nullptr, nullptr,
795 JSPROP_READONLY | JSPROP_PERMANENT))
796 return nullptr;
797
798 // Add `unsized` property, which is a link from the sized
799 // array to the unsized array.
800 RootedValue unsizedTypeDescrValue(cx, ObjectValue(*unsizedTypeDescr));
801 if (!JSObject::defineProperty(cx, obj, cx->names().unsized,
802 unsizedTypeDescrValue, nullptr, nullptr,
803 JSPROP_READONLY | JSPROP_PERMANENT))
804 return nullptr;
805
806 args.rval().setObject(*obj);
807 return true;
808 }
809
810 bool
811 js::IsTypedObjectArray(JSObject &obj)
812 {
813 if (!obj.is<TypedObject>())
814 return false;
815 TypeDescr& d = obj.as<TypedObject>().typeDescr();
816 return d.is<SizedArrayTypeDescr>() || d.is<UnsizedArrayTypeDescr>();
817 }
818
819 /*********************************
820 * StructType class
821 */
822
823 const Class StructTypeDescr::class_ = {
824 "StructType",
825 JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) |
826 JSCLASS_HAS_PRIVATE, // used to store FieldList
827 JS_PropertyStub,
828 JS_DeletePropertyStub,
829 JS_PropertyStub,
830 JS_StrictPropertyStub,
831 JS_EnumerateStub,
832 JS_ResolveStub,
833 JS_ConvertStub,
834 nullptr, /* finalize */
835 nullptr, /* call */
836 nullptr, /* hasInstance */
837 TypedObject::constructSized,
838 nullptr /* trace */
839 };
840
841 const JSPropertySpec StructMetaTypeDescr::typeObjectProperties[] = {
842 JS_PS_END
843 };
844
845 const JSFunctionSpec StructMetaTypeDescr::typeObjectMethods[] = {
846 {"array", {nullptr, nullptr}, 1, 0, "ArrayShorthand"},
847 JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
848 {"equivalent", {nullptr, nullptr}, 1, 0, "TypeDescrEquivalent"},
849 JS_FS_END
850 };
851
852 const JSPropertySpec StructMetaTypeDescr::typedObjectProperties[] = {
853 JS_PS_END
854 };
855
856 const JSFunctionSpec StructMetaTypeDescr::typedObjectMethods[] = {
857 JS_FS_END
858 };
859
860 JSObject *
861 StructMetaTypeDescr::create(JSContext *cx,
862 HandleObject metaTypeDescr,
863 HandleObject fields)
864 {
865 // Obtain names of fields, which are the own properties of `fields`
866 AutoIdVector ids(cx);
867 if (!GetPropertyNames(cx, fields, JSITER_OWNONLY, &ids))
868 return nullptr;
869
870 // Iterate through each field. Collect values for the various
871 // vectors below and also track total size and alignment. Be wary
872 // of overflow!
873 StringBuffer stringBuffer(cx); // Canonical string repr
874 AutoValueVector fieldNames(cx); // Name of each field.
875 AutoValueVector fieldTypeObjs(cx); // Type descriptor of each field.
876 AutoValueVector fieldOffsets(cx); // Offset of each field field.
877 RootedObject userFieldOffsets(cx); // User-exposed {f:offset} object
878 RootedObject userFieldTypes(cx); // User-exposed {f:descr} object.
879 CheckedInt32 sizeSoFar(0); // Size of struct thus far.
880 int32_t alignment = 1; // Alignment of struct.
881 bool opaque = false; // Opacity of struct.
882
883 userFieldOffsets = NewObjectWithProto<JSObject>(cx, nullptr, nullptr, TenuredObject);
884 if (!userFieldOffsets)
885 return nullptr;
886
887 userFieldTypes = NewObjectWithProto<JSObject>(cx, nullptr, nullptr, TenuredObject);
888 if (!userFieldTypes)
889 return nullptr;
890
891 if (!stringBuffer.append("new StructType({")) {
892 js_ReportOutOfMemory(cx);
893 return nullptr;
894 }
895
896 RootedValue fieldTypeVal(cx);
897 RootedId id(cx);
898 Rooted<SizedTypeDescr*> fieldType(cx);
899 for (unsigned int i = 0; i < ids.length(); i++) {
900 id = ids[i];
901
902 // Check that all the property names are non-numeric strings.
903 uint32_t unused;
904 if (!JSID_IS_ATOM(id) || JSID_TO_ATOM(id)->isIndex(&unused)) {
905 RootedValue idValue(cx, IdToValue(id));
906 ReportCannotConvertTo(cx, idValue, "StructType field name");
907 return nullptr;
908 }
909
910 // Load the value for the current field from the `fields` object.
911 // The value should be a type descriptor.
912 if (!JSObject::getGeneric(cx, fields, fields, id, &fieldTypeVal))
913 return nullptr;
914 fieldType = ToObjectIf<SizedTypeDescr>(fieldTypeVal);
915 if (!fieldType) {
916 ReportCannotConvertTo(cx, fieldTypeVal, "StructType field specifier");
917 return nullptr;
918 }
919
920 // Collect field name and type object
921 RootedValue fieldName(cx, IdToValue(id));
922 if (!fieldNames.append(fieldName)) {
923 js_ReportOutOfMemory(cx);
924 return nullptr;
925 }
926 if (!fieldTypeObjs.append(ObjectValue(*fieldType))) {
927 js_ReportOutOfMemory(cx);
928 return nullptr;
929 }
930
931 // userFieldTypes[id] = typeObj
932 if (!JSObject::defineGeneric(cx, userFieldTypes, id,
933 fieldTypeObjs.handleAt(i), nullptr, nullptr,
934 JSPROP_READONLY | JSPROP_PERMANENT))
935 return nullptr;
936
937 // Append "f:Type" to the string repr
938 if (i > 0 && !stringBuffer.append(", ")) {
939 js_ReportOutOfMemory(cx);
940 return nullptr;
941 }
942 if (!stringBuffer.append(JSID_TO_ATOM(id))) {
943 js_ReportOutOfMemory(cx);
944 return nullptr;
945 }
946 if (!stringBuffer.append(": ")) {
947 js_ReportOutOfMemory(cx);
948 return nullptr;
949 }
950 if (!stringBuffer.append(&fieldType->stringRepr())) {
951 js_ReportOutOfMemory(cx);
952 return nullptr;
953 }
954
955 // Offset of this field is the current total size adjusted for
956 // the field's alignment.
957 CheckedInt32 offset = roundUpToAlignment(sizeSoFar, fieldType->alignment());
958 if (!offset.isValid()) {
959 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
960 JSMSG_TYPEDOBJECT_TOO_BIG);
961 return nullptr;
962 }
963 if (!fieldOffsets.append(Int32Value(offset.value()))) {
964 js_ReportOutOfMemory(cx);
965 return nullptr;
966 }
967
968 // userFieldOffsets[id] = offset
969 RootedValue offsetValue(cx, Int32Value(offset.value()));
970 if (!JSObject::defineGeneric(cx, userFieldOffsets, id,
971 offsetValue, nullptr, nullptr,
972 JSPROP_READONLY | JSPROP_PERMANENT))
973 return nullptr;
974
975 // Add space for this field to the total struct size.
976 sizeSoFar = offset + fieldType->size();
977 if (!sizeSoFar.isValid()) {
978 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
979 JSMSG_TYPEDOBJECT_TOO_BIG);
980 return nullptr;
981 }
982
983 // Struct is opaque if any field is opaque
984 if (fieldType->opaque())
985 opaque = true;
986
987 // Alignment of the struct is the max of the alignment of its fields.
988 alignment = js::Max(alignment, fieldType->alignment());
989 }
990
991 // Complete string representation.
992 if (!stringBuffer.append("})")) {
993 js_ReportOutOfMemory(cx);
994 return nullptr;
995 }
996 RootedAtom stringRepr(cx, stringBuffer.finishAtom());
997 if (!stringRepr)
998 return nullptr;
999
1000 // Adjust the total size to be a multiple of the final alignment.
1001 CheckedInt32 totalSize = roundUpToAlignment(sizeSoFar, alignment);
1002 if (!totalSize.isValid()) {
1003 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
1004 JSMSG_TYPEDOBJECT_TOO_BIG);
1005 return nullptr;
1006 }
1007
1008 // Now create the resulting type descriptor.
1009 RootedObject structTypePrototype(cx, GetPrototype(cx, metaTypeDescr));
1010 if (!structTypePrototype)
1011 return nullptr;
1012
1013 Rooted<StructTypeDescr*> descr(cx);
1014 descr = NewObjectWithProto<StructTypeDescr>(cx, structTypePrototype, nullptr,
1015 TenuredObject);
1016 if (!descr)
1017 return nullptr;
1018
1019 descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(TypeDescr::Struct));
1020 descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
1021 descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(alignment));
1022 descr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(totalSize.value()));
1023 descr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(opaque));
1024
1025 // Construct for internal use an array with the name for each field.
1026 {
1027 RootedObject fieldNamesVec(cx);
1028 fieldNamesVec = NewDenseCopiedArray(cx, fieldNames.length(),
1029 fieldNames.begin(), nullptr,
1030 TenuredObject);
1031 if (!fieldNamesVec)
1032 return nullptr;
1033 descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES,
1034 ObjectValue(*fieldNamesVec));
1035 }
1036
1037 // Construct for internal use an array with the type object for each field.
1038 {
1039 RootedObject fieldTypeVec(cx);
1040 fieldTypeVec = NewDenseCopiedArray(cx, fieldTypeObjs.length(),
1041 fieldTypeObjs.begin(), nullptr,
1042 TenuredObject);
1043 if (!fieldTypeVec)
1044 return nullptr;
1045 descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_TYPES,
1046 ObjectValue(*fieldTypeVec));
1047 }
1048
1049 // Construct for internal use an array with the offset for each field.
1050 {
1051 RootedObject fieldOffsetsVec(cx);
1052 fieldOffsetsVec = NewDenseCopiedArray(cx, fieldOffsets.length(),
1053 fieldOffsets.begin(), nullptr,
1054 TenuredObject);
1055 if (!fieldOffsetsVec)
1056 return nullptr;
1057 descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS,
1058 ObjectValue(*fieldOffsetsVec));
1059 }
1060
1061 // Create data properties fieldOffsets and fieldTypes
1062 if (!JSObject::freeze(cx, userFieldOffsets))
1063 return nullptr;
1064 if (!JSObject::freeze(cx, userFieldTypes))
1065 return nullptr;
1066 RootedValue userFieldOffsetsValue(cx, ObjectValue(*userFieldOffsets));
1067 if (!JSObject::defineProperty(cx, descr, cx->names().fieldOffsets,
1068 userFieldOffsetsValue, nullptr, nullptr,
1069 JSPROP_READONLY | JSPROP_PERMANENT))
1070 {
1071 return nullptr;
1072 }
1073 RootedValue userFieldTypesValue(cx, ObjectValue(*userFieldTypes));
1074 if (!JSObject::defineProperty(cx, descr, cx->names().fieldTypes,
1075 userFieldTypesValue, nullptr, nullptr,
1076 JSPROP_READONLY | JSPROP_PERMANENT))
1077 {
1078 return nullptr;
1079 }
1080
1081 if (!CreateUserSizeAndAlignmentProperties(cx, descr))
1082 return nullptr;
1083
1084 Rooted<TypedProto*> prototypeObj(cx);
1085 prototypeObj = CreatePrototypeObjectForComplexTypeInstance(cx, descr, structTypePrototype);
1086 if (!prototypeObj)
1087 return nullptr;
1088
1089 descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj));
1090
1091 if (!LinkConstructorAndPrototype(cx, descr, prototypeObj))
1092 return nullptr;
1093
1094 return descr;
1095 }
1096
1097 bool
1098 StructMetaTypeDescr::construct(JSContext *cx, unsigned int argc, Value *vp)
1099 {
1100 CallArgs args = CallArgsFromVp(argc, vp);
1101
1102 if (!args.isConstructing()) {
1103 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
1104 JSMSG_NOT_FUNCTION, "StructType");
1105 return false;
1106 }
1107
1108 if (args.length() >= 1 && args[0].isObject()) {
1109 RootedObject metaTypeDescr(cx, &args.callee());
1110 RootedObject fields(cx, &args[0].toObject());
1111 RootedObject obj(cx, create(cx, metaTypeDescr, fields));
1112 if (!obj)
1113 return false;
1114 args.rval().setObject(*obj);
1115 return true;
1116 }
1117
1118 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
1119 JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS);
1120 return false;
1121 }
1122
1123 size_t
1124 StructTypeDescr::fieldCount()
1125 {
1126 return getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).toObject().getDenseInitializedLength();
1127 }
1128
1129 bool
1130 StructTypeDescr::fieldIndex(jsid id, size_t *out)
1131 {
1132 JSObject &fieldNames = getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).toObject();
1133 size_t l = fieldNames.getDenseInitializedLength();
1134 for (size_t i = 0; i < l; i++) {
1135 JSAtom &a = fieldNames.getDenseElement(i).toString()->asAtom();
1136 if (JSID_IS_ATOM(id, &a)) {
1137 *out = i;
1138 return true;
1139 }
1140 }
1141 return false;
1142 }
1143
1144 JSAtom &
1145 StructTypeDescr::fieldName(size_t index)
1146 {
1147 JSObject &fieldNames = getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).toObject();
1148 return fieldNames.getDenseElement(index).toString()->asAtom();
1149 }
1150
1151 int32_t
1152 StructTypeDescr::fieldOffset(size_t index)
1153 {
1154 JSObject &fieldOffsets =
1155 getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS).toObject();
1156 JS_ASSERT(index < fieldOffsets.getDenseInitializedLength());
1157 return fieldOffsets.getDenseElement(index).toInt32();
1158 }
1159
1160 SizedTypeDescr&
1161 StructTypeDescr::fieldDescr(size_t index)
1162 {
1163 JSObject &fieldDescrs =
1164 getReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_TYPES).toObject();
1165 JS_ASSERT(index < fieldDescrs.getDenseInitializedLength());
1166 return fieldDescrs.getDenseElement(index).toObject().as<SizedTypeDescr>();
1167 }
1168
1169 /******************************************************************************
1170 * Creating the TypedObject "module"
1171 *
1172 * We create one global, `TypedObject`, which contains the following
1173 * members:
1174 *
1175 * 1. uint8, uint16, etc
1176 * 2. ArrayType
1177 * 3. StructType
1178 *
1179 * Each of these is a function and hence their prototype is
1180 * `Function.__proto__` (in terms of the JS Engine, they are not
1181 * JSFunctions but rather instances of their own respective JSClasses
1182 * which override the call and construct operations).
1183 *
1184 * Each type object also has its own `prototype` field. Therefore,
1185 * using `StructType` as an example, the basic setup is:
1186 *
1187 * StructType --__proto__--> Function.__proto__
1188 * |
1189 * prototype -- prototype --> { }
1190 * |
1191 * v
1192 * { } -----__proto__--> Function.__proto__
1193 *
1194 * When a new type object (e.g., an instance of StructType) is created,
1195 * it will look as follows:
1196 *
1197 * MyStruct -__proto__-> StructType.prototype -__proto__-> Function.__proto__
1198 * | |
1199 * | prototype
1200 * | |
1201 * | v
1202 * prototype -----__proto__----> { }
1203 * |
1204 * v
1205 * { } --__proto__-> Object.prototype
1206 *
1207 * Finally, when an instance of `MyStruct` is created, its
1208 * structure is as follows:
1209 *
1210 * object -__proto__->
1211 * MyStruct.prototype -__proto__->
1212 * StructType.prototype.prototype -__proto__->
1213 * Object.prototype
1214 */
1215
1216 // Here `T` is either `ScalarTypeDescr` or `ReferenceTypeDescr`
1217 template<typename T>
1218 static bool
1219 DefineSimpleTypeDescr(JSContext *cx,
1220 Handle<GlobalObject *> global,
1221 HandleObject module,
1222 typename T::Type type,
1223 HandlePropertyName className)
1224 {
1225 RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
1226 if (!objProto)
1227 return false;
1228
1229 RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx));
1230 if (!funcProto)
1231 return false;
1232
1233 Rooted<T*> descr(cx);
1234 descr = NewObjectWithProto<T>(cx, funcProto, global, TenuredObject);
1235 if (!descr)
1236 return false;
1237
1238 descr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(T::Kind));
1239 descr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(className));
1240 descr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(T::alignment(type)));
1241 descr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(T::size(type)));
1242 descr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(T::Opaque));
1243 descr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(type));
1244
1245 if (!CreateUserSizeAndAlignmentProperties(cx, descr))
1246 return false;
1247
1248 if (!JS_DefineFunctions(cx, descr, T::typeObjectMethods))
1249 return false;
1250
1251 // Create the typed prototype for the scalar type. This winds up
1252 // not being user accessible, but we still create one for consistency.
1253 Rooted<TypedProto*> proto(cx);
1254 proto = NewObjectWithProto<TypedProto>(cx, objProto, nullptr, TenuredObject);
1255 if (!proto)
1256 return nullptr;
1257 proto->initTypeDescrSlot(*descr);
1258 descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*proto));
1259
1260 RootedValue descrValue(cx, ObjectValue(*descr));
1261 if (!JSObject::defineProperty(cx, module, className,
1262 descrValue, nullptr, nullptr, 0))
1263 {
1264 return false;
1265 }
1266
1267 return true;
1268 }
1269
1270 ///////////////////////////////////////////////////////////////////////////
1271
1272 template<typename T>
1273 static JSObject *
1274 DefineMetaTypeDescr(JSContext *cx,
1275 Handle<GlobalObject*> global,
1276 HandleObject module,
1277 TypedObjectModuleObject::Slot protoSlot)
1278 {
1279 RootedAtom className(cx, Atomize(cx, T::class_.name,
1280 strlen(T::class_.name)));
1281 if (!className)
1282 return nullptr;
1283
1284 RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx));
1285 if (!funcProto)
1286 return nullptr;
1287
1288 // Create ctor.prototype, which inherits from Function.__proto__
1289
1290 RootedObject proto(cx, NewObjectWithProto<JSObject>(cx, funcProto, global,
1291 SingletonObject));
1292 if (!proto)
1293 return nullptr;
1294
1295 // Create ctor.prototype.prototype, which inherits from Object.__proto__
1296
1297 RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
1298 if (!objProto)
1299 return nullptr;
1300 RootedObject protoProto(cx);
1301 protoProto = NewObjectWithProto<JSObject>(cx, objProto,
1302 global, SingletonObject);
1303 if (!protoProto)
1304 return nullptr;
1305
1306 RootedValue protoProtoValue(cx, ObjectValue(*protoProto));
1307 if (!JSObject::defineProperty(cx, proto, cx->names().prototype,
1308 protoProtoValue,
1309 nullptr, nullptr,
1310 JSPROP_READONLY | JSPROP_PERMANENT))
1311 return nullptr;
1312
1313 // Create ctor itself
1314
1315 const int constructorLength = 2;
1316 RootedFunction ctor(cx);
1317 ctor = global->createConstructor(cx, T::construct, className, constructorLength);
1318 if (!ctor ||
1319 !LinkConstructorAndPrototype(cx, ctor, proto) ||
1320 !DefinePropertiesAndBrand(cx, proto,
1321 T::typeObjectProperties,
1322 T::typeObjectMethods) ||
1323 !DefinePropertiesAndBrand(cx, protoProto,
1324 T::typedObjectProperties,
1325 T::typedObjectMethods))
1326 {
1327 return nullptr;
1328 }
1329
1330 module->initReservedSlot(protoSlot, ObjectValue(*proto));
1331
1332 return ctor;
1333 }
1334
1335 /* The initialization strategy for TypedObjects is mildly unusual
1336 * compared to other classes. Because all of the types are members
1337 * of a single global, `TypedObject`, we basically make the
1338 * initializer for the `TypedObject` class populate the
1339 * `TypedObject` global (which is referred to as "module" herein).
1340 */
1341 bool
1342 GlobalObject::initTypedObjectModule(JSContext *cx, Handle<GlobalObject*> global)
1343 {
1344 RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
1345 if (!objProto)
1346 return false;
1347
1348 Rooted<TypedObjectModuleObject*> module(cx);
1349 module = NewObjectWithProto<TypedObjectModuleObject>(cx, objProto, global);
1350 if (!module)
1351 return false;
1352
1353 if (!JS_DefineFunctions(cx, module, TypedObjectMethods))
1354 return false;
1355
1356 // uint8, uint16, any, etc
1357
1358 #define BINARYDATA_SCALAR_DEFINE(constant_, type_, name_) \
1359 if (!DefineSimpleTypeDescr<ScalarTypeDescr>(cx, global, module, constant_, \
1360 cx->names().name_)) \
1361 return nullptr;
1362 JS_FOR_EACH_SCALAR_TYPE_REPR(BINARYDATA_SCALAR_DEFINE)
1363 #undef BINARYDATA_SCALAR_DEFINE
1364
1365 #define BINARYDATA_REFERENCE_DEFINE(constant_, type_, name_) \
1366 if (!DefineSimpleTypeDescr<ReferenceTypeDescr>(cx, global, module, constant_, \
1367 cx->names().name_)) \
1368 return nullptr;
1369 JS_FOR_EACH_REFERENCE_TYPE_REPR(BINARYDATA_REFERENCE_DEFINE)
1370 #undef BINARYDATA_REFERENCE_DEFINE
1371
1372 // ArrayType.
1373
1374 RootedObject arrayType(cx);
1375 arrayType = DefineMetaTypeDescr<ArrayMetaTypeDescr>(
1376 cx, global, module, TypedObjectModuleObject::ArrayTypePrototype);
1377 if (!arrayType)
1378 return nullptr;
1379
1380 RootedValue arrayTypeValue(cx, ObjectValue(*arrayType));
1381 if (!JSObject::defineProperty(cx, module, cx->names().ArrayType,
1382 arrayTypeValue,
1383 nullptr, nullptr,
1384 JSPROP_READONLY | JSPROP_PERMANENT))
1385 return nullptr;
1386
1387 // StructType.
1388
1389 RootedObject structType(cx);
1390 structType = DefineMetaTypeDescr<StructMetaTypeDescr>(
1391 cx, global, module, TypedObjectModuleObject::StructTypePrototype);
1392 if (!structType)
1393 return nullptr;
1394
1395 RootedValue structTypeValue(cx, ObjectValue(*structType));
1396 if (!JSObject::defineProperty(cx, module, cx->names().StructType,
1397 structTypeValue,
1398 nullptr, nullptr,
1399 JSPROP_READONLY | JSPROP_PERMANENT))
1400 return nullptr;
1401
1402 // Everything is setup, install module on the global object:
1403 RootedValue moduleValue(cx, ObjectValue(*module));
1404 global->setConstructor(JSProto_TypedObject, moduleValue);
1405 if (!JSObject::defineProperty(cx, global, cx->names().TypedObject,
1406 moduleValue,
1407 nullptr, nullptr,
1408 0))
1409 {
1410 return nullptr;
1411 }
1412
1413 return module;
1414 }
1415
1416 JSObject *
1417 js_InitTypedObjectModuleObject(JSContext *cx, HandleObject obj)
1418 {
1419 JS_ASSERT(obj->is<GlobalObject>());
1420 Rooted<GlobalObject *> global(cx, &obj->as<GlobalObject>());
1421 return global->getOrCreateTypedObjectModule(cx);
1422 }
1423
1424 JSObject *
1425 js_InitTypedObjectDummy(JSContext *cx, HandleObject obj)
1426 {
1427 /*
1428 * This function is entered into the jsprototypes.h table
1429 * as the initializer for `TypedObject`. It should not
1430 * be executed via the `standard_class_atoms` mechanism.
1431 */
1432
1433 MOZ_ASSUME_UNREACHABLE("shouldn't be initializing TypedObject via the JSProtoKey initializer mechanism");
1434 }
1435
1436 /******************************************************************************
1437 * Typed objects
1438 */
1439
1440 /*static*/ TypedObject *
1441 TypedObject::createUnattached(JSContext *cx,
1442 HandleTypeDescr descr,
1443 int32_t length)
1444 {
1445 if (descr->opaque())
1446 return createUnattachedWithClass(cx, &OpaqueTypedObject::class_, descr, length);
1447 else
1448 return createUnattachedWithClass(cx, &TransparentTypedObject::class_, descr, length);
1449 }
1450
1451
1452 /*static*/ TypedObject *
1453 TypedObject::createUnattachedWithClass(JSContext *cx,
1454 const Class *clasp,
1455 HandleTypeDescr type,
1456 int32_t length)
1457 {
1458 JS_ASSERT(clasp == &TransparentTypedObject::class_ ||
1459 clasp == &OpaqueTypedObject::class_);
1460 JS_ASSERT(JSCLASS_RESERVED_SLOTS(clasp) == JS_TYPEDOBJ_SLOTS);
1461 JS_ASSERT(clasp->hasPrivate());
1462
1463 RootedObject proto(cx);
1464 if (type->is<SimpleTypeDescr>()) {
1465 // FIXME Bug 929651 -- What prototype to use?
1466 proto = type->global().getOrCreateObjectPrototype(cx);
1467 } else {
1468 RootedValue protoVal(cx);
1469 if (!JSObject::getProperty(cx, type, type,
1470 cx->names().prototype, &protoVal))
1471 {
1472 return nullptr;
1473 }
1474 proto = &protoVal.toObject();
1475 }
1476
1477 RootedObject obj(cx, NewObjectWithClassProto(cx, clasp, &*proto, nullptr));
1478 if (!obj)
1479 return nullptr;
1480
1481 obj->setPrivate(nullptr);
1482 obj->initReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(0));
1483 obj->initReservedSlot(JS_TYPEDOBJ_SLOT_BYTELENGTH, Int32Value(0));
1484 obj->initReservedSlot(JS_TYPEDOBJ_SLOT_OWNER, NullValue());
1485 obj->initReservedSlot(JS_TYPEDOBJ_SLOT_NEXT_VIEW, PrivateValue(nullptr));
1486 obj->initReservedSlot(JS_TYPEDOBJ_SLOT_LENGTH, Int32Value(length));
1487 obj->initReservedSlot(JS_TYPEDOBJ_SLOT_TYPE_DESCR, ObjectValue(*type));
1488
1489 // Tag the type object for this instance with the type
1490 // representation, if that has not been done already.
1491 if (!type->is<SimpleTypeDescr>()) { // FIXME Bug 929651
1492 RootedTypeObject typeObj(cx, obj->getType(cx));
1493 if (typeObj) {
1494 if (!typeObj->addTypedObjectAddendum(cx, type))
1495 return nullptr;
1496 }
1497 }
1498
1499 return static_cast<TypedObject*>(&*obj);
1500 }
1501
1502 void
1503 TypedObject::attach(ArrayBufferObject &buffer, int32_t offset)
1504 {
1505 JS_ASSERT(offset >= 0);
1506 JS_ASSERT((size_t) (offset + size()) <= buffer.byteLength());
1507
1508 buffer.addView(this);
1509 InitArrayBufferViewDataPointer(this, &buffer, offset);
1510 setReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(offset));
1511 setReservedSlot(JS_TYPEDOBJ_SLOT_OWNER, ObjectValue(buffer));
1512 }
1513
1514 void
1515 TypedObject::attach(TypedObject &typedObj, int32_t offset)
1516 {
1517 JS_ASSERT(!typedObj.owner().isNeutered());
1518 JS_ASSERT(typedObj.typedMem() != NULL);
1519
1520 attach(typedObj.owner(), typedObj.offset() + offset);
1521 }
1522
1523 // Returns a suitable JS_TYPEDOBJ_SLOT_LENGTH value for an instance of
1524 // the type `type`. `type` must not be an unsized array.
1525 static int32_t
1526 TypedObjLengthFromType(TypeDescr &descr)
1527 {
1528 switch (descr.kind()) {
1529 case TypeDescr::Scalar:
1530 case TypeDescr::Reference:
1531 case TypeDescr::Struct:
1532 case TypeDescr::X4:
1533 return 0;
1534
1535 case TypeDescr::SizedArray:
1536 return descr.as<SizedArrayTypeDescr>().length();
1537
1538 case TypeDescr::UnsizedArray:
1539 MOZ_ASSUME_UNREACHABLE("TypedObjLengthFromType() invoked on unsized type");
1540 }
1541 MOZ_ASSUME_UNREACHABLE("Invalid kind");
1542 }
1543
1544 /*static*/ TypedObject *
1545 TypedObject::createDerived(JSContext *cx, HandleSizedTypeDescr type,
1546 HandleTypedObject typedObj, int32_t offset)
1547 {
1548 JS_ASSERT(!typedObj->owner().isNeutered());
1549 JS_ASSERT(typedObj->typedMem() != NULL);
1550 JS_ASSERT(offset <= typedObj->size());
1551 JS_ASSERT(offset + type->size() <= typedObj->size());
1552
1553 int32_t length = TypedObjLengthFromType(*type);
1554
1555 const js::Class *clasp = typedObj->getClass();
1556 Rooted<TypedObject*> obj(cx);
1557 obj = createUnattachedWithClass(cx, clasp, type, length);
1558 if (!obj)
1559 return nullptr;
1560
1561 obj->attach(*typedObj, offset);
1562 return obj;
1563 }
1564
1565 /*static*/ TypedObject *
1566 TypedObject::createZeroed(JSContext *cx,
1567 HandleTypeDescr descr,
1568 int32_t length)
1569 {
1570 // Create unattached wrapper object.
1571 Rooted<TypedObject*> obj(cx, createUnattached(cx, descr, length));
1572 if (!obj)
1573 return nullptr;
1574
1575 // Allocate and initialize the memory for this instance.
1576 // Also initialize the JS_TYPEDOBJ_SLOT_LENGTH slot.
1577 switch (descr->kind()) {
1578 case TypeDescr::Scalar:
1579 case TypeDescr::Reference:
1580 case TypeDescr::Struct:
1581 case TypeDescr::X4:
1582 case TypeDescr::SizedArray:
1583 {
1584 size_t totalSize = descr->as<SizedTypeDescr>().size();
1585 Rooted<ArrayBufferObject*> buffer(cx);
1586 buffer = ArrayBufferObject::create(cx, totalSize);
1587 if (!buffer)
1588 return nullptr;
1589 descr->as<SizedTypeDescr>().initInstances(cx->runtime(), buffer->dataPointer(), 1);
1590 obj->attach(*buffer, 0);
1591 return obj;
1592 }
1593
1594 case TypeDescr::UnsizedArray:
1595 {
1596 Rooted<SizedTypeDescr*> elementTypeRepr(cx);
1597 elementTypeRepr = &descr->as<UnsizedArrayTypeDescr>().elementType();
1598
1599 CheckedInt32 totalSize = CheckedInt32(elementTypeRepr->size()) * length;
1600 if (!totalSize.isValid()) {
1601 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
1602 JSMSG_TYPEDOBJECT_TOO_BIG);
1603 return nullptr;
1604 }
1605
1606 Rooted<ArrayBufferObject*> buffer(cx);
1607 buffer = ArrayBufferObject::create(cx, totalSize.value());
1608 if (!buffer)
1609 return nullptr;
1610
1611 if (length)
1612 elementTypeRepr->initInstances(cx->runtime(), buffer->dataPointer(), length);
1613 obj->attach(*buffer, 0);
1614 return obj;
1615 }
1616 }
1617
1618 MOZ_ASSUME_UNREACHABLE("Bad TypeRepresentation Kind");
1619 }
1620
1621 static bool
1622 ReportTypedObjTypeError(JSContext *cx,
1623 const unsigned errorNumber,
1624 HandleTypedObject obj)
1625 {
1626 // Serialize type string of obj
1627 char *typeReprStr = JS_EncodeString(cx, &obj->typeDescr().stringRepr());
1628 if (!typeReprStr)
1629 return false;
1630
1631 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
1632 errorNumber, typeReprStr);
1633
1634 JS_free(cx, (void *) typeReprStr);
1635 return false;
1636 }
1637
1638 /*static*/ void
1639 TypedObject::obj_trace(JSTracer *trace, JSObject *object)
1640 {
1641 gc::MarkSlot(trace, &object->getReservedSlotRef(JS_TYPEDOBJ_SLOT_TYPE_DESCR),
1642 "TypedObjectTypeDescr");
1643
1644 ArrayBufferViewObject::trace(trace, object);
1645
1646 JS_ASSERT(object->is<TypedObject>());
1647 TypedObject &typedObj = object->as<TypedObject>();
1648 TypeDescr &descr = typedObj.typeDescr();
1649 if (descr.opaque()) {
1650 uint8_t *mem = typedObj.typedMem();
1651 if (!mem)
1652 return; // partially constructed
1653
1654 if (typedObj.owner().isNeutered())
1655 return;
1656
1657 switch (descr.kind()) {
1658 case TypeDescr::Scalar:
1659 case TypeDescr::Reference:
1660 case TypeDescr::Struct:
1661 case TypeDescr::SizedArray:
1662 case TypeDescr::X4:
1663 descr.as<SizedTypeDescr>().traceInstances(trace, mem, 1);
1664 break;
1665
1666 case TypeDescr::UnsizedArray:
1667 descr.as<UnsizedArrayTypeDescr>().elementType().traceInstances(trace, mem, typedObj.length());
1668 break;
1669 }
1670 }
1671 }
1672
1673 bool
1674 TypedObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId id,
1675 MutableHandleObject objp, MutableHandleShape propp)
1676 {
1677 JS_ASSERT(obj->is<TypedObject>());
1678
1679 Rooted<TypeDescr*> descr(cx, &obj->as<TypedObject>().typeDescr());
1680 switch (descr->kind()) {
1681 case TypeDescr::Scalar:
1682 case TypeDescr::Reference:
1683 case TypeDescr::X4:
1684 break;
1685
1686 case TypeDescr::SizedArray:
1687 case TypeDescr::UnsizedArray:
1688 {
1689 uint32_t index;
1690 if (js_IdIsIndex(id, &index))
1691 return obj_lookupElement(cx, obj, index, objp, propp);
1692
1693 if (JSID_IS_ATOM(id, cx->names().length)) {
1694 MarkNonNativePropertyFound(propp);
1695 objp.set(obj);
1696 return true;
1697 }
1698 break;
1699 }
1700
1701 case TypeDescr::Struct:
1702 {
1703 StructTypeDescr &structDescr = descr->as<StructTypeDescr>();
1704 size_t index;
1705 if (structDescr.fieldIndex(id, &index)) {
1706 MarkNonNativePropertyFound(propp);
1707 objp.set(obj);
1708 return true;
1709 }
1710 break;
1711 }
1712 }
1713
1714 RootedObject proto(cx, obj->getProto());
1715 if (!proto) {
1716 objp.set(nullptr);
1717 propp.set(nullptr);
1718 return true;
1719 }
1720
1721 return JSObject::lookupGeneric(cx, proto, id, objp, propp);
1722 }
1723
1724 bool
1725 TypedObject::obj_lookupProperty(JSContext *cx,
1726 HandleObject obj,
1727 HandlePropertyName name,
1728 MutableHandleObject objp,
1729 MutableHandleShape propp)
1730 {
1731 RootedId id(cx, NameToId(name));
1732 return obj_lookupGeneric(cx, obj, id, objp, propp);
1733 }
1734
1735 bool
1736 TypedObject::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
1737 MutableHandleObject objp, MutableHandleShape propp)
1738 {
1739 JS_ASSERT(obj->is<TypedObject>());
1740 MarkNonNativePropertyFound(propp);
1741 objp.set(obj);
1742 return true;
1743 }
1744
1745 static bool
1746 ReportPropertyError(JSContext *cx,
1747 const unsigned errorNumber,
1748 HandleId id)
1749 {
1750 RootedString str(cx, IdToString(cx, id));
1751 if (!str)
1752 return false;
1753
1754 char *propName = JS_EncodeString(cx, str);
1755 if (!propName)
1756 return false;
1757
1758 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr,
1759 errorNumber, propName);
1760
1761 JS_free(cx, propName);
1762 return false;
1763 }
1764
1765 bool
1766 TypedObject::obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId id, HandleValue v,
1767 PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
1768 {
1769 return ReportPropertyError(cx, JSMSG_UNDEFINED_PROP, id);
1770 }
1771
1772 bool
1773 TypedObject::obj_defineProperty(JSContext *cx, HandleObject obj,
1774 HandlePropertyName name, HandleValue v,
1775 PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
1776 {
1777 Rooted<jsid> id(cx, NameToId(name));
1778 return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
1779 }
1780
1781 bool
1782 TypedObject::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t index, HandleValue v,
1783 PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
1784 {
1785 AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
1786 Rooted<jsid> id(cx);
1787 if (!IndexToId(cx, index, &id))
1788 return false;
1789 return obj_defineGeneric(cx, obj, id, v, getter, setter, attrs);
1790 }
1791
1792 bool
1793 TypedObject::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject receiver,
1794 HandleId id, MutableHandleValue vp)
1795 {
1796 JS_ASSERT(obj->is<TypedObject>());
1797 Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
1798
1799 // Dispatch elements to obj_getElement:
1800 uint32_t index;
1801 if (js_IdIsIndex(id, &index))
1802 return obj_getElement(cx, obj, receiver, index, vp);
1803
1804 // Handle everything else here:
1805
1806 switch (typedObj->typeDescr().kind()) {
1807 case TypeDescr::Scalar:
1808 case TypeDescr::Reference:
1809 break;
1810
1811 case TypeDescr::X4:
1812 break;
1813
1814 case TypeDescr::SizedArray:
1815 case TypeDescr::UnsizedArray:
1816 if (JSID_IS_ATOM(id, cx->names().length)) {
1817 if (typedObj->owner().isNeutered() || !typedObj->typedMem()) { // unattached
1818 JS_ReportErrorNumber(
1819 cx, js_GetErrorMessage,
1820 nullptr, JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED);
1821 return false;
1822 }
1823
1824 vp.setInt32(typedObj->length());
1825 return true;
1826 }
1827 break;
1828
1829 case TypeDescr::Struct: {
1830 Rooted<StructTypeDescr*> descr(cx, &typedObj->typeDescr().as<StructTypeDescr>());
1831
1832 size_t fieldIndex;
1833 if (!descr->fieldIndex(id, &fieldIndex))
1834 break;
1835
1836 size_t offset = descr->fieldOffset(fieldIndex);
1837 Rooted<SizedTypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
1838 return Reify(cx, fieldType, typedObj, offset, vp);
1839 }
1840 }
1841
1842 RootedObject proto(cx, obj->getProto());
1843 if (!proto) {
1844 vp.setUndefined();
1845 return true;
1846 }
1847
1848 return JSObject::getGeneric(cx, proto, receiver, id, vp);
1849 }
1850
1851 bool
1852 TypedObject::obj_getProperty(JSContext *cx, HandleObject obj, HandleObject receiver,
1853 HandlePropertyName name, MutableHandleValue vp)
1854 {
1855 RootedId id(cx, NameToId(name));
1856 return obj_getGeneric(cx, obj, receiver, id, vp);
1857 }
1858
1859 bool
1860 TypedObject::obj_getElement(JSContext *cx, HandleObject obj, HandleObject receiver,
1861 uint32_t index, MutableHandleValue vp)
1862 {
1863 JS_ASSERT(obj->is<TypedObject>());
1864 Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
1865 Rooted<TypeDescr *> descr(cx, &typedObj->typeDescr());
1866
1867 switch (descr->kind()) {
1868 case TypeDescr::Scalar:
1869 case TypeDescr::Reference:
1870 case TypeDescr::X4:
1871 case TypeDescr::Struct:
1872 break;
1873
1874 case TypeDescr::SizedArray:
1875 return obj_getArrayElement<SizedArrayTypeDescr>(cx, typedObj, descr,
1876 index, vp);
1877
1878 case TypeDescr::UnsizedArray:
1879 return obj_getArrayElement<UnsizedArrayTypeDescr>(cx, typedObj, descr,
1880 index, vp);
1881 }
1882
1883 RootedObject proto(cx, obj->getProto());
1884 if (!proto) {
1885 vp.setUndefined();
1886 return true;
1887 }
1888
1889 return JSObject::getElement(cx, proto, receiver, index, vp);
1890 }
1891
1892 template<class T>
1893 /*static*/ bool
1894 TypedObject::obj_getArrayElement(JSContext *cx,
1895 Handle<TypedObject*> typedObj,
1896 Handle<TypeDescr*> typeDescr,
1897 uint32_t index,
1898 MutableHandleValue vp)
1899 {
1900 JS_ASSERT(typeDescr->is<T>());
1901
1902 if (index >= (size_t) typedObj->length()) {
1903 vp.setUndefined();
1904 return true;
1905 }
1906
1907 Rooted<SizedTypeDescr*> elementType(cx, &typeDescr->as<T>().elementType());
1908 size_t offset = elementType->size() * index;
1909 return Reify(cx, elementType, typedObj, offset, vp);
1910 }
1911
1912 bool
1913 TypedObject::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
1914 MutableHandleValue vp, bool strict)
1915 {
1916 JS_ASSERT(obj->is<TypedObject>());
1917 Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
1918
1919 uint32_t index;
1920 if (js_IdIsIndex(id, &index))
1921 return obj_setElement(cx, obj, index, vp, strict);
1922
1923 switch (typedObj->typeDescr().kind()) {
1924 case ScalarTypeDescr::Scalar:
1925 case TypeDescr::Reference:
1926 break;
1927
1928 case ScalarTypeDescr::X4:
1929 break;
1930
1931 case ScalarTypeDescr::SizedArray:
1932 case ScalarTypeDescr::UnsizedArray:
1933 if (JSID_IS_ATOM(id, cx->names().length)) {
1934 JS_ReportErrorNumber(cx, js_GetErrorMessage,
1935 nullptr, JSMSG_CANT_REDEFINE_ARRAY_LENGTH);
1936 return false;
1937 }
1938 break;
1939
1940 case ScalarTypeDescr::Struct: {
1941 Rooted<StructTypeDescr*> descr(cx, &typedObj->typeDescr().as<StructTypeDescr>());
1942
1943 size_t fieldIndex;
1944 if (!descr->fieldIndex(id, &fieldIndex))
1945 break;
1946
1947 size_t offset = descr->fieldOffset(fieldIndex);
1948 Rooted<SizedTypeDescr*> fieldType(cx, &descr->fieldDescr(fieldIndex));
1949 return ConvertAndCopyTo(cx, fieldType, typedObj, offset, vp);
1950 }
1951 }
1952
1953 return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj);
1954 }
1955
1956 bool
1957 TypedObject::obj_setProperty(JSContext *cx, HandleObject obj,
1958 HandlePropertyName name, MutableHandleValue vp,
1959 bool strict)
1960 {
1961 RootedId id(cx, NameToId(name));
1962 return obj_setGeneric(cx, obj, id, vp, strict);
1963 }
1964
1965 bool
1966 TypedObject::obj_setElement(JSContext *cx, HandleObject obj, uint32_t index,
1967 MutableHandleValue vp, bool strict)
1968 {
1969 JS_ASSERT(obj->is<TypedObject>());
1970 Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
1971 Rooted<TypeDescr *> descr(cx, &typedObj->typeDescr());
1972
1973 switch (descr->kind()) {
1974 case TypeDescr::Scalar:
1975 case TypeDescr::Reference:
1976 case TypeDescr::X4:
1977 case TypeDescr::Struct:
1978 break;
1979
1980 case TypeDescr::SizedArray:
1981 return obj_setArrayElement<SizedArrayTypeDescr>(cx, typedObj, descr, index, vp);
1982
1983 case TypeDescr::UnsizedArray:
1984 return obj_setArrayElement<UnsizedArrayTypeDescr>(cx, typedObj, descr, index, vp);
1985 }
1986
1987 return ReportTypedObjTypeError(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, typedObj);
1988 }
1989
1990 template<class T>
1991 /*static*/ bool
1992 TypedObject::obj_setArrayElement(JSContext *cx,
1993 Handle<TypedObject*> typedObj,
1994 Handle<TypeDescr*> descr,
1995 uint32_t index,
1996 MutableHandleValue vp)
1997 {
1998 JS_ASSERT(descr->is<T>());
1999
2000 if (index >= (size_t) typedObj->length()) {
2001 JS_ReportErrorNumber(cx, js_GetErrorMessage,
2002 nullptr, JSMSG_TYPEDOBJECT_BINARYARRAY_BAD_INDEX);
2003 return false;
2004 }
2005
2006 Rooted<SizedTypeDescr*> elementType(cx);
2007 elementType = &descr->as<T>().elementType();
2008 size_t offset = elementType->size() * index;
2009 return ConvertAndCopyTo(cx, elementType, typedObj, offset, vp);
2010 }
2011
2012 bool
2013 TypedObject::obj_getGenericAttributes(JSContext *cx, HandleObject obj,
2014 HandleId id, unsigned *attrsp)
2015 {
2016 uint32_t index;
2017 Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
2018 switch (typedObj->typeDescr().kind()) {
2019 case TypeDescr::Scalar:
2020 case TypeDescr::Reference:
2021 break;
2022
2023 case TypeDescr::X4:
2024 break;
2025
2026 case TypeDescr::SizedArray:
2027 case TypeDescr::UnsizedArray:
2028 if (js_IdIsIndex(id, &index)) {
2029 *attrsp = JSPROP_ENUMERATE | JSPROP_PERMANENT;
2030 return true;
2031 }
2032 if (JSID_IS_ATOM(id, cx->names().length)) {
2033 *attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
2034 return true;
2035 }
2036 break;
2037
2038 case TypeDescr::Struct:
2039 size_t index;
2040 if (typedObj->typeDescr().as<StructTypeDescr>().fieldIndex(id, &index)) {
2041 *attrsp = JSPROP_ENUMERATE | JSPROP_PERMANENT;
2042 return true;
2043 }
2044 break;
2045 }
2046
2047 RootedObject proto(cx, obj->getProto());
2048 if (!proto) {
2049 *attrsp = 0;
2050 return true;
2051 }
2052
2053 return JSObject::getGenericAttributes(cx, proto, id, attrsp);
2054 }
2055
2056 static bool
2057 IsOwnId(JSContext *cx, HandleObject obj, HandleId id)
2058 {
2059 uint32_t index;
2060 Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
2061 switch (typedObj->typeDescr().kind()) {
2062 case TypeDescr::Scalar:
2063 case TypeDescr::Reference:
2064 case TypeDescr::X4:
2065 return false;
2066
2067 case TypeDescr::SizedArray:
2068 case TypeDescr::UnsizedArray:
2069 return js_IdIsIndex(id, &index) || JSID_IS_ATOM(id, cx->names().length);
2070
2071 case TypeDescr::Struct:
2072 size_t index;
2073 if (typedObj->typeDescr().as<StructTypeDescr>().fieldIndex(id, &index))
2074 return true;
2075 }
2076
2077 return false;
2078 }
2079
2080 bool
2081 TypedObject::obj_setGenericAttributes(JSContext *cx, HandleObject obj,
2082 HandleId id, unsigned *attrsp)
2083 {
2084 if (IsOwnId(cx, obj, id))
2085 return ReportPropertyError(cx, JSMSG_CANT_REDEFINE_PROP, id);
2086
2087 RootedObject proto(cx, obj->getProto());
2088 if (!proto) {
2089 *attrsp = 0;
2090 return true;
2091 }
2092
2093 return JSObject::setGenericAttributes(cx, proto, id, attrsp);
2094 }
2095
2096 bool
2097 TypedObject::obj_deleteProperty(JSContext *cx, HandleObject obj,
2098 HandlePropertyName name, bool *succeeded)
2099 {
2100 Rooted<jsid> id(cx, NameToId(name));
2101 if (IsOwnId(cx, obj, id))
2102 return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
2103
2104 RootedObject proto(cx, obj->getProto());
2105 if (!proto) {
2106 *succeeded = false;
2107 return true;
2108 }
2109
2110 return JSObject::deleteProperty(cx, proto, name, succeeded);
2111 }
2112
2113 bool
2114 TypedObject::obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index,
2115 bool *succeeded)
2116 {
2117 RootedId id(cx);
2118 if (!IndexToId(cx, index, &id))
2119 return false;
2120
2121 if (IsOwnId(cx, obj, id))
2122 return ReportPropertyError(cx, JSMSG_CANT_DELETE, id);
2123
2124 RootedObject proto(cx, obj->getProto());
2125 if (!proto) {
2126 *succeeded = false;
2127 return true;
2128 }
2129
2130 return JSObject::deleteElement(cx, proto, index, succeeded);
2131 }
2132
2133 bool
2134 TypedObject::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op,
2135 MutableHandleValue statep, MutableHandleId idp)
2136 {
2137 int32_t index;
2138
2139 JS_ASSERT(obj->is<TypedObject>());
2140 Rooted<TypedObject *> typedObj(cx, &obj->as<TypedObject>());
2141 Rooted<TypeDescr *> descr(cx, &typedObj->typeDescr());
2142 switch (descr->kind()) {
2143 case TypeDescr::Scalar:
2144 case TypeDescr::Reference:
2145 case TypeDescr::X4:
2146 switch (enum_op) {
2147 case JSENUMERATE_INIT_ALL:
2148 case JSENUMERATE_INIT:
2149 statep.setInt32(0);
2150 idp.set(INT_TO_JSID(0));
2151
2152 case JSENUMERATE_NEXT:
2153 case JSENUMERATE_DESTROY:
2154 statep.setNull();
2155 break;
2156 }
2157 break;
2158
2159 case TypeDescr::SizedArray:
2160 case TypeDescr::UnsizedArray:
2161 switch (enum_op) {
2162 case JSENUMERATE_INIT_ALL:
2163 case JSENUMERATE_INIT:
2164 statep.setInt32(0);
2165 idp.set(INT_TO_JSID(typedObj->length()));
2166 break;
2167
2168 case JSENUMERATE_NEXT:
2169 index = static_cast<int32_t>(statep.toInt32());
2170
2171 if (index < typedObj->length()) {
2172 idp.set(INT_TO_JSID(index));
2173 statep.setInt32(index + 1);
2174 } else {
2175 JS_ASSERT(index == typedObj->length());
2176 statep.setNull();
2177 }
2178
2179 break;
2180
2181 case JSENUMERATE_DESTROY:
2182 statep.setNull();
2183 break;
2184 }
2185 break;
2186
2187 case TypeDescr::Struct:
2188 switch (enum_op) {
2189 case JSENUMERATE_INIT_ALL:
2190 case JSENUMERATE_INIT:
2191 statep.setInt32(0);
2192 idp.set(INT_TO_JSID(descr->as<StructTypeDescr>().fieldCount()));
2193 break;
2194
2195 case JSENUMERATE_NEXT:
2196 index = static_cast<uint32_t>(statep.toInt32());
2197
2198 if ((size_t) index < descr->as<StructTypeDescr>().fieldCount()) {
2199 idp.set(AtomToId(&descr->as<StructTypeDescr>().fieldName(index)));
2200 statep.setInt32(index + 1);
2201 } else {
2202 statep.setNull();
2203 }
2204
2205 break;
2206
2207 case JSENUMERATE_DESTROY:
2208 statep.setNull();
2209 break;
2210 }
2211 break;
2212 }
2213
2214 return true;
2215 }
2216
2217 /* static */ size_t
2218 TypedObject::offsetOfOwnerSlot()
2219 {
2220 return JSObject::getFixedSlotOffset(JS_TYPEDOBJ_SLOT_OWNER);
2221 }
2222
2223 /* static */ size_t
2224 TypedObject::offsetOfDataSlot()
2225 {
2226 // the offset of 7 is based on the alloc kind
2227 return JSObject::getPrivateDataOffset(JS_TYPEDOBJ_SLOT_DATA);
2228 }
2229
2230 /* static */ size_t
2231 TypedObject::offsetOfByteOffsetSlot()
2232 {
2233 return JSObject::getFixedSlotOffset(JS_TYPEDOBJ_SLOT_BYTEOFFSET);
2234 }
2235
2236 void
2237 TypedObject::neuter(void *newData)
2238 {
2239 setSlot(JS_TYPEDOBJ_SLOT_LENGTH, Int32Value(0));
2240 setSlot(JS_TYPEDOBJ_SLOT_BYTELENGTH, Int32Value(0));
2241 setSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(0));
2242 setPrivate(newData);
2243 }
2244
2245 /******************************************************************************
2246 * Typed Objects
2247 */
2248
2249 const Class TransparentTypedObject::class_ = {
2250 "TypedObject",
2251 Class::NON_NATIVE |
2252 JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEDOBJ_SLOTS) |
2253 JSCLASS_HAS_PRIVATE |
2254 JSCLASS_IMPLEMENTS_BARRIERS,
2255 JS_PropertyStub,
2256 JS_DeletePropertyStub,
2257 JS_PropertyStub,
2258 JS_StrictPropertyStub,
2259 JS_EnumerateStub,
2260 JS_ResolveStub,
2261 JS_ConvertStub,
2262 nullptr, /* finalize */
2263 nullptr, /* call */
2264 nullptr, /* hasInstance */
2265 nullptr, /* construct */
2266 TypedObject::obj_trace,
2267 JS_NULL_CLASS_SPEC,
2268 JS_NULL_CLASS_EXT,
2269 {
2270 TypedObject::obj_lookupGeneric,
2271 TypedObject::obj_lookupProperty,
2272 TypedObject::obj_lookupElement,
2273 TypedObject::obj_defineGeneric,
2274 TypedObject::obj_defineProperty,
2275 TypedObject::obj_defineElement,
2276 TypedObject::obj_getGeneric,
2277 TypedObject::obj_getProperty,
2278 TypedObject::obj_getElement,
2279 TypedObject::obj_setGeneric,
2280 TypedObject::obj_setProperty,
2281 TypedObject::obj_setElement,
2282 TypedObject::obj_getGenericAttributes,
2283 TypedObject::obj_setGenericAttributes,
2284 TypedObject::obj_deleteProperty,
2285 TypedObject::obj_deleteElement,
2286 nullptr, nullptr, // watch/unwatch
2287 nullptr, /* slice */
2288 TypedObject::obj_enumerate,
2289 nullptr, /* thisObject */
2290 }
2291 };
2292
2293 static int32_t
2294 LengthForType(TypeDescr &descr)
2295 {
2296 switch (descr.kind()) {
2297 case TypeDescr::Scalar:
2298 case TypeDescr::Reference:
2299 case TypeDescr::Struct:
2300 case TypeDescr::X4:
2301 return 0;
2302
2303 case TypeDescr::SizedArray:
2304 return descr.as<SizedArrayTypeDescr>().length();
2305
2306 case TypeDescr::UnsizedArray:
2307 return 0;
2308 }
2309
2310 MOZ_ASSUME_UNREACHABLE("Invalid kind");
2311 }
2312
2313 static bool
2314 CheckOffset(int32_t offset,
2315 int32_t size,
2316 int32_t alignment,
2317 int32_t bufferLength)
2318 {
2319 JS_ASSERT(size >= 0);
2320 JS_ASSERT(alignment >= 0);
2321
2322 // No negative offsets.
2323 if (offset < 0)
2324 return false;
2325
2326 // Offset (plus size) must be fully contained within the buffer.
2327 if (offset > bufferLength)
2328 return false;
2329 if (offset + size < offset)
2330 return false;
2331 if (offset + size > bufferLength)
2332 return false;
2333
2334 // Offset must be aligned.
2335 if ((offset % alignment) != 0)
2336 return false;
2337
2338 return true;
2339 }
2340
2341 /*static*/ bool
2342 TypedObject::constructSized(JSContext *cx, unsigned int argc, Value *vp)
2343 {
2344 CallArgs args = CallArgsFromVp(argc, vp);
2345
2346 JS_ASSERT(args.callee().is<SizedTypeDescr>());
2347 Rooted<SizedTypeDescr*> callee(cx, &args.callee().as<SizedTypeDescr>());
2348
2349 // Typed object constructors for sized types are overloaded in
2350 // three ways, in order of precedence:
2351 //
2352 // new TypeObj()
2353 // new TypeObj(buffer, [offset])
2354 // new TypeObj(data)
2355
2356 // Zero argument constructor:
2357 if (args.length() == 0) {
2358 int32_t length = LengthForType(*callee);
2359 Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
2360 if (!obj)
2361 return false;
2362 args.rval().setObject(*obj);
2363 return true;
2364 }
2365
2366 // Buffer constructor.
2367 if (args[0].isObject() && args[0].toObject().is<ArrayBufferObject>()) {
2368 Rooted<ArrayBufferObject*> buffer(cx);
2369 buffer = &args[0].toObject().as<ArrayBufferObject>();
2370
2371 if (callee->opaque() || buffer->isNeutered()) {
2372 JS_ReportErrorNumber(cx, js_GetErrorMessage,
2373 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
2374 return false;
2375 }
2376
2377 int32_t offset;
2378 if (args.length() >= 2 && !args[1].isUndefined()) {
2379 if (!args[1].isInt32()) {
2380 JS_ReportErrorNumber(cx, js_GetErrorMessage,
2381 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
2382 return false;
2383 }
2384
2385 offset = args[1].toInt32();
2386 } else {
2387 offset = 0;
2388 }
2389
2390 if (args.length() >= 3 && !args[2].isUndefined()) {
2391 JS_ReportErrorNumber(cx, js_GetErrorMessage,
2392 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
2393 return false;
2394 }
2395
2396 if (!CheckOffset(offset, callee->size(), callee->alignment(),
2397 buffer->byteLength()))
2398 {
2399 JS_ReportErrorNumber(cx, js_GetErrorMessage,
2400 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
2401 return false;
2402 }
2403
2404 Rooted<TypedObject*> obj(cx);
2405 obj = TypedObject::createUnattached(cx, callee, LengthForType(*callee));
2406 if (!obj)
2407 return false;
2408
2409 obj->attach(*buffer, offset);
2410 args.rval().setObject(*obj);
2411 return true;
2412 }
2413
2414 // Data constructor.
2415 if (args[0].isObject()) {
2416 // Create the typed object.
2417 int32_t length = LengthForType(*callee);
2418 Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
2419 if (!obj)
2420 return false;
2421
2422 // Initialize from `arg`.
2423 if (!ConvertAndCopyTo(cx, obj, args[0]))
2424 return false;
2425 args.rval().setObject(*obj);
2426 return true;
2427 }
2428
2429 // Something bogus.
2430 JS_ReportErrorNumber(cx, js_GetErrorMessage,
2431 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
2432 return false;
2433 }
2434
2435 /*static*/ bool
2436 TypedObject::constructUnsized(JSContext *cx, unsigned int argc, Value *vp)
2437 {
2438 CallArgs args = CallArgsFromVp(argc, vp);
2439
2440 JS_ASSERT(args.callee().is<TypeDescr>());
2441 Rooted<UnsizedArrayTypeDescr*> callee(cx);
2442 callee = &args.callee().as<UnsizedArrayTypeDescr>();
2443
2444 // Typed object constructors for unsized arrays are overloaded in
2445 // four ways, in order of precedence:
2446 //
2447 // new TypeObj(buffer, [offset, [length]]) // [1]
2448 // new TypeObj(length) // [1]
2449 // new TypeObj(data)
2450 // new TypeObj()
2451 //
2452 // [1] Explicit lengths only available for unsized arrays.
2453
2454 // Zero argument constructor:
2455 if (args.length() == 0) {
2456 Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, 0));
2457 if (!obj)
2458 return false;
2459 args.rval().setObject(*obj);
2460 return true;
2461 }
2462
2463 // Length constructor.
2464 if (args[0].isInt32()) {
2465 int32_t length = args[0].toInt32();
2466 if (length < 0) {
2467 JS_ReportErrorNumber(cx, js_GetErrorMessage,
2468 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
2469 return nullptr;
2470 }
2471 Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
2472 if (!obj)
2473 return false;
2474 args.rval().setObject(*obj);
2475 return true;
2476 }
2477
2478 // Buffer constructor.
2479 if (args[0].isObject() && args[0].toObject().is<ArrayBufferObject>()) {
2480 Rooted<ArrayBufferObject*> buffer(cx);
2481 buffer = &args[0].toObject().as<ArrayBufferObject>();
2482
2483 if (callee->opaque()) {
2484 JS_ReportErrorNumber(cx, js_GetErrorMessage,
2485 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
2486 return false;
2487 }
2488
2489 int32_t offset;
2490 if (args.length() >= 2 && !args[1].isUndefined()) {
2491 if (!args[1].isInt32()) {
2492 JS_ReportErrorNumber(cx, js_GetErrorMessage,
2493 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
2494 return false;
2495 }
2496
2497 offset = args[1].toInt32();
2498 } else {
2499 offset = 0;
2500 }
2501
2502 if (!CheckOffset(offset, 0, callee->alignment(), buffer->byteLength())) {
2503 JS_ReportErrorNumber(cx, js_GetErrorMessage,
2504 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
2505 return false;
2506 }
2507
2508 int32_t elemSize = callee->elementType().size();
2509 int32_t bytesRemaining = buffer->byteLength() - offset;
2510 int32_t maximumLength = bytesRemaining / elemSize;
2511
2512 int32_t length;
2513 if (args.length() >= 3 && !args[2].isUndefined()) {
2514 if (!args[2].isInt32()) {
2515 JS_ReportErrorNumber(cx, js_GetErrorMessage,
2516 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
2517 return false;
2518 }
2519 length = args[2].toInt32();
2520
2521 if (length < 0 || length > maximumLength) {
2522 JS_ReportErrorNumber(cx, js_GetErrorMessage,
2523 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
2524 return false;
2525 }
2526 } else {
2527 if ((bytesRemaining % elemSize) != 0) {
2528 JS_ReportErrorNumber(cx, js_GetErrorMessage,
2529 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
2530 return false;
2531 }
2532
2533 length = maximumLength;
2534 }
2535
2536 if (buffer->isNeutered()) {
2537 JS_ReportErrorNumber(cx, js_GetErrorMessage,
2538 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
2539 return false;
2540 }
2541
2542 Rooted<TypedObject*> obj(cx);
2543 obj = TypedObject::createUnattached(cx, callee, length);
2544 if (!obj)
2545 return false;
2546
2547 obj->attach(args[0].toObject().as<ArrayBufferObject>(), offset);
2548 }
2549
2550 // Data constructor for unsized values
2551 if (args[0].isObject()) {
2552 // Read length out of the object.
2553 RootedObject arg(cx, &args[0].toObject());
2554 RootedValue lengthVal(cx);
2555 if (!JSObject::getProperty(cx, arg, arg, cx->names().length, &lengthVal))
2556 return false;
2557 if (!lengthVal.isInt32()) {
2558 JS_ReportErrorNumber(cx, js_GetErrorMessage,
2559 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
2560 return false;
2561 }
2562 int32_t length = lengthVal.toInt32();
2563
2564 // Check that length * elementSize does not overflow.
2565 int32_t elementSize = callee->elementType().size();
2566 int32_t byteLength;
2567 if (!SafeMul(elementSize, length, &byteLength)) {
2568 JS_ReportErrorNumber(cx, js_GetErrorMessage,
2569 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
2570 return false;
2571 }
2572
2573 // Create the unsized array.
2574 Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
2575 if (!obj)
2576 return false;
2577
2578 // Initialize from `arg`
2579 if (!ConvertAndCopyTo(cx, obj, args[0]))
2580 return false;
2581 args.rval().setObject(*obj);
2582 return true;
2583 }
2584
2585 // Something bogus.
2586 JS_ReportErrorNumber(cx, js_GetErrorMessage,
2587 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
2588 return false;
2589 }
2590
2591 /******************************************************************************
2592 * Handles
2593 */
2594
2595 const Class OpaqueTypedObject::class_ = {
2596 "Handle",
2597 Class::NON_NATIVE |
2598 JSCLASS_HAS_RESERVED_SLOTS(JS_TYPEDOBJ_SLOTS) |
2599 JSCLASS_HAS_PRIVATE |
2600 JSCLASS_IMPLEMENTS_BARRIERS,
2601 JS_PropertyStub,
2602 JS_DeletePropertyStub,
2603 JS_PropertyStub,
2604 JS_StrictPropertyStub,
2605 JS_EnumerateStub,
2606 JS_ResolveStub,
2607 JS_ConvertStub,
2608 nullptr, /* finalize */
2609 nullptr, /* call */
2610 nullptr, /* construct */
2611 nullptr, /* hasInstance */
2612 TypedObject::obj_trace,
2613 JS_NULL_CLASS_SPEC,
2614 JS_NULL_CLASS_EXT,
2615 {
2616 TypedObject::obj_lookupGeneric,
2617 TypedObject::obj_lookupProperty,
2618 TypedObject::obj_lookupElement,
2619 TypedObject::obj_defineGeneric,
2620 TypedObject::obj_defineProperty,
2621 TypedObject::obj_defineElement,
2622 TypedObject::obj_getGeneric,
2623 TypedObject::obj_getProperty,
2624 TypedObject::obj_getElement,
2625 TypedObject::obj_setGeneric,
2626 TypedObject::obj_setProperty,
2627 TypedObject::obj_setElement,
2628 TypedObject::obj_getGenericAttributes,
2629 TypedObject::obj_setGenericAttributes,
2630 TypedObject::obj_deleteProperty,
2631 TypedObject::obj_deleteElement,
2632 nullptr, nullptr, // watch/unwatch
2633 nullptr, // slice
2634 TypedObject::obj_enumerate,
2635 nullptr, /* thisObject */
2636 }
2637 };
2638
2639 const JSFunctionSpec OpaqueTypedObject::handleStaticMethods[] = {
2640 {"move", {nullptr, nullptr}, 3, 0, "HandleMove"},
2641 {"get", {nullptr, nullptr}, 1, 0, "HandleGet"},
2642 {"set", {nullptr, nullptr}, 2, 0, "HandleSet"},
2643 {"isHandle", {nullptr, nullptr}, 1, 0, "HandleTest"},
2644 JS_FS_END
2645 };
2646
2647 /******************************************************************************
2648 * Intrinsics
2649 */
2650
2651 bool
2652 js::NewOpaqueTypedObject(JSContext *cx, unsigned argc, Value *vp)
2653 {
2654 CallArgs args = CallArgsFromVp(argc, vp);
2655 JS_ASSERT(args.length() == 1);
2656 JS_ASSERT(args[0].isObject() && args[0].toObject().is<SizedTypeDescr>());
2657
2658 Rooted<SizedTypeDescr*> descr(cx, &args[0].toObject().as<SizedTypeDescr>());
2659 int32_t length = TypedObjLengthFromType(*descr);
2660 Rooted<TypedObject*> obj(cx);
2661 obj = TypedObject::createUnattachedWithClass(cx, &OpaqueTypedObject::class_, descr, length);
2662 if (!obj)
2663 return false;
2664 args.rval().setObject(*obj);
2665 return true;
2666 }
2667
2668 bool
2669 js::NewDerivedTypedObject(JSContext *cx, unsigned argc, Value *vp)
2670 {
2671 CallArgs args = CallArgsFromVp(argc, vp);
2672 JS_ASSERT(args.length() == 3);
2673 JS_ASSERT(args[0].isObject() && args[0].toObject().is<SizedTypeDescr>());
2674 JS_ASSERT(args[1].isObject() && args[1].toObject().is<TypedObject>());
2675 JS_ASSERT(args[2].isInt32());
2676
2677 Rooted<SizedTypeDescr*> descr(cx, &args[0].toObject().as<SizedTypeDescr>());
2678 Rooted<TypedObject*> typedObj(cx, &args[1].toObject().as<TypedObject>());
2679 int32_t offset = args[2].toInt32();
2680
2681 Rooted<TypedObject*> obj(cx);
2682 obj = TypedObject::createDerived(cx, descr, typedObj, offset);
2683 if (!obj)
2684 return false;
2685
2686 args.rval().setObject(*obj);
2687 return true;
2688 }
2689
2690 bool
2691 js::AttachTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
2692 {
2693 CallArgs args = CallArgsFromVp(argc, vp);
2694 JS_ASSERT(args.length() == 3);
2695 JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
2696 JS_ASSERT(args[1].isObject() && args[1].toObject().is<TypedObject>());
2697 JS_ASSERT(args[2].isInt32());
2698
2699 TypedObject &handle = args[0].toObject().as<TypedObject>();
2700 TypedObject &target = args[1].toObject().as<TypedObject>();
2701 JS_ASSERT(handle.typedMem() == nullptr); // must not be attached already
2702 size_t offset = args[2].toInt32();
2703 handle.attach(target, offset);
2704 return true;
2705 }
2706
2707 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::AttachTypedObjectJitInfo,
2708 AttachTypedObjectJitInfo,
2709 js::AttachTypedObject);
2710
2711 bool
2712 js::SetTypedObjectOffset(ThreadSafeContext *, unsigned argc, Value *vp)
2713 {
2714 CallArgs args = CallArgsFromVp(argc, vp);
2715 JS_ASSERT(args.length() == 2);
2716 JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
2717 JS_ASSERT(args[1].isInt32());
2718
2719 TypedObject &typedObj = args[0].toObject().as<TypedObject>();
2720 int32_t offset = args[1].toInt32();
2721
2722 JS_ASSERT(!typedObj.owner().isNeutered());
2723 JS_ASSERT(typedObj.typedMem() != nullptr); // must be attached already
2724
2725 typedObj.setPrivate(typedObj.owner().dataPointer() + offset);
2726 typedObj.setReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(offset));
2727 args.rval().setUndefined();
2728 return true;
2729 }
2730
2731 bool
2732 js::intrinsic_SetTypedObjectOffset(JSContext *cx, unsigned argc, Value *vp)
2733 {
2734 // Do not use JSNativeThreadSafeWrapper<> so that ion can reference
2735 // this function more easily when inlining.
2736 return SetTypedObjectOffset(cx, argc, vp);
2737 }
2738
2739 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::intrinsic_SetTypedObjectOffsetJitInfo,
2740 SetTypedObjectJitInfo,
2741 SetTypedObjectOffset);
2742
2743 bool
2744 js::ObjectIsTypeDescr(ThreadSafeContext *, unsigned argc, Value *vp)
2745 {
2746 CallArgs args = CallArgsFromVp(argc, vp);
2747 JS_ASSERT(args.length() == 1);
2748 JS_ASSERT(args[0].isObject());
2749 args.rval().setBoolean(args[0].toObject().is<TypeDescr>());
2750 return true;
2751 }
2752
2753 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsTypeDescrJitInfo, ObjectIsTypeDescrJitInfo,
2754 js::ObjectIsTypeDescr);
2755
2756 bool
2757 js::ObjectIsTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
2758 {
2759 CallArgs args = CallArgsFromVp(argc, vp);
2760 JS_ASSERT(args.length() == 1);
2761 JS_ASSERT(args[0].isObject());
2762 args.rval().setBoolean(args[0].toObject().is<TypedObject>());
2763 return true;
2764 }
2765
2766 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsTypedObjectJitInfo,
2767 ObjectIsTypedObjectJitInfo,
2768 js::ObjectIsTypedObject);
2769
2770 bool
2771 js::ObjectIsOpaqueTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
2772 {
2773 CallArgs args = CallArgsFromVp(argc, vp);
2774 JS_ASSERT(args.length() == 1);
2775 JS_ASSERT(args[0].isObject());
2776 args.rval().setBoolean(args[0].toObject().is<OpaqueTypedObject>());
2777 return true;
2778 }
2779
2780 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsOpaqueTypedObjectJitInfo,
2781 ObjectIsOpaqueTypedObjectJitInfo,
2782 js::ObjectIsOpaqueTypedObject);
2783
2784 bool
2785 js::ObjectIsTransparentTypedObject(ThreadSafeContext *, unsigned argc, Value *vp)
2786 {
2787 CallArgs args = CallArgsFromVp(argc, vp);
2788 JS_ASSERT(args.length() == 1);
2789 JS_ASSERT(args[0].isObject());
2790 args.rval().setBoolean(args[0].toObject().is<TransparentTypedObject>());
2791 return true;
2792 }
2793
2794 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ObjectIsTransparentTypedObjectJitInfo,
2795 ObjectIsTransparentTypedObjectJitInfo,
2796 js::ObjectIsTransparentTypedObject);
2797
2798 bool
2799 js::TypeDescrIsSimpleType(ThreadSafeContext *, unsigned argc, Value *vp)
2800 {
2801 CallArgs args = CallArgsFromVp(argc, vp);
2802 JS_ASSERT(args.length() == 1);
2803 JS_ASSERT(args[0].isObject());
2804 JS_ASSERT(args[0].toObject().is<js::TypeDescr>());
2805 args.rval().setBoolean(args[0].toObject().is<js::SimpleTypeDescr>());
2806 return true;
2807 }
2808
2809 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypeDescrIsSimpleTypeJitInfo,
2810 TypeDescrIsSimpleTypeJitInfo,
2811 js::TypeDescrIsSimpleType);
2812
2813 bool
2814 js::TypeDescrIsArrayType(ThreadSafeContext *, unsigned argc, Value *vp)
2815 {
2816 CallArgs args = CallArgsFromVp(argc, vp);
2817 JS_ASSERT(args.length() == 1);
2818 JS_ASSERT(args[0].isObject());
2819 JS_ASSERT(args[0].toObject().is<js::TypeDescr>());
2820 JSObject& obj = args[0].toObject();
2821 args.rval().setBoolean(obj.is<js::SizedArrayTypeDescr>() ||
2822 obj.is<js::UnsizedArrayTypeDescr>());
2823 return true;
2824 }
2825
2826 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypeDescrIsArrayTypeJitInfo,
2827 TypeDescrIsArrayTypeJitInfo,
2828 js::TypeDescrIsArrayType);
2829
2830 bool
2831 js::TypeDescrIsSizedArrayType(ThreadSafeContext *, unsigned argc, Value *vp)
2832 {
2833 CallArgs args = CallArgsFromVp(argc, vp);
2834 JS_ASSERT(args.length() == 1);
2835 JS_ASSERT(args[0].isObject());
2836 JS_ASSERT(args[0].toObject().is<js::TypeDescr>());
2837 args.rval().setBoolean(args[0].toObject().is<js::SizedArrayTypeDescr>());
2838 return true;
2839 }
2840
2841 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypeDescrIsSizedArrayTypeJitInfo,
2842 TypeDescrIsSizedArrayTypeJitInfo,
2843 js::TypeDescrIsSizedArrayType);
2844
2845 bool
2846 js::TypeDescrIsUnsizedArrayType(ThreadSafeContext *, unsigned argc, Value *vp)
2847 {
2848 CallArgs args = CallArgsFromVp(argc, vp);
2849 JS_ASSERT(args.length() == 1);
2850 JS_ASSERT(args[0].isObject());
2851 JS_ASSERT(args[0].toObject().is<js::TypeDescr>());
2852 args.rval().setBoolean(args[0].toObject().is<js::UnsizedArrayTypeDescr>());
2853 return true;
2854 }
2855
2856 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypeDescrIsUnsizedArrayTypeJitInfo,
2857 TypeDescrIsUnsizedArrayTypeJitInfo,
2858 js::TypeDescrIsUnsizedArrayType);
2859
2860 bool
2861 js::TypedObjectIsAttached(ThreadSafeContext *cx, unsigned argc, Value *vp)
2862 {
2863 CallArgs args = CallArgsFromVp(argc, vp);
2864 JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
2865 TypedObject &typedObj = args[0].toObject().as<TypedObject>();
2866 args.rval().setBoolean(!typedObj.owner().isNeutered() && typedObj.typedMem() != nullptr);
2867 return true;
2868 }
2869
2870 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::TypedObjectIsAttachedJitInfo,
2871 TypedObjectIsAttachedJitInfo,
2872 js::TypedObjectIsAttached);
2873
2874 bool
2875 js::ClampToUint8(ThreadSafeContext *, unsigned argc, Value *vp)
2876 {
2877 CallArgs args = CallArgsFromVp(argc, vp);
2878 JS_ASSERT(args.length() == 1);
2879 JS_ASSERT(args[0].isNumber());
2880 args.rval().setNumber(ClampDoubleToUint8(args[0].toNumber()));
2881 return true;
2882 }
2883
2884 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::ClampToUint8JitInfo, ClampToUint8JitInfo,
2885 js::ClampToUint8);
2886
2887 bool
2888 js::Memcpy(ThreadSafeContext *, unsigned argc, Value *vp)
2889 {
2890 CallArgs args = CallArgsFromVp(argc, vp);
2891 JS_ASSERT(args.length() == 5);
2892 JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());
2893 JS_ASSERT(args[1].isInt32());
2894 JS_ASSERT(args[2].isObject() && args[2].toObject().is<TypedObject>());
2895 JS_ASSERT(args[3].isInt32());
2896 JS_ASSERT(args[4].isInt32());
2897
2898 TypedObject &targetTypedObj = args[0].toObject().as<TypedObject>();
2899 int32_t targetOffset = args[1].toInt32();
2900 TypedObject &sourceTypedObj = args[2].toObject().as<TypedObject>();
2901 int32_t sourceOffset = args[3].toInt32();
2902 int32_t size = args[4].toInt32();
2903
2904 JS_ASSERT(targetOffset >= 0);
2905 JS_ASSERT(sourceOffset >= 0);
2906 JS_ASSERT(size >= 0);
2907 JS_ASSERT(size + targetOffset <= targetTypedObj.size());
2908 JS_ASSERT(size + sourceOffset <= sourceTypedObj.size());
2909
2910 uint8_t *target = targetTypedObj.typedMem(targetOffset);
2911 uint8_t *source = sourceTypedObj.typedMem(sourceOffset);
2912 memcpy(target, source, size);
2913 args.rval().setUndefined();
2914 return true;
2915 }
2916
2917 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::MemcpyJitInfo, MemcpyJitInfo, js::Memcpy);
2918
2919 bool
2920 js::GetTypedObjectModule(JSContext *cx, unsigned argc, Value *vp)
2921 {
2922 CallArgs args = CallArgsFromVp(argc, vp);
2923 Rooted<GlobalObject*> global(cx, cx->global());
2924 JS_ASSERT(global);
2925 args.rval().setObject(global->getTypedObjectModule());
2926 return true;
2927 }
2928
2929 bool
2930 js::GetFloat32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp)
2931 {
2932 CallArgs args = CallArgsFromVp(argc, vp);
2933 Rooted<GlobalObject*> global(cx, cx->global());
2934 JS_ASSERT(global);
2935 args.rval().setObject(global->float32x4TypeDescr());
2936 return true;
2937 }
2938
2939 bool
2940 js::GetInt32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp)
2941 {
2942 CallArgs args = CallArgsFromVp(argc, vp);
2943 Rooted<GlobalObject*> global(cx, cx->global());
2944 JS_ASSERT(global);
2945 args.rval().setObject(global->int32x4TypeDescr());
2946 return true;
2947 }
2948
2949 #define JS_STORE_SCALAR_CLASS_IMPL(_constant, T, _name) \
2950 bool \
2951 js::StoreScalar##T::Func(ThreadSafeContext *, unsigned argc, Value *vp) \
2952 { \
2953 CallArgs args = CallArgsFromVp(argc, vp); \
2954 JS_ASSERT(args.length() == 3); \
2955 JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>()); \
2956 JS_ASSERT(args[1].isInt32()); \
2957 JS_ASSERT(args[2].isNumber()); \
2958 \
2959 TypedObject &typedObj = args[0].toObject().as<TypedObject>(); \
2960 int32_t offset = args[1].toInt32(); \
2961 \
2962 /* Should be guaranteed by the typed objects API: */ \
2963 JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0); \
2964 \
2965 T *target = reinterpret_cast<T*>(typedObj.typedMem(offset)); \
2966 double d = args[2].toNumber(); \
2967 *target = ConvertScalar<T>(d); \
2968 args.rval().setUndefined(); \
2969 return true; \
2970 } \
2971 \
2972 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::StoreScalar##T::JitInfo, \
2973 StoreScalar##T, \
2974 js::StoreScalar##T::Func);
2975
2976 #define JS_STORE_REFERENCE_CLASS_IMPL(_constant, T, _name) \
2977 bool \
2978 js::StoreReference##T::Func(ThreadSafeContext *, unsigned argc, Value *vp) \
2979 { \
2980 CallArgs args = CallArgsFromVp(argc, vp); \
2981 JS_ASSERT(args.length() == 3); \
2982 JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>()); \
2983 JS_ASSERT(args[1].isInt32()); \
2984 \
2985 TypedObject &typedObj = args[0].toObject().as<TypedObject>(); \
2986 int32_t offset = args[1].toInt32(); \
2987 \
2988 /* Should be guaranteed by the typed objects API: */ \
2989 JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0); \
2990 \
2991 T *target = reinterpret_cast<T*>(typedObj.typedMem(offset)); \
2992 store(target, args[2]); \
2993 args.rval().setUndefined(); \
2994 return true; \
2995 } \
2996 \
2997 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::StoreReference##T::JitInfo, \
2998 StoreReference##T, \
2999 js::StoreReference##T::Func);
3000
3001 #define JS_LOAD_SCALAR_CLASS_IMPL(_constant, T, _name) \
3002 bool \
3003 js::LoadScalar##T::Func(ThreadSafeContext *, unsigned argc, Value *vp) \
3004 { \
3005 CallArgs args = CallArgsFromVp(argc, vp); \
3006 JS_ASSERT(args.length() == 2); \
3007 JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>()); \
3008 JS_ASSERT(args[1].isInt32()); \
3009 \
3010 TypedObject &typedObj = args[0].toObject().as<TypedObject>(); \
3011 int32_t offset = args[1].toInt32(); \
3012 \
3013 /* Should be guaranteed by the typed objects API: */ \
3014 JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0); \
3015 \
3016 T *target = reinterpret_cast<T*>(typedObj.typedMem(offset)); \
3017 args.rval().setNumber((double) *target); \
3018 return true; \
3019 } \
3020 \
3021 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::LoadScalar##T::JitInfo, LoadScalar##T, \
3022 js::LoadScalar##T::Func);
3023
3024 #define JS_LOAD_REFERENCE_CLASS_IMPL(_constant, T, _name) \
3025 bool \
3026 js::LoadReference##T::Func(ThreadSafeContext *, unsigned argc, Value *vp) \
3027 { \
3028 CallArgs args = CallArgsFromVp(argc, vp); \
3029 JS_ASSERT(args.length() == 2); \
3030 JS_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>()); \
3031 JS_ASSERT(args[1].isInt32()); \
3032 \
3033 TypedObject &typedObj = args[0].toObject().as<TypedObject>(); \
3034 int32_t offset = args[1].toInt32(); \
3035 \
3036 /* Should be guaranteed by the typed objects API: */ \
3037 JS_ASSERT(offset % MOZ_ALIGNOF(T) == 0); \
3038 \
3039 T *target = reinterpret_cast<T*>(typedObj.typedMem(offset)); \
3040 load(target, args.rval()); \
3041 return true; \
3042 } \
3043 \
3044 JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::LoadReference##T::JitInfo, \
3045 LoadReference##T, \
3046 js::LoadReference##T::Func);
3047
3048 // Because the precise syntax for storing values/objects/strings
3049 // differs, we abstract it away using specialized variants of the
3050 // private methods `store()` and `load()`.
3051
3052 void
3053 StoreReferenceHeapValue::store(HeapValue *heap, const Value &v)
3054 {
3055 *heap = v;
3056 }
3057
3058 void
3059 StoreReferenceHeapPtrObject::store(HeapPtrObject *heap, const Value &v)
3060 {
3061 JS_ASSERT(v.isObjectOrNull()); // or else Store_object is being misused
3062 *heap = v.toObjectOrNull();
3063 }
3064
3065 void
3066 StoreReferenceHeapPtrString::store(HeapPtrString *heap, const Value &v)
3067 {
3068 JS_ASSERT(v.isString()); // or else Store_string is being misused
3069 *heap = v.toString();
3070 }
3071
3072 void
3073 LoadReferenceHeapValue::load(HeapValue *heap,
3074 MutableHandleValue v)
3075 {
3076 v.set(*heap);
3077 }
3078
3079 void
3080 LoadReferenceHeapPtrObject::load(HeapPtrObject *heap,
3081 MutableHandleValue v)
3082 {
3083 if (*heap)
3084 v.setObject(**heap);
3085 else
3086 v.setNull();
3087 }
3088
3089 void
3090 LoadReferenceHeapPtrString::load(HeapPtrString *heap,
3091 MutableHandleValue v)
3092 {
3093 v.setString(*heap);
3094 }
3095
3096 // I was using templates for this stuff instead of macros, but ran
3097 // into problems with the Unagi compiler.
3098 JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_STORE_SCALAR_CLASS_IMPL)
3099 JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_LOAD_SCALAR_CLASS_IMPL)
3100 JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_STORE_REFERENCE_CLASS_IMPL)
3101 JS_FOR_EACH_REFERENCE_TYPE_REPR(JS_LOAD_REFERENCE_CLASS_IMPL)
3102
3103 ///////////////////////////////////////////////////////////////////////////
3104 // Walking memory
3105
3106 template<typename V>
3107 static void
3108 visitReferences(SizedTypeDescr &descr,
3109 uint8_t *mem,
3110 V& visitor)
3111 {
3112 if (descr.transparent())
3113 return;
3114
3115 switch (descr.kind()) {
3116 case TypeDescr::Scalar:
3117 case TypeDescr::X4:
3118 return;
3119
3120 case TypeDescr::Reference:
3121 visitor.visitReference(descr.as<ReferenceTypeDescr>(), mem);
3122 return;
3123
3124 case TypeDescr::SizedArray:
3125 {
3126 SizedArrayTypeDescr &arrayDescr = descr.as<SizedArrayTypeDescr>();
3127 SizedTypeDescr &elementDescr = arrayDescr.elementType();
3128 for (int32_t i = 0; i < arrayDescr.length(); i++) {
3129 visitReferences(elementDescr, mem, visitor);
3130 mem += elementDescr.size();
3131 }
3132 return;
3133 }
3134
3135 case TypeDescr::UnsizedArray:
3136 {
3137 MOZ_ASSUME_UNREACHABLE("Only Sized Type representations");
3138 }
3139
3140 case TypeDescr::Struct:
3141 {
3142 StructTypeDescr &structDescr = descr.as<StructTypeDescr>();
3143 for (size_t i = 0; i < structDescr.fieldCount(); i++) {
3144 SizedTypeDescr &descr = structDescr.fieldDescr(i);
3145 size_t offset = structDescr.fieldOffset(i);
3146 visitReferences(descr, mem + offset, visitor);
3147 }
3148 return;
3149 }
3150 }
3151
3152 MOZ_ASSUME_UNREACHABLE("Invalid type repr kind");
3153 }
3154
3155 ///////////////////////////////////////////////////////////////////////////
3156 // Initializing instances
3157
3158 namespace js {
3159 class MemoryInitVisitor {
3160 const JSRuntime *rt_;
3161
3162 public:
3163 MemoryInitVisitor(const JSRuntime *rt)
3164 : rt_(rt)
3165 {}
3166
3167 void visitReference(ReferenceTypeDescr &descr, uint8_t *mem);
3168 };
3169 } // namespace js
3170
3171 void
3172 js::MemoryInitVisitor::visitReference(ReferenceTypeDescr &descr, uint8_t *mem)
3173 {
3174 switch (descr.type()) {
3175 case ReferenceTypeDescr::TYPE_ANY:
3176 {
3177 js::HeapValue *heapValue = reinterpret_cast<js::HeapValue *>(mem);
3178 heapValue->init(UndefinedValue());
3179 return;
3180 }
3181
3182 case ReferenceTypeDescr::TYPE_OBJECT:
3183 {
3184 js::HeapPtrObject *objectPtr =
3185 reinterpret_cast<js::HeapPtrObject *>(mem);
3186 objectPtr->init(nullptr);
3187 return;
3188 }
3189
3190 case ReferenceTypeDescr::TYPE_STRING:
3191 {
3192 js::HeapPtrString *stringPtr =
3193 reinterpret_cast<js::HeapPtrString *>(mem);
3194 stringPtr->init(rt_->emptyString);
3195 return;
3196 }
3197 }
3198
3199 MOZ_ASSUME_UNREACHABLE("Invalid kind");
3200 }
3201
3202 void
3203 SizedTypeDescr::initInstances(const JSRuntime *rt, uint8_t *mem, size_t length)
3204 {
3205 JS_ASSERT(length >= 1);
3206
3207 MemoryInitVisitor visitor(rt);
3208
3209 // Initialize the 0th instance
3210 memset(mem, 0, size());
3211 if (opaque())
3212 visitReferences(*this, mem, visitor);
3213
3214 // Stamp out N copies of later instances
3215 uint8_t *target = mem;
3216 for (size_t i = 1; i < length; i++) {
3217 target += size();
3218 memcpy(target, mem, size());
3219 }
3220 }
3221
3222 ///////////////////////////////////////////////////////////////////////////
3223 // Tracing instances
3224
3225 namespace js {
3226 class MemoryTracingVisitor {
3227 JSTracer *trace_;
3228
3229 public:
3230
3231 MemoryTracingVisitor(JSTracer *trace)
3232 : trace_(trace)
3233 {}
3234
3235 void visitReference(ReferenceTypeDescr &descr, uint8_t *mem);
3236 };
3237 } // namespace js
3238
3239 void
3240 js::MemoryTracingVisitor::visitReference(ReferenceTypeDescr &descr, uint8_t *mem)
3241 {
3242 switch (descr.type()) {
3243 case ReferenceTypeDescr::TYPE_ANY:
3244 {
3245 js::HeapValue *heapValue = reinterpret_cast<js::HeapValue *>(mem);
3246 gc::MarkValue(trace_, heapValue, "reference-val");
3247 return;
3248 }
3249
3250 case ReferenceTypeDescr::TYPE_OBJECT:
3251 {
3252 js::HeapPtrObject *objectPtr =
3253 reinterpret_cast<js::HeapPtrObject *>(mem);
3254 if (*objectPtr)
3255 gc::MarkObject(trace_, objectPtr, "reference-obj");
3256 return;
3257 }
3258
3259 case ReferenceTypeDescr::TYPE_STRING:
3260 {
3261 js::HeapPtrString *stringPtr =
3262 reinterpret_cast<js::HeapPtrString *>(mem);
3263 if (*stringPtr)
3264 gc::MarkString(trace_, stringPtr, "reference-str");
3265 return;
3266 }
3267 }
3268
3269 MOZ_ASSUME_UNREACHABLE("Invalid kind");
3270 }
3271
3272 void
3273 SizedTypeDescr::traceInstances(JSTracer *trace, uint8_t *mem, size_t length)
3274 {
3275 MemoryTracingVisitor visitor(trace);
3276
3277 for (size_t i = 0; i < length; i++) {
3278 visitReferences(*this, mem, visitor);
3279 mem += size();
3280 }
3281 }
3282

mercurial