Sat, 03 Jan 2015 20:18:00 +0100
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 |