js/src/builtin/TypedObject.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial