|
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 |