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 "vm/TypedArrayObject.h" |
michael@0 | 8 | |
michael@0 | 9 | #include "mozilla/Alignment.h" |
michael@0 | 10 | #include "mozilla/FloatingPoint.h" |
michael@0 | 11 | #include "mozilla/PodOperations.h" |
michael@0 | 12 | |
michael@0 | 13 | #include <string.h> |
michael@0 | 14 | #ifndef XP_WIN |
michael@0 | 15 | # include <sys/mman.h> |
michael@0 | 16 | #endif |
michael@0 | 17 | |
michael@0 | 18 | #include "jsapi.h" |
michael@0 | 19 | #include "jsarray.h" |
michael@0 | 20 | #include "jscntxt.h" |
michael@0 | 21 | #include "jscpucfg.h" |
michael@0 | 22 | #include "jsnum.h" |
michael@0 | 23 | #include "jsobj.h" |
michael@0 | 24 | #include "jstypes.h" |
michael@0 | 25 | #include "jsutil.h" |
michael@0 | 26 | #ifdef XP_WIN |
michael@0 | 27 | # include "jswin.h" |
michael@0 | 28 | #endif |
michael@0 | 29 | #include "jswrapper.h" |
michael@0 | 30 | |
michael@0 | 31 | #include "gc/Barrier.h" |
michael@0 | 32 | #include "gc/Marking.h" |
michael@0 | 33 | #include "jit/AsmJS.h" |
michael@0 | 34 | #include "jit/AsmJSModule.h" |
michael@0 | 35 | #include "vm/ArrayBufferObject.h" |
michael@0 | 36 | #include "vm/GlobalObject.h" |
michael@0 | 37 | #include "vm/Interpreter.h" |
michael@0 | 38 | #include "vm/NumericConversions.h" |
michael@0 | 39 | #include "vm/SharedArrayObject.h" |
michael@0 | 40 | #include "vm/WrapperObject.h" |
michael@0 | 41 | |
michael@0 | 42 | #include "jsatominlines.h" |
michael@0 | 43 | #include "jsinferinlines.h" |
michael@0 | 44 | #include "jsobjinlines.h" |
michael@0 | 45 | |
michael@0 | 46 | #include "vm/Shape-inl.h" |
michael@0 | 47 | |
michael@0 | 48 | using namespace js; |
michael@0 | 49 | using namespace js::gc; |
michael@0 | 50 | using namespace js::types; |
michael@0 | 51 | |
michael@0 | 52 | using mozilla::IsNaN; |
michael@0 | 53 | using mozilla::NegativeInfinity; |
michael@0 | 54 | using mozilla::PodCopy; |
michael@0 | 55 | using mozilla::PositiveInfinity; |
michael@0 | 56 | using JS::CanonicalizeNaN; |
michael@0 | 57 | using JS::GenericNaN; |
michael@0 | 58 | |
michael@0 | 59 | static bool |
michael@0 | 60 | ValueIsLength(const Value &v, uint32_t *len) |
michael@0 | 61 | { |
michael@0 | 62 | if (v.isInt32()) { |
michael@0 | 63 | int32_t i = v.toInt32(); |
michael@0 | 64 | if (i < 0) |
michael@0 | 65 | return false; |
michael@0 | 66 | *len = i; |
michael@0 | 67 | return true; |
michael@0 | 68 | } |
michael@0 | 69 | |
michael@0 | 70 | if (v.isDouble()) { |
michael@0 | 71 | double d = v.toDouble(); |
michael@0 | 72 | if (IsNaN(d)) |
michael@0 | 73 | return false; |
michael@0 | 74 | |
michael@0 | 75 | uint32_t length = uint32_t(d); |
michael@0 | 76 | if (d != double(length)) |
michael@0 | 77 | return false; |
michael@0 | 78 | |
michael@0 | 79 | *len = length; |
michael@0 | 80 | return true; |
michael@0 | 81 | } |
michael@0 | 82 | |
michael@0 | 83 | return false; |
michael@0 | 84 | } |
michael@0 | 85 | |
michael@0 | 86 | /* |
michael@0 | 87 | * TypedArrayObject |
michael@0 | 88 | * |
michael@0 | 89 | * The non-templated base class for the specific typed implementations. |
michael@0 | 90 | * This class holds all the member variables that are used by |
michael@0 | 91 | * the subclasses. |
michael@0 | 92 | */ |
michael@0 | 93 | |
michael@0 | 94 | void |
michael@0 | 95 | TypedArrayObject::neuter(void *newData) |
michael@0 | 96 | { |
michael@0 | 97 | setSlot(LENGTH_SLOT, Int32Value(0)); |
michael@0 | 98 | setSlot(BYTELENGTH_SLOT, Int32Value(0)); |
michael@0 | 99 | setSlot(BYTEOFFSET_SLOT, Int32Value(0)); |
michael@0 | 100 | setPrivate(newData); |
michael@0 | 101 | } |
michael@0 | 102 | |
michael@0 | 103 | ArrayBufferObject * |
michael@0 | 104 | TypedArrayObject::sharedBuffer() const |
michael@0 | 105 | { |
michael@0 | 106 | return &bufferValue(const_cast<TypedArrayObject*>(this)).toObject().as<SharedArrayBufferObject>(); |
michael@0 | 107 | } |
michael@0 | 108 | |
michael@0 | 109 | /* static */ bool |
michael@0 | 110 | TypedArrayObject::ensureHasBuffer(JSContext *cx, Handle<TypedArrayObject *> tarray) |
michael@0 | 111 | { |
michael@0 | 112 | if (tarray->buffer()) |
michael@0 | 113 | return true; |
michael@0 | 114 | |
michael@0 | 115 | Rooted<ArrayBufferObject *> buffer(cx, ArrayBufferObject::create(cx, tarray->byteLength())); |
michael@0 | 116 | if (!buffer) |
michael@0 | 117 | return false; |
michael@0 | 118 | |
michael@0 | 119 | buffer->addView(tarray); |
michael@0 | 120 | |
michael@0 | 121 | memcpy(buffer->dataPointer(), tarray->viewData(), tarray->byteLength()); |
michael@0 | 122 | InitArrayBufferViewDataPointer(tarray, buffer, 0); |
michael@0 | 123 | |
michael@0 | 124 | tarray->setSlot(BUFFER_SLOT, ObjectValue(*buffer)); |
michael@0 | 125 | return true; |
michael@0 | 126 | } |
michael@0 | 127 | |
michael@0 | 128 | /* static */ int |
michael@0 | 129 | TypedArrayObject::lengthOffset() |
michael@0 | 130 | { |
michael@0 | 131 | return JSObject::getFixedSlotOffset(LENGTH_SLOT); |
michael@0 | 132 | } |
michael@0 | 133 | |
michael@0 | 134 | /* static */ int |
michael@0 | 135 | TypedArrayObject::dataOffset() |
michael@0 | 136 | { |
michael@0 | 137 | return JSObject::getPrivateDataOffset(DATA_SLOT); |
michael@0 | 138 | } |
michael@0 | 139 | |
michael@0 | 140 | /* Helper clamped uint8_t type */ |
michael@0 | 141 | |
michael@0 | 142 | uint32_t JS_FASTCALL |
michael@0 | 143 | js::ClampDoubleToUint8(const double x) |
michael@0 | 144 | { |
michael@0 | 145 | // Not < so that NaN coerces to 0 |
michael@0 | 146 | if (!(x >= 0)) |
michael@0 | 147 | return 0; |
michael@0 | 148 | |
michael@0 | 149 | if (x > 255) |
michael@0 | 150 | return 255; |
michael@0 | 151 | |
michael@0 | 152 | double toTruncate = x + 0.5; |
michael@0 | 153 | uint8_t y = uint8_t(toTruncate); |
michael@0 | 154 | |
michael@0 | 155 | /* |
michael@0 | 156 | * now val is rounded to nearest, ties rounded up. We want |
michael@0 | 157 | * rounded to nearest ties to even, so check whether we had a |
michael@0 | 158 | * tie. |
michael@0 | 159 | */ |
michael@0 | 160 | if (y == toTruncate) { |
michael@0 | 161 | /* |
michael@0 | 162 | * It was a tie (since adding 0.5 gave us the exact integer |
michael@0 | 163 | * we want). Since we rounded up, we either already have an |
michael@0 | 164 | * even number or we have an odd number but the number we |
michael@0 | 165 | * want is one less. So just unconditionally masking out the |
michael@0 | 166 | * ones bit should do the trick to get us the value we |
michael@0 | 167 | * want. |
michael@0 | 168 | */ |
michael@0 | 169 | return y & ~1; |
michael@0 | 170 | } |
michael@0 | 171 | |
michael@0 | 172 | return y; |
michael@0 | 173 | } |
michael@0 | 174 | |
michael@0 | 175 | template<typename NativeType> static inline const int TypeIDOfType(); |
michael@0 | 176 | template<> inline const int TypeIDOfType<int8_t>() { return ScalarTypeDescr::TYPE_INT8; } |
michael@0 | 177 | template<> inline const int TypeIDOfType<uint8_t>() { return ScalarTypeDescr::TYPE_UINT8; } |
michael@0 | 178 | template<> inline const int TypeIDOfType<int16_t>() { return ScalarTypeDescr::TYPE_INT16; } |
michael@0 | 179 | template<> inline const int TypeIDOfType<uint16_t>() { return ScalarTypeDescr::TYPE_UINT16; } |
michael@0 | 180 | template<> inline const int TypeIDOfType<int32_t>() { return ScalarTypeDescr::TYPE_INT32; } |
michael@0 | 181 | template<> inline const int TypeIDOfType<uint32_t>() { return ScalarTypeDescr::TYPE_UINT32; } |
michael@0 | 182 | template<> inline const int TypeIDOfType<float>() { return ScalarTypeDescr::TYPE_FLOAT32; } |
michael@0 | 183 | template<> inline const int TypeIDOfType<double>() { return ScalarTypeDescr::TYPE_FLOAT64; } |
michael@0 | 184 | template<> inline const int TypeIDOfType<uint8_clamped>() { return ScalarTypeDescr::TYPE_UINT8_CLAMPED; } |
michael@0 | 185 | |
michael@0 | 186 | template<typename ElementType> |
michael@0 | 187 | static inline JSObject * |
michael@0 | 188 | NewArray(JSContext *cx, uint32_t nelements); |
michael@0 | 189 | |
michael@0 | 190 | namespace { |
michael@0 | 191 | |
michael@0 | 192 | template<typename NativeType> |
michael@0 | 193 | class TypedArrayObjectTemplate : public TypedArrayObject |
michael@0 | 194 | { |
michael@0 | 195 | public: |
michael@0 | 196 | typedef NativeType ThisType; |
michael@0 | 197 | typedef TypedArrayObjectTemplate<NativeType> ThisTypedArrayObject; |
michael@0 | 198 | static const int ArrayTypeID() { return TypeIDOfType<NativeType>(); } |
michael@0 | 199 | static const bool ArrayTypeIsUnsigned() { return TypeIsUnsigned<NativeType>(); } |
michael@0 | 200 | static const bool ArrayTypeIsFloatingPoint() { return TypeIsFloatingPoint<NativeType>(); } |
michael@0 | 201 | |
michael@0 | 202 | static const size_t BYTES_PER_ELEMENT = sizeof(ThisType); |
michael@0 | 203 | |
michael@0 | 204 | static inline const Class *protoClass() |
michael@0 | 205 | { |
michael@0 | 206 | return &TypedArrayObject::protoClasses[ArrayTypeID()]; |
michael@0 | 207 | } |
michael@0 | 208 | |
michael@0 | 209 | static inline const Class *instanceClass() |
michael@0 | 210 | { |
michael@0 | 211 | return &TypedArrayObject::classes[ArrayTypeID()]; |
michael@0 | 212 | } |
michael@0 | 213 | |
michael@0 | 214 | static bool is(HandleValue v) { |
michael@0 | 215 | return v.isObject() && v.toObject().hasClass(instanceClass()); |
michael@0 | 216 | } |
michael@0 | 217 | |
michael@0 | 218 | static void |
michael@0 | 219 | setIndexValue(TypedArrayObject &tarray, uint32_t index, double d) |
michael@0 | 220 | { |
michael@0 | 221 | // If the array is an integer array, we only handle up to |
michael@0 | 222 | // 32-bit ints from this point on. if we want to handle |
michael@0 | 223 | // 64-bit ints, we'll need some changes. |
michael@0 | 224 | |
michael@0 | 225 | // Assign based on characteristics of the destination type |
michael@0 | 226 | if (ArrayTypeIsFloatingPoint()) { |
michael@0 | 227 | setIndex(tarray, index, NativeType(d)); |
michael@0 | 228 | } else if (ArrayTypeIsUnsigned()) { |
michael@0 | 229 | JS_ASSERT(sizeof(NativeType) <= 4); |
michael@0 | 230 | uint32_t n = ToUint32(d); |
michael@0 | 231 | setIndex(tarray, index, NativeType(n)); |
michael@0 | 232 | } else if (ArrayTypeID() == ScalarTypeDescr::TYPE_UINT8_CLAMPED) { |
michael@0 | 233 | // The uint8_clamped type has a special rounding converter |
michael@0 | 234 | // for doubles. |
michael@0 | 235 | setIndex(tarray, index, NativeType(d)); |
michael@0 | 236 | } else { |
michael@0 | 237 | JS_ASSERT(sizeof(NativeType) <= 4); |
michael@0 | 238 | int32_t n = ToInt32(d); |
michael@0 | 239 | setIndex(tarray, index, NativeType(n)); |
michael@0 | 240 | } |
michael@0 | 241 | } |
michael@0 | 242 | |
michael@0 | 243 | static TypedArrayObject * |
michael@0 | 244 | makeProtoInstance(JSContext *cx, HandleObject proto, AllocKind allocKind) |
michael@0 | 245 | { |
michael@0 | 246 | JS_ASSERT(proto); |
michael@0 | 247 | |
michael@0 | 248 | RootedObject obj(cx, NewBuiltinClassInstance(cx, instanceClass(), allocKind)); |
michael@0 | 249 | if (!obj) |
michael@0 | 250 | return nullptr; |
michael@0 | 251 | |
michael@0 | 252 | types::TypeObject *type = cx->getNewType(obj->getClass(), proto.get()); |
michael@0 | 253 | if (!type) |
michael@0 | 254 | return nullptr; |
michael@0 | 255 | obj->setType(type); |
michael@0 | 256 | |
michael@0 | 257 | return &obj->as<TypedArrayObject>(); |
michael@0 | 258 | } |
michael@0 | 259 | |
michael@0 | 260 | static TypedArrayObject * |
michael@0 | 261 | makeTypedInstance(JSContext *cx, uint32_t len, AllocKind allocKind) |
michael@0 | 262 | { |
michael@0 | 263 | if (len * sizeof(NativeType) >= TypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH) { |
michael@0 | 264 | return &NewBuiltinClassInstance(cx, instanceClass(), allocKind, |
michael@0 | 265 | SingletonObject)->as<TypedArrayObject>(); |
michael@0 | 266 | } |
michael@0 | 267 | |
michael@0 | 268 | jsbytecode *pc; |
michael@0 | 269 | RootedScript script(cx, cx->currentScript(&pc)); |
michael@0 | 270 | NewObjectKind newKind = script |
michael@0 | 271 | ? UseNewTypeForInitializer(script, pc, instanceClass()) |
michael@0 | 272 | : GenericObject; |
michael@0 | 273 | RootedObject obj(cx, NewBuiltinClassInstance(cx, instanceClass(), allocKind, newKind)); |
michael@0 | 274 | if (!obj) |
michael@0 | 275 | return nullptr; |
michael@0 | 276 | |
michael@0 | 277 | if (script) { |
michael@0 | 278 | if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind)) |
michael@0 | 279 | return nullptr; |
michael@0 | 280 | } |
michael@0 | 281 | |
michael@0 | 282 | return &obj->as<TypedArrayObject>(); |
michael@0 | 283 | } |
michael@0 | 284 | |
michael@0 | 285 | static JSObject * |
michael@0 | 286 | makeInstance(JSContext *cx, Handle<ArrayBufferObject *> buffer, uint32_t byteOffset, uint32_t len, |
michael@0 | 287 | HandleObject proto) |
michael@0 | 288 | { |
michael@0 | 289 | JS_ASSERT_IF(!buffer, byteOffset == 0); |
michael@0 | 290 | |
michael@0 | 291 | gc::AllocKind allocKind = buffer |
michael@0 | 292 | ? GetGCObjectKind(instanceClass()) |
michael@0 | 293 | : AllocKindForLazyBuffer(len * sizeof(NativeType)); |
michael@0 | 294 | |
michael@0 | 295 | Rooted<TypedArrayObject*> obj(cx); |
michael@0 | 296 | if (proto) |
michael@0 | 297 | obj = makeProtoInstance(cx, proto, allocKind); |
michael@0 | 298 | else |
michael@0 | 299 | obj = makeTypedInstance(cx, len, allocKind); |
michael@0 | 300 | if (!obj) |
michael@0 | 301 | return nullptr; |
michael@0 | 302 | |
michael@0 | 303 | obj->setSlot(TYPE_SLOT, Int32Value(ArrayTypeID())); |
michael@0 | 304 | obj->setSlot(BUFFER_SLOT, ObjectOrNullValue(buffer)); |
michael@0 | 305 | |
michael@0 | 306 | if (buffer) { |
michael@0 | 307 | InitArrayBufferViewDataPointer(obj, buffer, byteOffset); |
michael@0 | 308 | } else { |
michael@0 | 309 | void *data = obj->fixedData(FIXED_DATA_START); |
michael@0 | 310 | obj->initPrivate(data); |
michael@0 | 311 | memset(data, 0, len * sizeof(NativeType)); |
michael@0 | 312 | } |
michael@0 | 313 | |
michael@0 | 314 | obj->setSlot(LENGTH_SLOT, Int32Value(len)); |
michael@0 | 315 | obj->setSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset)); |
michael@0 | 316 | obj->setSlot(BYTELENGTH_SLOT, Int32Value(len * sizeof(NativeType))); |
michael@0 | 317 | obj->setSlot(NEXT_VIEW_SLOT, PrivateValue(nullptr)); |
michael@0 | 318 | |
michael@0 | 319 | #ifdef DEBUG |
michael@0 | 320 | if (buffer) { |
michael@0 | 321 | uint32_t arrayByteLength = obj->byteLength(); |
michael@0 | 322 | uint32_t arrayByteOffset = obj->byteOffset(); |
michael@0 | 323 | uint32_t bufferByteLength = buffer->byteLength(); |
michael@0 | 324 | JS_ASSERT_IF(!buffer->isNeutered(), buffer->dataPointer() <= obj->viewData()); |
michael@0 | 325 | JS_ASSERT(bufferByteLength - arrayByteOffset >= arrayByteLength); |
michael@0 | 326 | JS_ASSERT(arrayByteOffset <= bufferByteLength); |
michael@0 | 327 | } |
michael@0 | 328 | |
michael@0 | 329 | // Verify that the private slot is at the expected place |
michael@0 | 330 | JS_ASSERT(obj->numFixedSlots() == DATA_SLOT); |
michael@0 | 331 | #endif |
michael@0 | 332 | |
michael@0 | 333 | if (buffer) |
michael@0 | 334 | buffer->addView(obj); |
michael@0 | 335 | |
michael@0 | 336 | return obj; |
michael@0 | 337 | } |
michael@0 | 338 | |
michael@0 | 339 | static JSObject * |
michael@0 | 340 | makeInstance(JSContext *cx, Handle<ArrayBufferObject *> bufobj, uint32_t byteOffset, uint32_t len) |
michael@0 | 341 | { |
michael@0 | 342 | RootedObject nullproto(cx, nullptr); |
michael@0 | 343 | return makeInstance(cx, bufobj, byteOffset, len, nullproto); |
michael@0 | 344 | } |
michael@0 | 345 | |
michael@0 | 346 | /* |
michael@0 | 347 | * new [Type]Array(length) |
michael@0 | 348 | * new [Type]Array(otherTypedArray) |
michael@0 | 349 | * new [Type]Array(JSArray) |
michael@0 | 350 | * new [Type]Array(ArrayBuffer, [optional] byteOffset, [optional] length) |
michael@0 | 351 | */ |
michael@0 | 352 | static bool |
michael@0 | 353 | class_constructor(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 354 | { |
michael@0 | 355 | /* N.B. this is a constructor for protoClass, not instanceClass! */ |
michael@0 | 356 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 357 | JSObject *obj = create(cx, args); |
michael@0 | 358 | if (!obj) |
michael@0 | 359 | return false; |
michael@0 | 360 | args.rval().setObject(*obj); |
michael@0 | 361 | return true; |
michael@0 | 362 | } |
michael@0 | 363 | |
michael@0 | 364 | static JSObject * |
michael@0 | 365 | create(JSContext *cx, const CallArgs& args) |
michael@0 | 366 | { |
michael@0 | 367 | /* () or (number) */ |
michael@0 | 368 | uint32_t len = 0; |
michael@0 | 369 | if (args.length() == 0 || ValueIsLength(args[0], &len)) |
michael@0 | 370 | return fromLength(cx, len); |
michael@0 | 371 | |
michael@0 | 372 | /* (not an object) */ |
michael@0 | 373 | if (!args[0].isObject()) { |
michael@0 | 374 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
michael@0 | 375 | return nullptr; |
michael@0 | 376 | } |
michael@0 | 377 | |
michael@0 | 378 | RootedObject dataObj(cx, &args.get(0).toObject()); |
michael@0 | 379 | |
michael@0 | 380 | /* |
michael@0 | 381 | * (typedArray) |
michael@0 | 382 | * (type[] array) |
michael@0 | 383 | * |
michael@0 | 384 | * Otherwise create a new typed array and copy elements 0..len-1 |
michael@0 | 385 | * properties from the object, treating it as some sort of array. |
michael@0 | 386 | * Note that offset and length will be ignored |
michael@0 | 387 | */ |
michael@0 | 388 | if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObject>() && |
michael@0 | 389 | !UncheckedUnwrap(dataObj)->is<SharedArrayBufferObject>()) |
michael@0 | 390 | { |
michael@0 | 391 | return fromArray(cx, dataObj); |
michael@0 | 392 | } |
michael@0 | 393 | |
michael@0 | 394 | /* (ArrayBuffer, [byteOffset, [length]]) */ |
michael@0 | 395 | int32_t byteOffset = 0; |
michael@0 | 396 | int32_t length = -1; |
michael@0 | 397 | |
michael@0 | 398 | if (args.length() > 1) { |
michael@0 | 399 | if (!ToInt32(cx, args[1], &byteOffset)) |
michael@0 | 400 | return nullptr; |
michael@0 | 401 | if (byteOffset < 0) { |
michael@0 | 402 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
michael@0 | 403 | JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "1"); |
michael@0 | 404 | return nullptr; |
michael@0 | 405 | } |
michael@0 | 406 | |
michael@0 | 407 | if (args.length() > 2) { |
michael@0 | 408 | if (!ToInt32(cx, args[2], &length)) |
michael@0 | 409 | return nullptr; |
michael@0 | 410 | if (length < 0) { |
michael@0 | 411 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
michael@0 | 412 | JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "2"); |
michael@0 | 413 | return nullptr; |
michael@0 | 414 | } |
michael@0 | 415 | } |
michael@0 | 416 | } |
michael@0 | 417 | |
michael@0 | 418 | Rooted<JSObject*> proto(cx, nullptr); |
michael@0 | 419 | return fromBuffer(cx, dataObj, byteOffset, length, proto); |
michael@0 | 420 | } |
michael@0 | 421 | |
michael@0 | 422 | static bool IsThisClass(HandleValue v) { |
michael@0 | 423 | return v.isObject() && v.toObject().hasClass(instanceClass()); |
michael@0 | 424 | } |
michael@0 | 425 | |
michael@0 | 426 | template<Value ValueGetter(TypedArrayObject *tarr)> |
michael@0 | 427 | static bool |
michael@0 | 428 | GetterImpl(JSContext *cx, CallArgs args) |
michael@0 | 429 | { |
michael@0 | 430 | JS_ASSERT(IsThisClass(args.thisv())); |
michael@0 | 431 | args.rval().set(ValueGetter(&args.thisv().toObject().as<TypedArrayObject>())); |
michael@0 | 432 | return true; |
michael@0 | 433 | } |
michael@0 | 434 | |
michael@0 | 435 | // ValueGetter is a function that takes an unwrapped typed array object and |
michael@0 | 436 | // returns a Value. Given such a function, Getter<> is a native that |
michael@0 | 437 | // retrieves a given Value, probably from a slot on the object. |
michael@0 | 438 | template<Value ValueGetter(TypedArrayObject *tarr)> |
michael@0 | 439 | static bool |
michael@0 | 440 | Getter(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 441 | { |
michael@0 | 442 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 443 | return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass, |
michael@0 | 444 | ThisTypedArrayObject::GetterImpl<ValueGetter> >(cx, args); |
michael@0 | 445 | } |
michael@0 | 446 | |
michael@0 | 447 | static bool |
michael@0 | 448 | BufferGetterImpl(JSContext *cx, CallArgs args) |
michael@0 | 449 | { |
michael@0 | 450 | JS_ASSERT(IsThisClass(args.thisv())); |
michael@0 | 451 | Rooted<TypedArrayObject *> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>()); |
michael@0 | 452 | if (!ensureHasBuffer(cx, tarray)) |
michael@0 | 453 | return false; |
michael@0 | 454 | args.rval().set(bufferValue(tarray)); |
michael@0 | 455 | return true; |
michael@0 | 456 | } |
michael@0 | 457 | |
michael@0 | 458 | // BufferGetter is a function that lazily constructs the array buffer for a |
michael@0 | 459 | // typed array before fetching it. |
michael@0 | 460 | static bool |
michael@0 | 461 | BufferGetter(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 462 | { |
michael@0 | 463 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 464 | return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass, |
michael@0 | 465 | ThisTypedArrayObject::BufferGetterImpl>(cx, args); |
michael@0 | 466 | } |
michael@0 | 467 | |
michael@0 | 468 | // Define an accessor for a read-only property that invokes a native getter |
michael@0 | 469 | static bool |
michael@0 | 470 | DefineGetter(JSContext *cx, HandleObject proto, PropertyName *name, Native native) |
michael@0 | 471 | { |
michael@0 | 472 | RootedId id(cx, NameToId(name)); |
michael@0 | 473 | unsigned attrs = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT; |
michael@0 | 474 | |
michael@0 | 475 | Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal()); |
michael@0 | 476 | JSObject *getter = NewFunction(cx, NullPtr(), native, 0, |
michael@0 | 477 | JSFunction::NATIVE_FUN, global, NullPtr()); |
michael@0 | 478 | if (!getter) |
michael@0 | 479 | return false; |
michael@0 | 480 | |
michael@0 | 481 | return DefineNativeProperty(cx, proto, id, UndefinedHandleValue, |
michael@0 | 482 | JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr, |
michael@0 | 483 | attrs); |
michael@0 | 484 | } |
michael@0 | 485 | |
michael@0 | 486 | static |
michael@0 | 487 | bool defineGetters(JSContext *cx, HandleObject proto) |
michael@0 | 488 | { |
michael@0 | 489 | if (!DefineGetter(cx, proto, cx->names().length, Getter<lengthValue>)) |
michael@0 | 490 | return false; |
michael@0 | 491 | |
michael@0 | 492 | if (!DefineGetter(cx, proto, cx->names().buffer, BufferGetter)) |
michael@0 | 493 | return false; |
michael@0 | 494 | |
michael@0 | 495 | if (!DefineGetter(cx, proto, cx->names().byteLength, Getter<byteLengthValue>)) |
michael@0 | 496 | return false; |
michael@0 | 497 | |
michael@0 | 498 | if (!DefineGetter(cx, proto, cx->names().byteOffset, Getter<byteOffsetValue>)) |
michael@0 | 499 | return false; |
michael@0 | 500 | |
michael@0 | 501 | return true; |
michael@0 | 502 | } |
michael@0 | 503 | |
michael@0 | 504 | /* subarray(start[, end]) */ |
michael@0 | 505 | static bool |
michael@0 | 506 | fun_subarray_impl(JSContext *cx, CallArgs args) |
michael@0 | 507 | { |
michael@0 | 508 | JS_ASSERT(IsThisClass(args.thisv())); |
michael@0 | 509 | Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>()); |
michael@0 | 510 | |
michael@0 | 511 | // these are the default values |
michael@0 | 512 | uint32_t length = tarray->length(); |
michael@0 | 513 | uint32_t begin = 0, end = length; |
michael@0 | 514 | |
michael@0 | 515 | if (args.length() > 0) { |
michael@0 | 516 | if (!ToClampedIndex(cx, args[0], length, &begin)) |
michael@0 | 517 | return false; |
michael@0 | 518 | |
michael@0 | 519 | if (args.length() > 1) { |
michael@0 | 520 | if (!ToClampedIndex(cx, args[1], length, &end)) |
michael@0 | 521 | return false; |
michael@0 | 522 | } |
michael@0 | 523 | } |
michael@0 | 524 | |
michael@0 | 525 | if (begin > end) |
michael@0 | 526 | begin = end; |
michael@0 | 527 | |
michael@0 | 528 | JSObject *nobj = createSubarray(cx, tarray, begin, end); |
michael@0 | 529 | if (!nobj) |
michael@0 | 530 | return false; |
michael@0 | 531 | args.rval().setObject(*nobj); |
michael@0 | 532 | return true; |
michael@0 | 533 | } |
michael@0 | 534 | |
michael@0 | 535 | static bool |
michael@0 | 536 | fun_subarray(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 537 | { |
michael@0 | 538 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 539 | return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass, |
michael@0 | 540 | ThisTypedArrayObject::fun_subarray_impl>(cx, args); |
michael@0 | 541 | } |
michael@0 | 542 | |
michael@0 | 543 | /* move(begin, end, dest) */ |
michael@0 | 544 | static bool |
michael@0 | 545 | fun_move_impl(JSContext *cx, CallArgs args) |
michael@0 | 546 | { |
michael@0 | 547 | JS_ASSERT(IsThisClass(args.thisv())); |
michael@0 | 548 | Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>()); |
michael@0 | 549 | |
michael@0 | 550 | if (args.length() < 3) { |
michael@0 | 551 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
michael@0 | 552 | return false; |
michael@0 | 553 | } |
michael@0 | 554 | |
michael@0 | 555 | uint32_t srcBegin; |
michael@0 | 556 | uint32_t srcEnd; |
michael@0 | 557 | uint32_t dest; |
michael@0 | 558 | |
michael@0 | 559 | uint32_t originalLength = tarray->length(); |
michael@0 | 560 | if (!ToClampedIndex(cx, args[0], originalLength, &srcBegin) || |
michael@0 | 561 | !ToClampedIndex(cx, args[1], originalLength, &srcEnd) || |
michael@0 | 562 | !ToClampedIndex(cx, args[2], originalLength, &dest)) |
michael@0 | 563 | { |
michael@0 | 564 | return false; |
michael@0 | 565 | } |
michael@0 | 566 | |
michael@0 | 567 | if (srcBegin > srcEnd) { |
michael@0 | 568 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_INDEX); |
michael@0 | 569 | return false; |
michael@0 | 570 | } |
michael@0 | 571 | |
michael@0 | 572 | uint32_t lengthDuringMove = tarray->length(); // beware ToClampedIndex |
michael@0 | 573 | uint32_t nelts = srcEnd - srcBegin; |
michael@0 | 574 | |
michael@0 | 575 | MOZ_ASSERT(dest <= INT32_MAX, "size limited to 2**31"); |
michael@0 | 576 | MOZ_ASSERT(nelts <= INT32_MAX, "size limited to 2**31"); |
michael@0 | 577 | if (dest + nelts > lengthDuringMove || srcEnd > lengthDuringMove) { |
michael@0 | 578 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
michael@0 | 579 | return false; |
michael@0 | 580 | } |
michael@0 | 581 | |
michael@0 | 582 | uint32_t byteDest = dest * sizeof(NativeType); |
michael@0 | 583 | uint32_t byteSrc = srcBegin * sizeof(NativeType); |
michael@0 | 584 | uint32_t byteSize = nelts * sizeof(NativeType); |
michael@0 | 585 | |
michael@0 | 586 | #ifdef DEBUG |
michael@0 | 587 | uint32_t viewByteLength = tarray->byteLength(); |
michael@0 | 588 | JS_ASSERT(byteDest <= viewByteLength); |
michael@0 | 589 | JS_ASSERT(byteSrc <= viewByteLength); |
michael@0 | 590 | JS_ASSERT(byteDest + byteSize <= viewByteLength); |
michael@0 | 591 | JS_ASSERT(byteSrc + byteSize <= viewByteLength); |
michael@0 | 592 | |
michael@0 | 593 | // Should not overflow because size is limited to 2^31 |
michael@0 | 594 | JS_ASSERT(byteDest + byteSize >= byteDest); |
michael@0 | 595 | JS_ASSERT(byteSrc + byteSize >= byteSrc); |
michael@0 | 596 | #endif |
michael@0 | 597 | |
michael@0 | 598 | uint8_t *data = static_cast<uint8_t*>(tarray->viewData()); |
michael@0 | 599 | memmove(&data[byteDest], &data[byteSrc], byteSize); |
michael@0 | 600 | args.rval().setUndefined(); |
michael@0 | 601 | return true; |
michael@0 | 602 | } |
michael@0 | 603 | |
michael@0 | 604 | static bool |
michael@0 | 605 | fun_move(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 606 | { |
michael@0 | 607 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 608 | return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass, |
michael@0 | 609 | ThisTypedArrayObject::fun_move_impl>(cx, args); |
michael@0 | 610 | } |
michael@0 | 611 | |
michael@0 | 612 | /* set(array[, offset]) */ |
michael@0 | 613 | static bool |
michael@0 | 614 | fun_set_impl(JSContext *cx, CallArgs args) |
michael@0 | 615 | { |
michael@0 | 616 | JS_ASSERT(IsThisClass(args.thisv())); |
michael@0 | 617 | Rooted<TypedArrayObject*> tarray(cx, &args.thisv().toObject().as<TypedArrayObject>()); |
michael@0 | 618 | |
michael@0 | 619 | // first arg must be either a typed array or a JS array |
michael@0 | 620 | if (args.length() == 0 || !args[0].isObject()) { |
michael@0 | 621 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
michael@0 | 622 | return false; |
michael@0 | 623 | } |
michael@0 | 624 | |
michael@0 | 625 | int32_t offset = 0; |
michael@0 | 626 | if (args.length() > 1) { |
michael@0 | 627 | if (!ToInt32(cx, args[1], &offset)) |
michael@0 | 628 | return false; |
michael@0 | 629 | |
michael@0 | 630 | if (offset < 0 || uint32_t(offset) > tarray->length()) { |
michael@0 | 631 | // the given offset is bogus |
michael@0 | 632 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
michael@0 | 633 | JSMSG_TYPED_ARRAY_BAD_INDEX, "2"); |
michael@0 | 634 | return false; |
michael@0 | 635 | } |
michael@0 | 636 | } |
michael@0 | 637 | |
michael@0 | 638 | if (!args[0].isObject()) { |
michael@0 | 639 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
michael@0 | 640 | return false; |
michael@0 | 641 | } |
michael@0 | 642 | |
michael@0 | 643 | RootedObject arg0(cx, args[0].toObjectOrNull()); |
michael@0 | 644 | if (arg0->is<TypedArrayObject>()) { |
michael@0 | 645 | if (arg0->as<TypedArrayObject>().length() > tarray->length() - offset) { |
michael@0 | 646 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); |
michael@0 | 647 | return false; |
michael@0 | 648 | } |
michael@0 | 649 | |
michael@0 | 650 | if (!copyFromTypedArray(cx, tarray, arg0, offset)) |
michael@0 | 651 | return false; |
michael@0 | 652 | } else { |
michael@0 | 653 | uint32_t len; |
michael@0 | 654 | if (!GetLengthProperty(cx, arg0, &len)) |
michael@0 | 655 | return false; |
michael@0 | 656 | |
michael@0 | 657 | if (uint32_t(offset) > tarray->length() || len > tarray->length() - offset) { |
michael@0 | 658 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); |
michael@0 | 659 | return false; |
michael@0 | 660 | } |
michael@0 | 661 | |
michael@0 | 662 | if (!copyFromArray(cx, tarray, arg0, len, offset)) |
michael@0 | 663 | return false; |
michael@0 | 664 | } |
michael@0 | 665 | |
michael@0 | 666 | args.rval().setUndefined(); |
michael@0 | 667 | return true; |
michael@0 | 668 | } |
michael@0 | 669 | |
michael@0 | 670 | static bool |
michael@0 | 671 | fun_set(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 672 | { |
michael@0 | 673 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 674 | return CallNonGenericMethod<ThisTypedArrayObject::IsThisClass, |
michael@0 | 675 | ThisTypedArrayObject::fun_set_impl>(cx, args); |
michael@0 | 676 | } |
michael@0 | 677 | |
michael@0 | 678 | public: |
michael@0 | 679 | static JSObject * |
michael@0 | 680 | fromBuffer(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt, |
michael@0 | 681 | HandleObject proto) |
michael@0 | 682 | { |
michael@0 | 683 | if (!ObjectClassIs(bufobj, ESClass_ArrayBuffer, cx)) { |
michael@0 | 684 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
michael@0 | 685 | return nullptr; // must be arrayBuffer |
michael@0 | 686 | } |
michael@0 | 687 | |
michael@0 | 688 | JS_ASSERT(IsArrayBuffer(bufobj) || bufobj->is<ProxyObject>()); |
michael@0 | 689 | if (bufobj->is<ProxyObject>()) { |
michael@0 | 690 | /* |
michael@0 | 691 | * Normally, NonGenericMethodGuard handles the case of transparent |
michael@0 | 692 | * wrappers. However, we have a peculiar situation: we want to |
michael@0 | 693 | * construct the new typed array in the compartment of the buffer, |
michael@0 | 694 | * so that the typed array can point directly at their buffer's |
michael@0 | 695 | * data without crossing compartment boundaries. So we use the |
michael@0 | 696 | * machinery underlying NonGenericMethodGuard directly to proxy the |
michael@0 | 697 | * native call. We will end up with a wrapper in the origin |
michael@0 | 698 | * compartment for a view in the target compartment referencing the |
michael@0 | 699 | * ArrayBufferObject in that same compartment. |
michael@0 | 700 | */ |
michael@0 | 701 | JSObject *wrapped = CheckedUnwrap(bufobj); |
michael@0 | 702 | if (!wrapped) { |
michael@0 | 703 | JS_ReportError(cx, "Permission denied to access object"); |
michael@0 | 704 | return nullptr; |
michael@0 | 705 | } |
michael@0 | 706 | if (IsArrayBuffer(wrapped)) { |
michael@0 | 707 | /* |
michael@0 | 708 | * And for even more fun, the new view's prototype should be |
michael@0 | 709 | * set to the origin compartment's prototype object, not the |
michael@0 | 710 | * target's (specifically, the actual view in the target |
michael@0 | 711 | * compartment will use as its prototype a wrapper around the |
michael@0 | 712 | * origin compartment's view.prototype object). |
michael@0 | 713 | * |
michael@0 | 714 | * Rather than hack some crazy solution together, implement |
michael@0 | 715 | * this all using a private helper function, created when |
michael@0 | 716 | * ArrayBufferObject was initialized and cached in the global. |
michael@0 | 717 | * This reuses all the existing cross-compartment crazy so we |
michael@0 | 718 | * don't have to do anything *uniquely* crazy here. |
michael@0 | 719 | */ |
michael@0 | 720 | |
michael@0 | 721 | Rooted<JSObject*> proto(cx); |
michael@0 | 722 | if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &proto)) |
michael@0 | 723 | return nullptr; |
michael@0 | 724 | |
michael@0 | 725 | InvokeArgs args(cx); |
michael@0 | 726 | if (!args.init(3)) |
michael@0 | 727 | return nullptr; |
michael@0 | 728 | |
michael@0 | 729 | args.setCallee(cx->compartment()->maybeGlobal()->createArrayFromBuffer<NativeType>()); |
michael@0 | 730 | args.setThis(ObjectValue(*bufobj)); |
michael@0 | 731 | args[0].setNumber(byteOffset); |
michael@0 | 732 | args[1].setInt32(lengthInt); |
michael@0 | 733 | args[2].setObject(*proto); |
michael@0 | 734 | |
michael@0 | 735 | if (!Invoke(cx, args)) |
michael@0 | 736 | return nullptr; |
michael@0 | 737 | return &args.rval().toObject(); |
michael@0 | 738 | } |
michael@0 | 739 | } |
michael@0 | 740 | |
michael@0 | 741 | if (!IsArrayBuffer(bufobj)) { |
michael@0 | 742 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
michael@0 | 743 | return nullptr; // must be arrayBuffer |
michael@0 | 744 | } |
michael@0 | 745 | |
michael@0 | 746 | Rooted<ArrayBufferObject *> buffer(cx, &AsArrayBuffer(bufobj)); |
michael@0 | 747 | |
michael@0 | 748 | if (byteOffset > buffer->byteLength() || byteOffset % sizeof(NativeType) != 0) { |
michael@0 | 749 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
michael@0 | 750 | return nullptr; // invalid byteOffset |
michael@0 | 751 | } |
michael@0 | 752 | |
michael@0 | 753 | uint32_t len; |
michael@0 | 754 | if (lengthInt == -1) { |
michael@0 | 755 | len = (buffer->byteLength() - byteOffset) / sizeof(NativeType); |
michael@0 | 756 | if (len * sizeof(NativeType) != buffer->byteLength() - byteOffset) { |
michael@0 | 757 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
michael@0 | 758 | JSMSG_TYPED_ARRAY_BAD_ARGS); |
michael@0 | 759 | return nullptr; // given byte array doesn't map exactly to sizeof(NativeType) * N |
michael@0 | 760 | } |
michael@0 | 761 | } else { |
michael@0 | 762 | len = uint32_t(lengthInt); |
michael@0 | 763 | } |
michael@0 | 764 | |
michael@0 | 765 | // Go slowly and check for overflow. |
michael@0 | 766 | uint32_t arrayByteLength = len * sizeof(NativeType); |
michael@0 | 767 | if (len >= INT32_MAX / sizeof(NativeType) || byteOffset >= INT32_MAX - arrayByteLength) { |
michael@0 | 768 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
michael@0 | 769 | return nullptr; // overflow when calculating byteOffset + len * sizeof(NativeType) |
michael@0 | 770 | } |
michael@0 | 771 | |
michael@0 | 772 | if (arrayByteLength + byteOffset > buffer->byteLength()) { |
michael@0 | 773 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
michael@0 | 774 | return nullptr; // byteOffset + len is too big for the arraybuffer |
michael@0 | 775 | } |
michael@0 | 776 | |
michael@0 | 777 | return makeInstance(cx, buffer, byteOffset, len, proto); |
michael@0 | 778 | } |
michael@0 | 779 | |
michael@0 | 780 | static bool |
michael@0 | 781 | maybeCreateArrayBuffer(JSContext *cx, uint32_t nelements, MutableHandle<ArrayBufferObject *> buffer) |
michael@0 | 782 | { |
michael@0 | 783 | // Make sure that array elements evenly divide into the inline buffer's |
michael@0 | 784 | // size, for the test below. |
michael@0 | 785 | JS_STATIC_ASSERT((INLINE_BUFFER_LIMIT / sizeof(NativeType)) * sizeof(NativeType) == INLINE_BUFFER_LIMIT); |
michael@0 | 786 | |
michael@0 | 787 | if (nelements <= INLINE_BUFFER_LIMIT / sizeof(NativeType)) { |
michael@0 | 788 | // The array's data can be inline, and the buffer created lazily. |
michael@0 | 789 | return true; |
michael@0 | 790 | } |
michael@0 | 791 | |
michael@0 | 792 | if (nelements >= INT32_MAX / sizeof(NativeType)) { |
michael@0 | 793 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
michael@0 | 794 | JSMSG_NEED_DIET, "size and count"); |
michael@0 | 795 | return false; |
michael@0 | 796 | } |
michael@0 | 797 | |
michael@0 | 798 | buffer.set(ArrayBufferObject::create(cx, nelements * sizeof(NativeType))); |
michael@0 | 799 | return !!buffer; |
michael@0 | 800 | } |
michael@0 | 801 | |
michael@0 | 802 | static JSObject * |
michael@0 | 803 | fromLength(JSContext *cx, uint32_t nelements) |
michael@0 | 804 | { |
michael@0 | 805 | Rooted<ArrayBufferObject *> buffer(cx); |
michael@0 | 806 | if (!maybeCreateArrayBuffer(cx, nelements, &buffer)) |
michael@0 | 807 | return nullptr; |
michael@0 | 808 | return makeInstance(cx, buffer, 0, nelements); |
michael@0 | 809 | } |
michael@0 | 810 | |
michael@0 | 811 | static JSObject * |
michael@0 | 812 | fromArray(JSContext *cx, HandleObject other) |
michael@0 | 813 | { |
michael@0 | 814 | uint32_t len; |
michael@0 | 815 | if (other->is<TypedArrayObject>()) { |
michael@0 | 816 | len = other->as<TypedArrayObject>().length(); |
michael@0 | 817 | } else if (!GetLengthProperty(cx, other, &len)) { |
michael@0 | 818 | return nullptr; |
michael@0 | 819 | } |
michael@0 | 820 | |
michael@0 | 821 | Rooted<ArrayBufferObject *> buffer(cx); |
michael@0 | 822 | if (!maybeCreateArrayBuffer(cx, len, &buffer)) |
michael@0 | 823 | return nullptr; |
michael@0 | 824 | |
michael@0 | 825 | RootedObject obj(cx, makeInstance(cx, buffer, 0, len)); |
michael@0 | 826 | if (!obj || !copyFromArray(cx, obj, other, len)) |
michael@0 | 827 | return nullptr; |
michael@0 | 828 | return obj; |
michael@0 | 829 | } |
michael@0 | 830 | |
michael@0 | 831 | static const NativeType |
michael@0 | 832 | getIndex(JSObject *obj, uint32_t index) |
michael@0 | 833 | { |
michael@0 | 834 | TypedArrayObject &tarray = obj->as<TypedArrayObject>(); |
michael@0 | 835 | MOZ_ASSERT(index < tarray.length()); |
michael@0 | 836 | return static_cast<const NativeType*>(tarray.viewData())[index]; |
michael@0 | 837 | } |
michael@0 | 838 | |
michael@0 | 839 | static void |
michael@0 | 840 | setIndex(TypedArrayObject &tarray, uint32_t index, NativeType val) |
michael@0 | 841 | { |
michael@0 | 842 | MOZ_ASSERT(index < tarray.length()); |
michael@0 | 843 | static_cast<NativeType*>(tarray.viewData())[index] = val; |
michael@0 | 844 | } |
michael@0 | 845 | |
michael@0 | 846 | static Value getIndexValue(JSObject *tarray, uint32_t index); |
michael@0 | 847 | |
michael@0 | 848 | static JSObject * |
michael@0 | 849 | createSubarray(JSContext *cx, HandleObject tarrayArg, uint32_t begin, uint32_t end) |
michael@0 | 850 | { |
michael@0 | 851 | Rooted<TypedArrayObject*> tarray(cx, &tarrayArg->as<TypedArrayObject>()); |
michael@0 | 852 | |
michael@0 | 853 | if (begin > tarray->length() || end > tarray->length() || begin > end) { |
michael@0 | 854 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_INDEX); |
michael@0 | 855 | return nullptr; |
michael@0 | 856 | } |
michael@0 | 857 | |
michael@0 | 858 | if (!ensureHasBuffer(cx, tarray)) |
michael@0 | 859 | return nullptr; |
michael@0 | 860 | |
michael@0 | 861 | Rooted<ArrayBufferObject *> bufobj(cx, tarray->buffer()); |
michael@0 | 862 | JS_ASSERT(bufobj); |
michael@0 | 863 | |
michael@0 | 864 | uint32_t length = end - begin; |
michael@0 | 865 | |
michael@0 | 866 | JS_ASSERT(begin < UINT32_MAX / sizeof(NativeType)); |
michael@0 | 867 | uint32_t arrayByteOffset = tarray->byteOffset(); |
michael@0 | 868 | JS_ASSERT(UINT32_MAX - begin * sizeof(NativeType) >= arrayByteOffset); |
michael@0 | 869 | uint32_t byteOffset = arrayByteOffset + begin * sizeof(NativeType); |
michael@0 | 870 | |
michael@0 | 871 | return makeInstance(cx, bufobj, byteOffset, length); |
michael@0 | 872 | } |
michael@0 | 873 | |
michael@0 | 874 | protected: |
michael@0 | 875 | static NativeType |
michael@0 | 876 | doubleToNative(double d) |
michael@0 | 877 | { |
michael@0 | 878 | if (TypeIsFloatingPoint<NativeType>()) { |
michael@0 | 879 | #ifdef JS_MORE_DETERMINISTIC |
michael@0 | 880 | // The JS spec doesn't distinguish among different NaN values, and |
michael@0 | 881 | // it deliberately doesn't specify the bit pattern written to a |
michael@0 | 882 | // typed array when NaN is written into it. This bit-pattern |
michael@0 | 883 | // inconsistency could confuse deterministic testing, so always |
michael@0 | 884 | // canonicalize NaN values in more-deterministic builds. |
michael@0 | 885 | d = CanonicalizeNaN(d); |
michael@0 | 886 | #endif |
michael@0 | 887 | return NativeType(d); |
michael@0 | 888 | } |
michael@0 | 889 | if (MOZ_UNLIKELY(IsNaN(d))) |
michael@0 | 890 | return NativeType(0); |
michael@0 | 891 | if (TypeIsUnsigned<NativeType>()) |
michael@0 | 892 | return NativeType(ToUint32(d)); |
michael@0 | 893 | return NativeType(ToInt32(d)); |
michael@0 | 894 | } |
michael@0 | 895 | |
michael@0 | 896 | static bool |
michael@0 | 897 | canConvertInfallibly(const Value &v) |
michael@0 | 898 | { |
michael@0 | 899 | return v.isNumber() || v.isBoolean() || v.isNull() || v.isUndefined(); |
michael@0 | 900 | } |
michael@0 | 901 | |
michael@0 | 902 | static NativeType |
michael@0 | 903 | infallibleValueToNative(const Value &v) |
michael@0 | 904 | { |
michael@0 | 905 | if (v.isInt32()) |
michael@0 | 906 | return v.toInt32(); |
michael@0 | 907 | if (v.isDouble()) |
michael@0 | 908 | return doubleToNative(v.toDouble()); |
michael@0 | 909 | if (v.isBoolean()) |
michael@0 | 910 | return v.toBoolean(); |
michael@0 | 911 | if (v.isNull()) |
michael@0 | 912 | return 0; |
michael@0 | 913 | |
michael@0 | 914 | MOZ_ASSERT(v.isUndefined()); |
michael@0 | 915 | return ArrayTypeIsFloatingPoint() ? NativeType(GenericNaN()) : NativeType(0); |
michael@0 | 916 | } |
michael@0 | 917 | |
michael@0 | 918 | static bool |
michael@0 | 919 | valueToNative(JSContext *cx, const Value &v, NativeType *result) |
michael@0 | 920 | { |
michael@0 | 921 | MOZ_ASSERT(!v.isMagic()); |
michael@0 | 922 | |
michael@0 | 923 | if (MOZ_LIKELY(canConvertInfallibly(v))) { |
michael@0 | 924 | *result = infallibleValueToNative(v); |
michael@0 | 925 | return true; |
michael@0 | 926 | } |
michael@0 | 927 | |
michael@0 | 928 | double d; |
michael@0 | 929 | MOZ_ASSERT(v.isString() || v.isObject()); |
michael@0 | 930 | if (!(v.isString() ? StringToNumber(cx, v.toString(), &d) : ToNumber(cx, v, &d))) |
michael@0 | 931 | return false; |
michael@0 | 932 | |
michael@0 | 933 | *result = doubleToNative(d); |
michael@0 | 934 | return true; |
michael@0 | 935 | } |
michael@0 | 936 | |
michael@0 | 937 | static bool |
michael@0 | 938 | copyFromArray(JSContext *cx, HandleObject thisTypedArrayObj, |
michael@0 | 939 | HandleObject source, uint32_t len, uint32_t offset = 0) |
michael@0 | 940 | { |
michael@0 | 941 | Rooted<TypedArrayObject*> thisTypedArray(cx, &thisTypedArrayObj->as<TypedArrayObject>()); |
michael@0 | 942 | JS_ASSERT(offset <= thisTypedArray->length()); |
michael@0 | 943 | JS_ASSERT(len <= thisTypedArray->length() - offset); |
michael@0 | 944 | if (source->is<TypedArrayObject>()) |
michael@0 | 945 | return copyFromTypedArray(cx, thisTypedArray, source, offset); |
michael@0 | 946 | |
michael@0 | 947 | uint32_t i = 0; |
michael@0 | 948 | if (source->isNative()) { |
michael@0 | 949 | // Attempt fast-path infallible conversion of dense elements up to |
michael@0 | 950 | // the first potentially side-effectful lookup or conversion. |
michael@0 | 951 | uint32_t bound = Min(source->getDenseInitializedLength(), len); |
michael@0 | 952 | |
michael@0 | 953 | NativeType *dest = static_cast<NativeType*>(thisTypedArray->viewData()) + offset; |
michael@0 | 954 | |
michael@0 | 955 | const Value *srcValues = source->getDenseElements(); |
michael@0 | 956 | for (; i < bound; i++) { |
michael@0 | 957 | // Note: holes don't convert infallibly. |
michael@0 | 958 | if (!canConvertInfallibly(srcValues[i])) |
michael@0 | 959 | break; |
michael@0 | 960 | dest[i] = infallibleValueToNative(srcValues[i]); |
michael@0 | 961 | } |
michael@0 | 962 | if (i == len) |
michael@0 | 963 | return true; |
michael@0 | 964 | } |
michael@0 | 965 | |
michael@0 | 966 | // Convert and copy any remaining elements generically. |
michael@0 | 967 | RootedValue v(cx); |
michael@0 | 968 | for (; i < len; i++) { |
michael@0 | 969 | if (!JSObject::getElement(cx, source, source, i, &v)) |
michael@0 | 970 | return false; |
michael@0 | 971 | |
michael@0 | 972 | NativeType n; |
michael@0 | 973 | if (!valueToNative(cx, v, &n)) |
michael@0 | 974 | return false; |
michael@0 | 975 | |
michael@0 | 976 | len = Min(len, thisTypedArray->length()); |
michael@0 | 977 | if (i >= len) |
michael@0 | 978 | break; |
michael@0 | 979 | |
michael@0 | 980 | // Compute every iteration in case getElement acts wacky. |
michael@0 | 981 | void *data = thisTypedArray->viewData(); |
michael@0 | 982 | static_cast<NativeType*>(data)[offset + i] = n; |
michael@0 | 983 | } |
michael@0 | 984 | |
michael@0 | 985 | return true; |
michael@0 | 986 | } |
michael@0 | 987 | |
michael@0 | 988 | static bool |
michael@0 | 989 | copyFromTypedArray(JSContext *cx, JSObject *thisTypedArrayObj, JSObject *tarrayObj, |
michael@0 | 990 | uint32_t offset) |
michael@0 | 991 | { |
michael@0 | 992 | TypedArrayObject *thisTypedArray = &thisTypedArrayObj->as<TypedArrayObject>(); |
michael@0 | 993 | TypedArrayObject *tarray = &tarrayObj->as<TypedArrayObject>(); |
michael@0 | 994 | JS_ASSERT(offset <= thisTypedArray->length()); |
michael@0 | 995 | JS_ASSERT(tarray->length() <= thisTypedArray->length() - offset); |
michael@0 | 996 | if (tarray->buffer() == thisTypedArray->buffer()) |
michael@0 | 997 | return copyFromWithOverlap(cx, thisTypedArray, tarray, offset); |
michael@0 | 998 | |
michael@0 | 999 | NativeType *dest = static_cast<NativeType*>(thisTypedArray->viewData()) + offset; |
michael@0 | 1000 | |
michael@0 | 1001 | if (tarray->type() == thisTypedArray->type()) { |
michael@0 | 1002 | js_memcpy(dest, tarray->viewData(), tarray->byteLength()); |
michael@0 | 1003 | return true; |
michael@0 | 1004 | } |
michael@0 | 1005 | |
michael@0 | 1006 | unsigned srclen = tarray->length(); |
michael@0 | 1007 | switch (tarray->type()) { |
michael@0 | 1008 | case ScalarTypeDescr::TYPE_INT8: { |
michael@0 | 1009 | int8_t *src = static_cast<int8_t*>(tarray->viewData()); |
michael@0 | 1010 | for (unsigned i = 0; i < srclen; ++i) |
michael@0 | 1011 | *dest++ = NativeType(*src++); |
michael@0 | 1012 | break; |
michael@0 | 1013 | } |
michael@0 | 1014 | case ScalarTypeDescr::TYPE_UINT8: |
michael@0 | 1015 | case ScalarTypeDescr::TYPE_UINT8_CLAMPED: { |
michael@0 | 1016 | uint8_t *src = static_cast<uint8_t*>(tarray->viewData()); |
michael@0 | 1017 | for (unsigned i = 0; i < srclen; ++i) |
michael@0 | 1018 | *dest++ = NativeType(*src++); |
michael@0 | 1019 | break; |
michael@0 | 1020 | } |
michael@0 | 1021 | case ScalarTypeDescr::TYPE_INT16: { |
michael@0 | 1022 | int16_t *src = static_cast<int16_t*>(tarray->viewData()); |
michael@0 | 1023 | for (unsigned i = 0; i < srclen; ++i) |
michael@0 | 1024 | *dest++ = NativeType(*src++); |
michael@0 | 1025 | break; |
michael@0 | 1026 | } |
michael@0 | 1027 | case ScalarTypeDescr::TYPE_UINT16: { |
michael@0 | 1028 | uint16_t *src = static_cast<uint16_t*>(tarray->viewData()); |
michael@0 | 1029 | for (unsigned i = 0; i < srclen; ++i) |
michael@0 | 1030 | *dest++ = NativeType(*src++); |
michael@0 | 1031 | break; |
michael@0 | 1032 | } |
michael@0 | 1033 | case ScalarTypeDescr::TYPE_INT32: { |
michael@0 | 1034 | int32_t *src = static_cast<int32_t*>(tarray->viewData()); |
michael@0 | 1035 | for (unsigned i = 0; i < srclen; ++i) |
michael@0 | 1036 | *dest++ = NativeType(*src++); |
michael@0 | 1037 | break; |
michael@0 | 1038 | } |
michael@0 | 1039 | case ScalarTypeDescr::TYPE_UINT32: { |
michael@0 | 1040 | uint32_t *src = static_cast<uint32_t*>(tarray->viewData()); |
michael@0 | 1041 | for (unsigned i = 0; i < srclen; ++i) |
michael@0 | 1042 | *dest++ = NativeType(*src++); |
michael@0 | 1043 | break; |
michael@0 | 1044 | } |
michael@0 | 1045 | case ScalarTypeDescr::TYPE_FLOAT32: { |
michael@0 | 1046 | float *src = static_cast<float*>(tarray->viewData()); |
michael@0 | 1047 | for (unsigned i = 0; i < srclen; ++i) |
michael@0 | 1048 | *dest++ = NativeType(*src++); |
michael@0 | 1049 | break; |
michael@0 | 1050 | } |
michael@0 | 1051 | case ScalarTypeDescr::TYPE_FLOAT64: { |
michael@0 | 1052 | double *src = static_cast<double*>(tarray->viewData()); |
michael@0 | 1053 | for (unsigned i = 0; i < srclen; ++i) |
michael@0 | 1054 | *dest++ = NativeType(*src++); |
michael@0 | 1055 | break; |
michael@0 | 1056 | } |
michael@0 | 1057 | default: |
michael@0 | 1058 | MOZ_ASSUME_UNREACHABLE("copyFrom with a TypedArrayObject of unknown type"); |
michael@0 | 1059 | } |
michael@0 | 1060 | |
michael@0 | 1061 | return true; |
michael@0 | 1062 | } |
michael@0 | 1063 | |
michael@0 | 1064 | static bool |
michael@0 | 1065 | copyFromWithOverlap(JSContext *cx, JSObject *selfObj, JSObject *tarrayObj, uint32_t offset) |
michael@0 | 1066 | { |
michael@0 | 1067 | TypedArrayObject *self = &selfObj->as<TypedArrayObject>(); |
michael@0 | 1068 | TypedArrayObject *tarray = &tarrayObj->as<TypedArrayObject>(); |
michael@0 | 1069 | |
michael@0 | 1070 | JS_ASSERT(offset <= self->length()); |
michael@0 | 1071 | |
michael@0 | 1072 | NativeType *dest = static_cast<NativeType*>(self->viewData()) + offset; |
michael@0 | 1073 | uint32_t byteLength = tarray->byteLength(); |
michael@0 | 1074 | |
michael@0 | 1075 | if (tarray->type() == self->type()) { |
michael@0 | 1076 | memmove(dest, tarray->viewData(), byteLength); |
michael@0 | 1077 | return true; |
michael@0 | 1078 | } |
michael@0 | 1079 | |
michael@0 | 1080 | // We have to make a copy of the source array here, since |
michael@0 | 1081 | // there's overlap, and we have to convert types. |
michael@0 | 1082 | void *srcbuf = cx->malloc_(byteLength); |
michael@0 | 1083 | if (!srcbuf) |
michael@0 | 1084 | return false; |
michael@0 | 1085 | js_memcpy(srcbuf, tarray->viewData(), byteLength); |
michael@0 | 1086 | |
michael@0 | 1087 | uint32_t len = tarray->length(); |
michael@0 | 1088 | switch (tarray->type()) { |
michael@0 | 1089 | case ScalarTypeDescr::TYPE_INT8: { |
michael@0 | 1090 | int8_t *src = (int8_t*) srcbuf; |
michael@0 | 1091 | for (unsigned i = 0; i < len; ++i) |
michael@0 | 1092 | *dest++ = NativeType(*src++); |
michael@0 | 1093 | break; |
michael@0 | 1094 | } |
michael@0 | 1095 | case ScalarTypeDescr::TYPE_UINT8: |
michael@0 | 1096 | case ScalarTypeDescr::TYPE_UINT8_CLAMPED: { |
michael@0 | 1097 | uint8_t *src = (uint8_t*) srcbuf; |
michael@0 | 1098 | for (unsigned i = 0; i < len; ++i) |
michael@0 | 1099 | *dest++ = NativeType(*src++); |
michael@0 | 1100 | break; |
michael@0 | 1101 | } |
michael@0 | 1102 | case ScalarTypeDescr::TYPE_INT16: { |
michael@0 | 1103 | int16_t *src = (int16_t*) srcbuf; |
michael@0 | 1104 | for (unsigned i = 0; i < len; ++i) |
michael@0 | 1105 | *dest++ = NativeType(*src++); |
michael@0 | 1106 | break; |
michael@0 | 1107 | } |
michael@0 | 1108 | case ScalarTypeDescr::TYPE_UINT16: { |
michael@0 | 1109 | uint16_t *src = (uint16_t*) srcbuf; |
michael@0 | 1110 | for (unsigned i = 0; i < len; ++i) |
michael@0 | 1111 | *dest++ = NativeType(*src++); |
michael@0 | 1112 | break; |
michael@0 | 1113 | } |
michael@0 | 1114 | case ScalarTypeDescr::TYPE_INT32: { |
michael@0 | 1115 | int32_t *src = (int32_t*) srcbuf; |
michael@0 | 1116 | for (unsigned i = 0; i < len; ++i) |
michael@0 | 1117 | *dest++ = NativeType(*src++); |
michael@0 | 1118 | break; |
michael@0 | 1119 | } |
michael@0 | 1120 | case ScalarTypeDescr::TYPE_UINT32: { |
michael@0 | 1121 | uint32_t *src = (uint32_t*) srcbuf; |
michael@0 | 1122 | for (unsigned i = 0; i < len; ++i) |
michael@0 | 1123 | *dest++ = NativeType(*src++); |
michael@0 | 1124 | break; |
michael@0 | 1125 | } |
michael@0 | 1126 | case ScalarTypeDescr::TYPE_FLOAT32: { |
michael@0 | 1127 | float *src = (float*) srcbuf; |
michael@0 | 1128 | for (unsigned i = 0; i < len; ++i) |
michael@0 | 1129 | *dest++ = NativeType(*src++); |
michael@0 | 1130 | break; |
michael@0 | 1131 | } |
michael@0 | 1132 | case ScalarTypeDescr::TYPE_FLOAT64: { |
michael@0 | 1133 | double *src = (double*) srcbuf; |
michael@0 | 1134 | for (unsigned i = 0; i < len; ++i) |
michael@0 | 1135 | *dest++ = NativeType(*src++); |
michael@0 | 1136 | break; |
michael@0 | 1137 | } |
michael@0 | 1138 | default: |
michael@0 | 1139 | MOZ_ASSUME_UNREACHABLE("copyFromWithOverlap with a TypedArrayObject of unknown type"); |
michael@0 | 1140 | } |
michael@0 | 1141 | |
michael@0 | 1142 | js_free(srcbuf); |
michael@0 | 1143 | return true; |
michael@0 | 1144 | } |
michael@0 | 1145 | }; |
michael@0 | 1146 | |
michael@0 | 1147 | class Int8ArrayObject : public TypedArrayObjectTemplate<int8_t> { |
michael@0 | 1148 | public: |
michael@0 | 1149 | enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_INT8 }; |
michael@0 | 1150 | static const JSProtoKey key = JSProto_Int8Array; |
michael@0 | 1151 | static const JSFunctionSpec jsfuncs[]; |
michael@0 | 1152 | }; |
michael@0 | 1153 | class Uint8ArrayObject : public TypedArrayObjectTemplate<uint8_t> { |
michael@0 | 1154 | public: |
michael@0 | 1155 | enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_UINT8 }; |
michael@0 | 1156 | static const JSProtoKey key = JSProto_Uint8Array; |
michael@0 | 1157 | static const JSFunctionSpec jsfuncs[]; |
michael@0 | 1158 | }; |
michael@0 | 1159 | class Int16ArrayObject : public TypedArrayObjectTemplate<int16_t> { |
michael@0 | 1160 | public: |
michael@0 | 1161 | enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_INT16 }; |
michael@0 | 1162 | static const JSProtoKey key = JSProto_Int16Array; |
michael@0 | 1163 | static const JSFunctionSpec jsfuncs[]; |
michael@0 | 1164 | }; |
michael@0 | 1165 | class Uint16ArrayObject : public TypedArrayObjectTemplate<uint16_t> { |
michael@0 | 1166 | public: |
michael@0 | 1167 | enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_UINT16 }; |
michael@0 | 1168 | static const JSProtoKey key = JSProto_Uint16Array; |
michael@0 | 1169 | static const JSFunctionSpec jsfuncs[]; |
michael@0 | 1170 | }; |
michael@0 | 1171 | class Int32ArrayObject : public TypedArrayObjectTemplate<int32_t> { |
michael@0 | 1172 | public: |
michael@0 | 1173 | enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_INT32 }; |
michael@0 | 1174 | static const JSProtoKey key = JSProto_Int32Array; |
michael@0 | 1175 | static const JSFunctionSpec jsfuncs[]; |
michael@0 | 1176 | }; |
michael@0 | 1177 | class Uint32ArrayObject : public TypedArrayObjectTemplate<uint32_t> { |
michael@0 | 1178 | public: |
michael@0 | 1179 | enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_UINT32 }; |
michael@0 | 1180 | static const JSProtoKey key = JSProto_Uint32Array; |
michael@0 | 1181 | static const JSFunctionSpec jsfuncs[]; |
michael@0 | 1182 | }; |
michael@0 | 1183 | class Float32ArrayObject : public TypedArrayObjectTemplate<float> { |
michael@0 | 1184 | public: |
michael@0 | 1185 | enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_FLOAT32 }; |
michael@0 | 1186 | static const JSProtoKey key = JSProto_Float32Array; |
michael@0 | 1187 | static const JSFunctionSpec jsfuncs[]; |
michael@0 | 1188 | }; |
michael@0 | 1189 | class Float64ArrayObject : public TypedArrayObjectTemplate<double> { |
michael@0 | 1190 | public: |
michael@0 | 1191 | enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_FLOAT64 }; |
michael@0 | 1192 | static const JSProtoKey key = JSProto_Float64Array; |
michael@0 | 1193 | static const JSFunctionSpec jsfuncs[]; |
michael@0 | 1194 | }; |
michael@0 | 1195 | class Uint8ClampedArrayObject : public TypedArrayObjectTemplate<uint8_clamped> { |
michael@0 | 1196 | public: |
michael@0 | 1197 | enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_UINT8_CLAMPED }; |
michael@0 | 1198 | static const JSProtoKey key = JSProto_Uint8ClampedArray; |
michael@0 | 1199 | static const JSFunctionSpec jsfuncs[]; |
michael@0 | 1200 | }; |
michael@0 | 1201 | |
michael@0 | 1202 | } /* anonymous namespace */ |
michael@0 | 1203 | |
michael@0 | 1204 | template<typename T> |
michael@0 | 1205 | bool |
michael@0 | 1206 | ArrayBufferObject::createTypedArrayFromBufferImpl(JSContext *cx, CallArgs args) |
michael@0 | 1207 | { |
michael@0 | 1208 | typedef TypedArrayObjectTemplate<T> ArrayType; |
michael@0 | 1209 | JS_ASSERT(IsArrayBuffer(args.thisv())); |
michael@0 | 1210 | JS_ASSERT(args.length() == 3); |
michael@0 | 1211 | |
michael@0 | 1212 | Rooted<JSObject*> buffer(cx, &args.thisv().toObject()); |
michael@0 | 1213 | Rooted<JSObject*> proto(cx, &args[2].toObject()); |
michael@0 | 1214 | |
michael@0 | 1215 | Rooted<JSObject*> obj(cx); |
michael@0 | 1216 | double byteOffset = args[0].toNumber(); |
michael@0 | 1217 | MOZ_ASSERT(0 <= byteOffset); |
michael@0 | 1218 | MOZ_ASSERT(byteOffset <= UINT32_MAX); |
michael@0 | 1219 | MOZ_ASSERT(byteOffset == uint32_t(byteOffset)); |
michael@0 | 1220 | obj = ArrayType::fromBuffer(cx, buffer, uint32_t(byteOffset), args[1].toInt32(), proto); |
michael@0 | 1221 | if (!obj) |
michael@0 | 1222 | return false; |
michael@0 | 1223 | args.rval().setObject(*obj); |
michael@0 | 1224 | return true; |
michael@0 | 1225 | } |
michael@0 | 1226 | |
michael@0 | 1227 | template<typename T> |
michael@0 | 1228 | bool |
michael@0 | 1229 | ArrayBufferObject::createTypedArrayFromBuffer(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1230 | { |
michael@0 | 1231 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1232 | return CallNonGenericMethod<IsArrayBuffer, createTypedArrayFromBufferImpl<T> >(cx, args); |
michael@0 | 1233 | } |
michael@0 | 1234 | |
michael@0 | 1235 | // this default implementation is only valid for integer types |
michael@0 | 1236 | // less than 32-bits in size. |
michael@0 | 1237 | template<typename NativeType> |
michael@0 | 1238 | Value |
michael@0 | 1239 | TypedArrayObjectTemplate<NativeType>::getIndexValue(JSObject *tarray, uint32_t index) |
michael@0 | 1240 | { |
michael@0 | 1241 | JS_STATIC_ASSERT(sizeof(NativeType) < 4); |
michael@0 | 1242 | |
michael@0 | 1243 | return Int32Value(getIndex(tarray, index)); |
michael@0 | 1244 | } |
michael@0 | 1245 | |
michael@0 | 1246 | namespace { |
michael@0 | 1247 | |
michael@0 | 1248 | // and we need to specialize for 32-bit integers and floats |
michael@0 | 1249 | template<> |
michael@0 | 1250 | Value |
michael@0 | 1251 | TypedArrayObjectTemplate<int32_t>::getIndexValue(JSObject *tarray, uint32_t index) |
michael@0 | 1252 | { |
michael@0 | 1253 | return Int32Value(getIndex(tarray, index)); |
michael@0 | 1254 | } |
michael@0 | 1255 | |
michael@0 | 1256 | template<> |
michael@0 | 1257 | Value |
michael@0 | 1258 | TypedArrayObjectTemplate<uint32_t>::getIndexValue(JSObject *tarray, uint32_t index) |
michael@0 | 1259 | { |
michael@0 | 1260 | uint32_t val = getIndex(tarray, index); |
michael@0 | 1261 | return NumberValue(val); |
michael@0 | 1262 | } |
michael@0 | 1263 | |
michael@0 | 1264 | template<> |
michael@0 | 1265 | Value |
michael@0 | 1266 | TypedArrayObjectTemplate<float>::getIndexValue(JSObject *tarray, uint32_t index) |
michael@0 | 1267 | { |
michael@0 | 1268 | float val = getIndex(tarray, index); |
michael@0 | 1269 | double dval = val; |
michael@0 | 1270 | |
michael@0 | 1271 | /* |
michael@0 | 1272 | * Doubles in typed arrays could be typed-punned arrays of integers. This |
michael@0 | 1273 | * could allow user code to break the engine-wide invariant that only |
michael@0 | 1274 | * canonical nans are stored into jsvals, which means user code could |
michael@0 | 1275 | * confuse the engine into interpreting a double-typed jsval as an |
michael@0 | 1276 | * object-typed jsval. |
michael@0 | 1277 | * |
michael@0 | 1278 | * This could be removed for platforms/compilers known to convert a 32-bit |
michael@0 | 1279 | * non-canonical nan to a 64-bit canonical nan. |
michael@0 | 1280 | */ |
michael@0 | 1281 | return DoubleValue(CanonicalizeNaN(dval)); |
michael@0 | 1282 | } |
michael@0 | 1283 | |
michael@0 | 1284 | template<> |
michael@0 | 1285 | Value |
michael@0 | 1286 | TypedArrayObjectTemplate<double>::getIndexValue(JSObject *tarray, uint32_t index) |
michael@0 | 1287 | { |
michael@0 | 1288 | double val = getIndex(tarray, index); |
michael@0 | 1289 | |
michael@0 | 1290 | /* |
michael@0 | 1291 | * Doubles in typed arrays could be typed-punned arrays of integers. This |
michael@0 | 1292 | * could allow user code to break the engine-wide invariant that only |
michael@0 | 1293 | * canonical nans are stored into jsvals, which means user code could |
michael@0 | 1294 | * confuse the engine into interpreting a double-typed jsval as an |
michael@0 | 1295 | * object-typed jsval. |
michael@0 | 1296 | */ |
michael@0 | 1297 | return DoubleValue(CanonicalizeNaN(val)); |
michael@0 | 1298 | } |
michael@0 | 1299 | |
michael@0 | 1300 | } /* anonymous namespace */ |
michael@0 | 1301 | |
michael@0 | 1302 | static NewObjectKind |
michael@0 | 1303 | DataViewNewObjectKind(JSContext *cx, uint32_t byteLength, JSObject *proto) |
michael@0 | 1304 | { |
michael@0 | 1305 | if (!proto && byteLength >= TypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH) |
michael@0 | 1306 | return SingletonObject; |
michael@0 | 1307 | jsbytecode *pc; |
michael@0 | 1308 | JSScript *script = cx->currentScript(&pc); |
michael@0 | 1309 | if (!script) |
michael@0 | 1310 | return GenericObject; |
michael@0 | 1311 | return types::UseNewTypeForInitializer(script, pc, &DataViewObject::class_); |
michael@0 | 1312 | } |
michael@0 | 1313 | |
michael@0 | 1314 | inline DataViewObject * |
michael@0 | 1315 | DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength, |
michael@0 | 1316 | Handle<ArrayBufferObject*> arrayBuffer, JSObject *protoArg) |
michael@0 | 1317 | { |
michael@0 | 1318 | JS_ASSERT(byteOffset <= INT32_MAX); |
michael@0 | 1319 | JS_ASSERT(byteLength <= INT32_MAX); |
michael@0 | 1320 | |
michael@0 | 1321 | RootedObject proto(cx, protoArg); |
michael@0 | 1322 | RootedObject obj(cx); |
michael@0 | 1323 | |
michael@0 | 1324 | // This is overflow-safe: 2 * INT32_MAX is still a valid uint32_t. |
michael@0 | 1325 | if (byteOffset + byteLength > arrayBuffer->byteLength()) { |
michael@0 | 1326 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); |
michael@0 | 1327 | return nullptr; |
michael@0 | 1328 | |
michael@0 | 1329 | } |
michael@0 | 1330 | |
michael@0 | 1331 | NewObjectKind newKind = DataViewNewObjectKind(cx, byteLength, proto); |
michael@0 | 1332 | obj = NewBuiltinClassInstance(cx, &class_, newKind); |
michael@0 | 1333 | if (!obj) |
michael@0 | 1334 | return nullptr; |
michael@0 | 1335 | |
michael@0 | 1336 | if (proto) { |
michael@0 | 1337 | types::TypeObject *type = cx->getNewType(&class_, TaggedProto(proto)); |
michael@0 | 1338 | if (!type) |
michael@0 | 1339 | return nullptr; |
michael@0 | 1340 | obj->setType(type); |
michael@0 | 1341 | } else if (byteLength >= TypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH) { |
michael@0 | 1342 | JS_ASSERT(obj->hasSingletonType()); |
michael@0 | 1343 | } else { |
michael@0 | 1344 | jsbytecode *pc; |
michael@0 | 1345 | RootedScript script(cx, cx->currentScript(&pc)); |
michael@0 | 1346 | if (script) { |
michael@0 | 1347 | if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind)) |
michael@0 | 1348 | return nullptr; |
michael@0 | 1349 | } |
michael@0 | 1350 | } |
michael@0 | 1351 | |
michael@0 | 1352 | DataViewObject &dvobj = obj->as<DataViewObject>(); |
michael@0 | 1353 | dvobj.setFixedSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset)); |
michael@0 | 1354 | dvobj.setFixedSlot(BYTELENGTH_SLOT, Int32Value(byteLength)); |
michael@0 | 1355 | dvobj.setFixedSlot(BUFFER_SLOT, ObjectValue(*arrayBuffer)); |
michael@0 | 1356 | dvobj.setFixedSlot(NEXT_VIEW_SLOT, PrivateValue(nullptr)); |
michael@0 | 1357 | InitArrayBufferViewDataPointer(&dvobj, arrayBuffer, byteOffset); |
michael@0 | 1358 | JS_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength()); |
michael@0 | 1359 | |
michael@0 | 1360 | // Verify that the private slot is at the expected place |
michael@0 | 1361 | JS_ASSERT(dvobj.numFixedSlots() == DATA_SLOT); |
michael@0 | 1362 | |
michael@0 | 1363 | arrayBuffer->addView(&dvobj); |
michael@0 | 1364 | |
michael@0 | 1365 | return &dvobj; |
michael@0 | 1366 | } |
michael@0 | 1367 | |
michael@0 | 1368 | bool |
michael@0 | 1369 | DataViewObject::construct(JSContext *cx, JSObject *bufobj, const CallArgs &args, HandleObject proto) |
michael@0 | 1370 | { |
michael@0 | 1371 | if (!IsArrayBuffer(bufobj)) { |
michael@0 | 1372 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, |
michael@0 | 1373 | "DataView", "ArrayBuffer", bufobj->getClass()->name); |
michael@0 | 1374 | return false; |
michael@0 | 1375 | } |
michael@0 | 1376 | |
michael@0 | 1377 | Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj)); |
michael@0 | 1378 | uint32_t bufferLength = buffer->byteLength(); |
michael@0 | 1379 | uint32_t byteOffset = 0; |
michael@0 | 1380 | uint32_t byteLength = bufferLength; |
michael@0 | 1381 | |
michael@0 | 1382 | if (args.length() > 1) { |
michael@0 | 1383 | if (!ToUint32(cx, args[1], &byteOffset)) |
michael@0 | 1384 | return false; |
michael@0 | 1385 | if (byteOffset > INT32_MAX) { |
michael@0 | 1386 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
michael@0 | 1387 | JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); |
michael@0 | 1388 | return false; |
michael@0 | 1389 | } |
michael@0 | 1390 | |
michael@0 | 1391 | if (args.length() > 2) { |
michael@0 | 1392 | if (!ToUint32(cx, args[2], &byteLength)) |
michael@0 | 1393 | return false; |
michael@0 | 1394 | if (byteLength > INT32_MAX) { |
michael@0 | 1395 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
michael@0 | 1396 | JSMSG_ARG_INDEX_OUT_OF_RANGE, "2"); |
michael@0 | 1397 | return false; |
michael@0 | 1398 | } |
michael@0 | 1399 | } else { |
michael@0 | 1400 | if (byteOffset > bufferLength) { |
michael@0 | 1401 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
michael@0 | 1402 | JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); |
michael@0 | 1403 | return false; |
michael@0 | 1404 | } |
michael@0 | 1405 | |
michael@0 | 1406 | byteLength = bufferLength - byteOffset; |
michael@0 | 1407 | } |
michael@0 | 1408 | } |
michael@0 | 1409 | |
michael@0 | 1410 | /* The sum of these cannot overflow a uint32_t */ |
michael@0 | 1411 | JS_ASSERT(byteOffset <= INT32_MAX); |
michael@0 | 1412 | JS_ASSERT(byteLength <= INT32_MAX); |
michael@0 | 1413 | |
michael@0 | 1414 | if (byteOffset + byteLength > bufferLength) { |
michael@0 | 1415 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); |
michael@0 | 1416 | return false; |
michael@0 | 1417 | } |
michael@0 | 1418 | |
michael@0 | 1419 | JSObject *obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, proto); |
michael@0 | 1420 | if (!obj) |
michael@0 | 1421 | return false; |
michael@0 | 1422 | args.rval().setObject(*obj); |
michael@0 | 1423 | return true; |
michael@0 | 1424 | } |
michael@0 | 1425 | |
michael@0 | 1426 | bool |
michael@0 | 1427 | DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1428 | { |
michael@0 | 1429 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1430 | |
michael@0 | 1431 | RootedObject bufobj(cx); |
michael@0 | 1432 | if (!GetFirstArgumentAsObject(cx, args, "DataView constructor", &bufobj)) |
michael@0 | 1433 | return false; |
michael@0 | 1434 | |
michael@0 | 1435 | if (bufobj->is<WrapperObject>() && IsArrayBuffer(UncheckedUnwrap(bufobj))) { |
michael@0 | 1436 | Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal()); |
michael@0 | 1437 | Rooted<JSObject*> proto(cx, global->getOrCreateDataViewPrototype(cx)); |
michael@0 | 1438 | if (!proto) |
michael@0 | 1439 | return false; |
michael@0 | 1440 | |
michael@0 | 1441 | InvokeArgs args2(cx); |
michael@0 | 1442 | if (!args2.init(args.length() + 1)) |
michael@0 | 1443 | return false; |
michael@0 | 1444 | args2.setCallee(global->createDataViewForThis()); |
michael@0 | 1445 | args2.setThis(ObjectValue(*bufobj)); |
michael@0 | 1446 | PodCopy(args2.array(), args.array(), args.length()); |
michael@0 | 1447 | args2[args.length()].setObject(*proto); |
michael@0 | 1448 | if (!Invoke(cx, args2)) |
michael@0 | 1449 | return false; |
michael@0 | 1450 | args.rval().set(args2.rval()); |
michael@0 | 1451 | return true; |
michael@0 | 1452 | } |
michael@0 | 1453 | |
michael@0 | 1454 | return construct(cx, bufobj, args, NullPtr()); |
michael@0 | 1455 | } |
michael@0 | 1456 | |
michael@0 | 1457 | template <typename NativeType> |
michael@0 | 1458 | /* static */ uint8_t * |
michael@0 | 1459 | DataViewObject::getDataPointer(JSContext *cx, Handle<DataViewObject*> obj, uint32_t offset) |
michael@0 | 1460 | { |
michael@0 | 1461 | const size_t TypeSize = sizeof(NativeType); |
michael@0 | 1462 | if (offset > UINT32_MAX - TypeSize || offset + TypeSize > obj->byteLength()) { |
michael@0 | 1463 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); |
michael@0 | 1464 | return nullptr; |
michael@0 | 1465 | } |
michael@0 | 1466 | |
michael@0 | 1467 | return static_cast<uint8_t*>(obj->dataPointer()) + offset; |
michael@0 | 1468 | } |
michael@0 | 1469 | |
michael@0 | 1470 | static inline bool |
michael@0 | 1471 | needToSwapBytes(bool littleEndian) |
michael@0 | 1472 | { |
michael@0 | 1473 | #if IS_LITTLE_ENDIAN |
michael@0 | 1474 | return !littleEndian; |
michael@0 | 1475 | #else |
michael@0 | 1476 | return littleEndian; |
michael@0 | 1477 | #endif |
michael@0 | 1478 | } |
michael@0 | 1479 | |
michael@0 | 1480 | static inline uint8_t |
michael@0 | 1481 | swapBytes(uint8_t x) |
michael@0 | 1482 | { |
michael@0 | 1483 | return x; |
michael@0 | 1484 | } |
michael@0 | 1485 | |
michael@0 | 1486 | static inline uint16_t |
michael@0 | 1487 | swapBytes(uint16_t x) |
michael@0 | 1488 | { |
michael@0 | 1489 | return ((x & 0xff) << 8) | (x >> 8); |
michael@0 | 1490 | } |
michael@0 | 1491 | |
michael@0 | 1492 | static inline uint32_t |
michael@0 | 1493 | swapBytes(uint32_t x) |
michael@0 | 1494 | { |
michael@0 | 1495 | return ((x & 0xff) << 24) | |
michael@0 | 1496 | ((x & 0xff00) << 8) | |
michael@0 | 1497 | ((x & 0xff0000) >> 8) | |
michael@0 | 1498 | ((x & 0xff000000) >> 24); |
michael@0 | 1499 | } |
michael@0 | 1500 | |
michael@0 | 1501 | static inline uint64_t |
michael@0 | 1502 | swapBytes(uint64_t x) |
michael@0 | 1503 | { |
michael@0 | 1504 | uint32_t a = x & UINT32_MAX; |
michael@0 | 1505 | uint32_t b = x >> 32; |
michael@0 | 1506 | return (uint64_t(swapBytes(a)) << 32) | swapBytes(b); |
michael@0 | 1507 | } |
michael@0 | 1508 | |
michael@0 | 1509 | template <typename DataType> struct DataToRepType { typedef DataType result; }; |
michael@0 | 1510 | template <> struct DataToRepType<int8_t> { typedef uint8_t result; }; |
michael@0 | 1511 | template <> struct DataToRepType<uint8_t> { typedef uint8_t result; }; |
michael@0 | 1512 | template <> struct DataToRepType<int16_t> { typedef uint16_t result; }; |
michael@0 | 1513 | template <> struct DataToRepType<uint16_t> { typedef uint16_t result; }; |
michael@0 | 1514 | template <> struct DataToRepType<int32_t> { typedef uint32_t result; }; |
michael@0 | 1515 | template <> struct DataToRepType<uint32_t> { typedef uint32_t result; }; |
michael@0 | 1516 | template <> struct DataToRepType<float> { typedef uint32_t result; }; |
michael@0 | 1517 | template <> struct DataToRepType<double> { typedef uint64_t result; }; |
michael@0 | 1518 | |
michael@0 | 1519 | template <typename DataType> |
michael@0 | 1520 | struct DataViewIO |
michael@0 | 1521 | { |
michael@0 | 1522 | typedef typename DataToRepType<DataType>::result ReadWriteType; |
michael@0 | 1523 | |
michael@0 | 1524 | static void fromBuffer(DataType *dest, const uint8_t *unalignedBuffer, bool wantSwap) |
michael@0 | 1525 | { |
michael@0 | 1526 | JS_ASSERT((reinterpret_cast<uintptr_t>(dest) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0); |
michael@0 | 1527 | memcpy((void *) dest, unalignedBuffer, sizeof(ReadWriteType)); |
michael@0 | 1528 | if (wantSwap) { |
michael@0 | 1529 | ReadWriteType *rwDest = reinterpret_cast<ReadWriteType *>(dest); |
michael@0 | 1530 | *rwDest = swapBytes(*rwDest); |
michael@0 | 1531 | } |
michael@0 | 1532 | } |
michael@0 | 1533 | |
michael@0 | 1534 | static void toBuffer(uint8_t *unalignedBuffer, const DataType *src, bool wantSwap) |
michael@0 | 1535 | { |
michael@0 | 1536 | JS_ASSERT((reinterpret_cast<uintptr_t>(src) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0); |
michael@0 | 1537 | ReadWriteType temp = *reinterpret_cast<const ReadWriteType *>(src); |
michael@0 | 1538 | if (wantSwap) |
michael@0 | 1539 | temp = swapBytes(temp); |
michael@0 | 1540 | memcpy(unalignedBuffer, (void *) &temp, sizeof(ReadWriteType)); |
michael@0 | 1541 | } |
michael@0 | 1542 | }; |
michael@0 | 1543 | |
michael@0 | 1544 | template<typename NativeType> |
michael@0 | 1545 | /* static */ bool |
michael@0 | 1546 | DataViewObject::read(JSContext *cx, Handle<DataViewObject*> obj, |
michael@0 | 1547 | CallArgs &args, NativeType *val, const char *method) |
michael@0 | 1548 | { |
michael@0 | 1549 | if (args.length() < 1) { |
michael@0 | 1550 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
michael@0 | 1551 | JSMSG_MORE_ARGS_NEEDED, method, "0", "s"); |
michael@0 | 1552 | return false; |
michael@0 | 1553 | } |
michael@0 | 1554 | |
michael@0 | 1555 | uint32_t offset; |
michael@0 | 1556 | if (!ToUint32(cx, args[0], &offset)) |
michael@0 | 1557 | return false; |
michael@0 | 1558 | |
michael@0 | 1559 | bool fromLittleEndian = args.length() >= 2 && ToBoolean(args[1]); |
michael@0 | 1560 | |
michael@0 | 1561 | uint8_t *data = DataViewObject::getDataPointer<NativeType>(cx, obj, offset); |
michael@0 | 1562 | if (!data) |
michael@0 | 1563 | return false; |
michael@0 | 1564 | |
michael@0 | 1565 | DataViewIO<NativeType>::fromBuffer(val, data, needToSwapBytes(fromLittleEndian)); |
michael@0 | 1566 | return true; |
michael@0 | 1567 | } |
michael@0 | 1568 | |
michael@0 | 1569 | template <typename NativeType> |
michael@0 | 1570 | static inline bool |
michael@0 | 1571 | WebIDLCast(JSContext *cx, HandleValue value, NativeType *out) |
michael@0 | 1572 | { |
michael@0 | 1573 | int32_t temp; |
michael@0 | 1574 | if (!ToInt32(cx, value, &temp)) |
michael@0 | 1575 | return false; |
michael@0 | 1576 | // Technically, the behavior of assigning an out of range value to a signed |
michael@0 | 1577 | // variable is undefined. In practice, compilers seem to do what we want |
michael@0 | 1578 | // without issuing any warnings. |
michael@0 | 1579 | *out = static_cast<NativeType>(temp); |
michael@0 | 1580 | return true; |
michael@0 | 1581 | } |
michael@0 | 1582 | |
michael@0 | 1583 | template <> |
michael@0 | 1584 | inline bool |
michael@0 | 1585 | WebIDLCast<float>(JSContext *cx, HandleValue value, float *out) |
michael@0 | 1586 | { |
michael@0 | 1587 | double temp; |
michael@0 | 1588 | if (!ToNumber(cx, value, &temp)) |
michael@0 | 1589 | return false; |
michael@0 | 1590 | *out = static_cast<float>(temp); |
michael@0 | 1591 | return true; |
michael@0 | 1592 | } |
michael@0 | 1593 | |
michael@0 | 1594 | template <> |
michael@0 | 1595 | inline bool |
michael@0 | 1596 | WebIDLCast<double>(JSContext *cx, HandleValue value, double *out) |
michael@0 | 1597 | { |
michael@0 | 1598 | return ToNumber(cx, value, out); |
michael@0 | 1599 | } |
michael@0 | 1600 | |
michael@0 | 1601 | template<typename NativeType> |
michael@0 | 1602 | /* static */ bool |
michael@0 | 1603 | DataViewObject::write(JSContext *cx, Handle<DataViewObject*> obj, |
michael@0 | 1604 | CallArgs &args, const char *method) |
michael@0 | 1605 | { |
michael@0 | 1606 | if (args.length() < 2) { |
michael@0 | 1607 | JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, |
michael@0 | 1608 | JSMSG_MORE_ARGS_NEEDED, method, "1", ""); |
michael@0 | 1609 | return false; |
michael@0 | 1610 | } |
michael@0 | 1611 | |
michael@0 | 1612 | uint32_t offset; |
michael@0 | 1613 | if (!ToUint32(cx, args[0], &offset)) |
michael@0 | 1614 | return false; |
michael@0 | 1615 | |
michael@0 | 1616 | NativeType value; |
michael@0 | 1617 | if (!WebIDLCast(cx, args[1], &value)) |
michael@0 | 1618 | return false; |
michael@0 | 1619 | |
michael@0 | 1620 | bool toLittleEndian = args.length() >= 3 && ToBoolean(args[2]); |
michael@0 | 1621 | |
michael@0 | 1622 | uint8_t *data = DataViewObject::getDataPointer<NativeType>(cx, obj, offset); |
michael@0 | 1623 | if (!data) |
michael@0 | 1624 | return false; |
michael@0 | 1625 | |
michael@0 | 1626 | DataViewIO<NativeType>::toBuffer(data, &value, needToSwapBytes(toLittleEndian)); |
michael@0 | 1627 | return true; |
michael@0 | 1628 | } |
michael@0 | 1629 | |
michael@0 | 1630 | bool |
michael@0 | 1631 | DataViewObject::getInt8Impl(JSContext *cx, CallArgs args) |
michael@0 | 1632 | { |
michael@0 | 1633 | JS_ASSERT(is(args.thisv())); |
michael@0 | 1634 | |
michael@0 | 1635 | Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
michael@0 | 1636 | |
michael@0 | 1637 | int8_t val; |
michael@0 | 1638 | if (!read(cx, thisView, args, &val, "getInt8")) |
michael@0 | 1639 | return false; |
michael@0 | 1640 | args.rval().setInt32(val); |
michael@0 | 1641 | return true; |
michael@0 | 1642 | } |
michael@0 | 1643 | |
michael@0 | 1644 | bool |
michael@0 | 1645 | DataViewObject::fun_getInt8(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1646 | { |
michael@0 | 1647 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1648 | return CallNonGenericMethod<is, getInt8Impl>(cx, args); |
michael@0 | 1649 | } |
michael@0 | 1650 | |
michael@0 | 1651 | bool |
michael@0 | 1652 | DataViewObject::getUint8Impl(JSContext *cx, CallArgs args) |
michael@0 | 1653 | { |
michael@0 | 1654 | JS_ASSERT(is(args.thisv())); |
michael@0 | 1655 | |
michael@0 | 1656 | Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
michael@0 | 1657 | |
michael@0 | 1658 | uint8_t val; |
michael@0 | 1659 | if (!read(cx, thisView, args, &val, "getUint8")) |
michael@0 | 1660 | return false; |
michael@0 | 1661 | args.rval().setInt32(val); |
michael@0 | 1662 | return true; |
michael@0 | 1663 | } |
michael@0 | 1664 | |
michael@0 | 1665 | bool |
michael@0 | 1666 | DataViewObject::fun_getUint8(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1667 | { |
michael@0 | 1668 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1669 | return CallNonGenericMethod<is, getUint8Impl>(cx, args); |
michael@0 | 1670 | } |
michael@0 | 1671 | |
michael@0 | 1672 | bool |
michael@0 | 1673 | DataViewObject::getInt16Impl(JSContext *cx, CallArgs args) |
michael@0 | 1674 | { |
michael@0 | 1675 | JS_ASSERT(is(args.thisv())); |
michael@0 | 1676 | |
michael@0 | 1677 | Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
michael@0 | 1678 | |
michael@0 | 1679 | int16_t val; |
michael@0 | 1680 | if (!read(cx, thisView, args, &val, "getInt16")) |
michael@0 | 1681 | return false; |
michael@0 | 1682 | args.rval().setInt32(val); |
michael@0 | 1683 | return true; |
michael@0 | 1684 | } |
michael@0 | 1685 | |
michael@0 | 1686 | bool |
michael@0 | 1687 | DataViewObject::fun_getInt16(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1688 | { |
michael@0 | 1689 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1690 | return CallNonGenericMethod<is, getInt16Impl>(cx, args); |
michael@0 | 1691 | } |
michael@0 | 1692 | |
michael@0 | 1693 | bool |
michael@0 | 1694 | DataViewObject::getUint16Impl(JSContext *cx, CallArgs args) |
michael@0 | 1695 | { |
michael@0 | 1696 | JS_ASSERT(is(args.thisv())); |
michael@0 | 1697 | |
michael@0 | 1698 | Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
michael@0 | 1699 | |
michael@0 | 1700 | uint16_t val; |
michael@0 | 1701 | if (!read(cx, thisView, args, &val, "getUint16")) |
michael@0 | 1702 | return false; |
michael@0 | 1703 | args.rval().setInt32(val); |
michael@0 | 1704 | return true; |
michael@0 | 1705 | } |
michael@0 | 1706 | |
michael@0 | 1707 | bool |
michael@0 | 1708 | DataViewObject::fun_getUint16(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1709 | { |
michael@0 | 1710 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1711 | return CallNonGenericMethod<is, getUint16Impl>(cx, args); |
michael@0 | 1712 | } |
michael@0 | 1713 | |
michael@0 | 1714 | bool |
michael@0 | 1715 | DataViewObject::getInt32Impl(JSContext *cx, CallArgs args) |
michael@0 | 1716 | { |
michael@0 | 1717 | JS_ASSERT(is(args.thisv())); |
michael@0 | 1718 | |
michael@0 | 1719 | Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
michael@0 | 1720 | |
michael@0 | 1721 | int32_t val; |
michael@0 | 1722 | if (!read(cx, thisView, args, &val, "getInt32")) |
michael@0 | 1723 | return false; |
michael@0 | 1724 | args.rval().setInt32(val); |
michael@0 | 1725 | return true; |
michael@0 | 1726 | } |
michael@0 | 1727 | |
michael@0 | 1728 | bool |
michael@0 | 1729 | DataViewObject::fun_getInt32(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1730 | { |
michael@0 | 1731 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1732 | return CallNonGenericMethod<is, getInt32Impl>(cx, args); |
michael@0 | 1733 | } |
michael@0 | 1734 | |
michael@0 | 1735 | bool |
michael@0 | 1736 | DataViewObject::getUint32Impl(JSContext *cx, CallArgs args) |
michael@0 | 1737 | { |
michael@0 | 1738 | JS_ASSERT(is(args.thisv())); |
michael@0 | 1739 | |
michael@0 | 1740 | Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
michael@0 | 1741 | |
michael@0 | 1742 | uint32_t val; |
michael@0 | 1743 | if (!read(cx, thisView, args, &val, "getUint32")) |
michael@0 | 1744 | return false; |
michael@0 | 1745 | args.rval().setNumber(val); |
michael@0 | 1746 | return true; |
michael@0 | 1747 | } |
michael@0 | 1748 | |
michael@0 | 1749 | bool |
michael@0 | 1750 | DataViewObject::fun_getUint32(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1751 | { |
michael@0 | 1752 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1753 | return CallNonGenericMethod<is, getUint32Impl>(cx, args); |
michael@0 | 1754 | } |
michael@0 | 1755 | |
michael@0 | 1756 | bool |
michael@0 | 1757 | DataViewObject::getFloat32Impl(JSContext *cx, CallArgs args) |
michael@0 | 1758 | { |
michael@0 | 1759 | JS_ASSERT(is(args.thisv())); |
michael@0 | 1760 | |
michael@0 | 1761 | Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
michael@0 | 1762 | |
michael@0 | 1763 | float val; |
michael@0 | 1764 | if (!read(cx, thisView, args, &val, "getFloat32")) |
michael@0 | 1765 | return false; |
michael@0 | 1766 | |
michael@0 | 1767 | args.rval().setDouble(CanonicalizeNaN(val)); |
michael@0 | 1768 | return true; |
michael@0 | 1769 | } |
michael@0 | 1770 | |
michael@0 | 1771 | bool |
michael@0 | 1772 | DataViewObject::fun_getFloat32(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1773 | { |
michael@0 | 1774 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1775 | return CallNonGenericMethod<is, getFloat32Impl>(cx, args); |
michael@0 | 1776 | } |
michael@0 | 1777 | |
michael@0 | 1778 | bool |
michael@0 | 1779 | DataViewObject::getFloat64Impl(JSContext *cx, CallArgs args) |
michael@0 | 1780 | { |
michael@0 | 1781 | JS_ASSERT(is(args.thisv())); |
michael@0 | 1782 | |
michael@0 | 1783 | Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
michael@0 | 1784 | |
michael@0 | 1785 | double val; |
michael@0 | 1786 | if (!read(cx, thisView, args, &val, "getFloat64")) |
michael@0 | 1787 | return false; |
michael@0 | 1788 | |
michael@0 | 1789 | args.rval().setDouble(CanonicalizeNaN(val)); |
michael@0 | 1790 | return true; |
michael@0 | 1791 | } |
michael@0 | 1792 | |
michael@0 | 1793 | bool |
michael@0 | 1794 | DataViewObject::fun_getFloat64(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1795 | { |
michael@0 | 1796 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1797 | return CallNonGenericMethod<is, getFloat64Impl>(cx, args); |
michael@0 | 1798 | } |
michael@0 | 1799 | |
michael@0 | 1800 | bool |
michael@0 | 1801 | DataViewObject::setInt8Impl(JSContext *cx, CallArgs args) |
michael@0 | 1802 | { |
michael@0 | 1803 | JS_ASSERT(is(args.thisv())); |
michael@0 | 1804 | |
michael@0 | 1805 | Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
michael@0 | 1806 | |
michael@0 | 1807 | if (!write<int8_t>(cx, thisView, args, "setInt8")) |
michael@0 | 1808 | return false; |
michael@0 | 1809 | args.rval().setUndefined(); |
michael@0 | 1810 | return true; |
michael@0 | 1811 | } |
michael@0 | 1812 | |
michael@0 | 1813 | bool |
michael@0 | 1814 | DataViewObject::fun_setInt8(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1815 | { |
michael@0 | 1816 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1817 | return CallNonGenericMethod<is, setInt8Impl>(cx, args); |
michael@0 | 1818 | } |
michael@0 | 1819 | |
michael@0 | 1820 | bool |
michael@0 | 1821 | DataViewObject::setUint8Impl(JSContext *cx, CallArgs args) |
michael@0 | 1822 | { |
michael@0 | 1823 | JS_ASSERT(is(args.thisv())); |
michael@0 | 1824 | |
michael@0 | 1825 | Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
michael@0 | 1826 | |
michael@0 | 1827 | if (!write<uint8_t>(cx, thisView, args, "setUint8")) |
michael@0 | 1828 | return false; |
michael@0 | 1829 | args.rval().setUndefined(); |
michael@0 | 1830 | return true; |
michael@0 | 1831 | } |
michael@0 | 1832 | |
michael@0 | 1833 | bool |
michael@0 | 1834 | DataViewObject::fun_setUint8(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1835 | { |
michael@0 | 1836 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1837 | return CallNonGenericMethod<is, setUint8Impl>(cx, args); |
michael@0 | 1838 | } |
michael@0 | 1839 | |
michael@0 | 1840 | bool |
michael@0 | 1841 | DataViewObject::setInt16Impl(JSContext *cx, CallArgs args) |
michael@0 | 1842 | { |
michael@0 | 1843 | JS_ASSERT(is(args.thisv())); |
michael@0 | 1844 | |
michael@0 | 1845 | Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
michael@0 | 1846 | |
michael@0 | 1847 | if (!write<int16_t>(cx, thisView, args, "setInt16")) |
michael@0 | 1848 | return false; |
michael@0 | 1849 | args.rval().setUndefined(); |
michael@0 | 1850 | return true; |
michael@0 | 1851 | } |
michael@0 | 1852 | |
michael@0 | 1853 | bool |
michael@0 | 1854 | DataViewObject::fun_setInt16(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1855 | { |
michael@0 | 1856 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1857 | return CallNonGenericMethod<is, setInt16Impl>(cx, args); |
michael@0 | 1858 | } |
michael@0 | 1859 | |
michael@0 | 1860 | bool |
michael@0 | 1861 | DataViewObject::setUint16Impl(JSContext *cx, CallArgs args) |
michael@0 | 1862 | { |
michael@0 | 1863 | JS_ASSERT(is(args.thisv())); |
michael@0 | 1864 | |
michael@0 | 1865 | Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
michael@0 | 1866 | |
michael@0 | 1867 | if (!write<uint16_t>(cx, thisView, args, "setUint16")) |
michael@0 | 1868 | return false; |
michael@0 | 1869 | args.rval().setUndefined(); |
michael@0 | 1870 | return true; |
michael@0 | 1871 | } |
michael@0 | 1872 | |
michael@0 | 1873 | bool |
michael@0 | 1874 | DataViewObject::fun_setUint16(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1875 | { |
michael@0 | 1876 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1877 | return CallNonGenericMethod<is, setUint16Impl>(cx, args); |
michael@0 | 1878 | } |
michael@0 | 1879 | |
michael@0 | 1880 | bool |
michael@0 | 1881 | DataViewObject::setInt32Impl(JSContext *cx, CallArgs args) |
michael@0 | 1882 | { |
michael@0 | 1883 | JS_ASSERT(is(args.thisv())); |
michael@0 | 1884 | |
michael@0 | 1885 | Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
michael@0 | 1886 | |
michael@0 | 1887 | if (!write<int32_t>(cx, thisView, args, "setInt32")) |
michael@0 | 1888 | return false; |
michael@0 | 1889 | args.rval().setUndefined(); |
michael@0 | 1890 | return true; |
michael@0 | 1891 | } |
michael@0 | 1892 | |
michael@0 | 1893 | bool |
michael@0 | 1894 | DataViewObject::fun_setInt32(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1895 | { |
michael@0 | 1896 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1897 | return CallNonGenericMethod<is, setInt32Impl>(cx, args); |
michael@0 | 1898 | } |
michael@0 | 1899 | |
michael@0 | 1900 | bool |
michael@0 | 1901 | DataViewObject::setUint32Impl(JSContext *cx, CallArgs args) |
michael@0 | 1902 | { |
michael@0 | 1903 | JS_ASSERT(is(args.thisv())); |
michael@0 | 1904 | |
michael@0 | 1905 | Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
michael@0 | 1906 | |
michael@0 | 1907 | if (!write<uint32_t>(cx, thisView, args, "setUint32")) |
michael@0 | 1908 | return false; |
michael@0 | 1909 | args.rval().setUndefined(); |
michael@0 | 1910 | return true; |
michael@0 | 1911 | } |
michael@0 | 1912 | |
michael@0 | 1913 | bool |
michael@0 | 1914 | DataViewObject::fun_setUint32(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1915 | { |
michael@0 | 1916 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1917 | return CallNonGenericMethod<is, setUint32Impl>(cx, args); |
michael@0 | 1918 | } |
michael@0 | 1919 | |
michael@0 | 1920 | bool |
michael@0 | 1921 | DataViewObject::setFloat32Impl(JSContext *cx, CallArgs args) |
michael@0 | 1922 | { |
michael@0 | 1923 | JS_ASSERT(is(args.thisv())); |
michael@0 | 1924 | |
michael@0 | 1925 | Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
michael@0 | 1926 | |
michael@0 | 1927 | if (!write<float>(cx, thisView, args, "setFloat32")) |
michael@0 | 1928 | return false; |
michael@0 | 1929 | args.rval().setUndefined(); |
michael@0 | 1930 | return true; |
michael@0 | 1931 | } |
michael@0 | 1932 | |
michael@0 | 1933 | bool |
michael@0 | 1934 | DataViewObject::fun_setFloat32(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1935 | { |
michael@0 | 1936 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1937 | return CallNonGenericMethod<is, setFloat32Impl>(cx, args); |
michael@0 | 1938 | } |
michael@0 | 1939 | |
michael@0 | 1940 | bool |
michael@0 | 1941 | DataViewObject::setFloat64Impl(JSContext *cx, CallArgs args) |
michael@0 | 1942 | { |
michael@0 | 1943 | JS_ASSERT(is(args.thisv())); |
michael@0 | 1944 | |
michael@0 | 1945 | Rooted<DataViewObject*> thisView(cx, &args.thisv().toObject().as<DataViewObject>()); |
michael@0 | 1946 | |
michael@0 | 1947 | if (!write<double>(cx, thisView, args, "setFloat64")) |
michael@0 | 1948 | return false; |
michael@0 | 1949 | args.rval().setUndefined(); |
michael@0 | 1950 | return true; |
michael@0 | 1951 | } |
michael@0 | 1952 | |
michael@0 | 1953 | bool |
michael@0 | 1954 | DataViewObject::fun_setFloat64(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 1955 | { |
michael@0 | 1956 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 1957 | return CallNonGenericMethod<is, setFloat64Impl>(cx, args); |
michael@0 | 1958 | } |
michael@0 | 1959 | |
michael@0 | 1960 | Value |
michael@0 | 1961 | TypedArrayObject::getElement(uint32_t index) |
michael@0 | 1962 | { |
michael@0 | 1963 | switch (type()) { |
michael@0 | 1964 | case ScalarTypeDescr::TYPE_INT8: |
michael@0 | 1965 | return TypedArrayObjectTemplate<int8_t>::getIndexValue(this, index); |
michael@0 | 1966 | break; |
michael@0 | 1967 | case ScalarTypeDescr::TYPE_UINT8: |
michael@0 | 1968 | return TypedArrayObjectTemplate<uint8_t>::getIndexValue(this, index); |
michael@0 | 1969 | break; |
michael@0 | 1970 | case ScalarTypeDescr::TYPE_UINT8_CLAMPED: |
michael@0 | 1971 | return TypedArrayObjectTemplate<uint8_clamped>::getIndexValue(this, index); |
michael@0 | 1972 | break; |
michael@0 | 1973 | case ScalarTypeDescr::TYPE_INT16: |
michael@0 | 1974 | return TypedArrayObjectTemplate<int16_t>::getIndexValue(this, index); |
michael@0 | 1975 | break; |
michael@0 | 1976 | case ScalarTypeDescr::TYPE_UINT16: |
michael@0 | 1977 | return TypedArrayObjectTemplate<uint16_t>::getIndexValue(this, index); |
michael@0 | 1978 | break; |
michael@0 | 1979 | case ScalarTypeDescr::TYPE_INT32: |
michael@0 | 1980 | return TypedArrayObjectTemplate<int32_t>::getIndexValue(this, index); |
michael@0 | 1981 | break; |
michael@0 | 1982 | case ScalarTypeDescr::TYPE_UINT32: |
michael@0 | 1983 | return TypedArrayObjectTemplate<uint32_t>::getIndexValue(this, index); |
michael@0 | 1984 | break; |
michael@0 | 1985 | case ScalarTypeDescr::TYPE_FLOAT32: |
michael@0 | 1986 | return TypedArrayObjectTemplate<float>::getIndexValue(this, index); |
michael@0 | 1987 | break; |
michael@0 | 1988 | case ScalarTypeDescr::TYPE_FLOAT64: |
michael@0 | 1989 | return TypedArrayObjectTemplate<double>::getIndexValue(this, index); |
michael@0 | 1990 | break; |
michael@0 | 1991 | default: |
michael@0 | 1992 | MOZ_ASSUME_UNREACHABLE("Unknown TypedArray type"); |
michael@0 | 1993 | break; |
michael@0 | 1994 | } |
michael@0 | 1995 | } |
michael@0 | 1996 | |
michael@0 | 1997 | void |
michael@0 | 1998 | TypedArrayObject::setElement(TypedArrayObject &obj, uint32_t index, double d) |
michael@0 | 1999 | { |
michael@0 | 2000 | MOZ_ASSERT(index < obj.length()); |
michael@0 | 2001 | |
michael@0 | 2002 | switch (obj.type()) { |
michael@0 | 2003 | case ScalarTypeDescr::TYPE_INT8: |
michael@0 | 2004 | TypedArrayObjectTemplate<int8_t>::setIndexValue(obj, index, d); |
michael@0 | 2005 | break; |
michael@0 | 2006 | case ScalarTypeDescr::TYPE_UINT8: |
michael@0 | 2007 | TypedArrayObjectTemplate<uint8_t>::setIndexValue(obj, index, d); |
michael@0 | 2008 | break; |
michael@0 | 2009 | case ScalarTypeDescr::TYPE_UINT8_CLAMPED: |
michael@0 | 2010 | TypedArrayObjectTemplate<uint8_clamped>::setIndexValue(obj, index, d); |
michael@0 | 2011 | break; |
michael@0 | 2012 | case ScalarTypeDescr::TYPE_INT16: |
michael@0 | 2013 | TypedArrayObjectTemplate<int16_t>::setIndexValue(obj, index, d); |
michael@0 | 2014 | break; |
michael@0 | 2015 | case ScalarTypeDescr::TYPE_UINT16: |
michael@0 | 2016 | TypedArrayObjectTemplate<uint16_t>::setIndexValue(obj, index, d); |
michael@0 | 2017 | break; |
michael@0 | 2018 | case ScalarTypeDescr::TYPE_INT32: |
michael@0 | 2019 | TypedArrayObjectTemplate<int32_t>::setIndexValue(obj, index, d); |
michael@0 | 2020 | break; |
michael@0 | 2021 | case ScalarTypeDescr::TYPE_UINT32: |
michael@0 | 2022 | TypedArrayObjectTemplate<uint32_t>::setIndexValue(obj, index, d); |
michael@0 | 2023 | break; |
michael@0 | 2024 | case ScalarTypeDescr::TYPE_FLOAT32: |
michael@0 | 2025 | TypedArrayObjectTemplate<float>::setIndexValue(obj, index, d); |
michael@0 | 2026 | break; |
michael@0 | 2027 | case ScalarTypeDescr::TYPE_FLOAT64: |
michael@0 | 2028 | TypedArrayObjectTemplate<double>::setIndexValue(obj, index, d); |
michael@0 | 2029 | break; |
michael@0 | 2030 | default: |
michael@0 | 2031 | MOZ_ASSUME_UNREACHABLE("Unknown TypedArray type"); |
michael@0 | 2032 | break; |
michael@0 | 2033 | } |
michael@0 | 2034 | } |
michael@0 | 2035 | |
michael@0 | 2036 | /*** |
michael@0 | 2037 | *** JS impl |
michael@0 | 2038 | ***/ |
michael@0 | 2039 | |
michael@0 | 2040 | /* |
michael@0 | 2041 | * TypedArrayObject boilerplate |
michael@0 | 2042 | */ |
michael@0 | 2043 | |
michael@0 | 2044 | #ifndef RELEASE_BUILD |
michael@0 | 2045 | # define IMPL_TYPED_ARRAY_STATICS(_typedArray) \ |
michael@0 | 2046 | const JSFunctionSpec _typedArray##Object::jsfuncs[] = { \ |
michael@0 | 2047 | JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0, 0), \ |
michael@0 | 2048 | JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \ |
michael@0 | 2049 | JS_FN("set", _typedArray##Object::fun_set, 2, JSFUN_GENERIC_NATIVE), \ |
michael@0 | 2050 | JS_FN("move", _typedArray##Object::fun_move, 3, JSFUN_GENERIC_NATIVE), \ |
michael@0 | 2051 | JS_FS_END \ |
michael@0 | 2052 | } |
michael@0 | 2053 | #else |
michael@0 | 2054 | # define IMPL_TYPED_ARRAY_STATICS(_typedArray) \ |
michael@0 | 2055 | const JSFunctionSpec _typedArray##Object::jsfuncs[] = { \ |
michael@0 | 2056 | JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0, 0), \ |
michael@0 | 2057 | JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \ |
michael@0 | 2058 | JS_FN("set", _typedArray##Object::fun_set, 2, JSFUN_GENERIC_NATIVE), \ |
michael@0 | 2059 | JS_FS_END \ |
michael@0 | 2060 | } |
michael@0 | 2061 | #endif |
michael@0 | 2062 | |
michael@0 | 2063 | #define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Name,NativeType) \ |
michael@0 | 2064 | JS_FRIEND_API(JSObject *) JS_New ## Name ## Array(JSContext *cx, uint32_t nelements) \ |
michael@0 | 2065 | { \ |
michael@0 | 2066 | return TypedArrayObjectTemplate<NativeType>::fromLength(cx, nelements); \ |
michael@0 | 2067 | } \ |
michael@0 | 2068 | JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayFromArray(JSContext *cx, HandleObject other) \ |
michael@0 | 2069 | { \ |
michael@0 | 2070 | return TypedArrayObjectTemplate<NativeType>::fromArray(cx, other); \ |
michael@0 | 2071 | } \ |
michael@0 | 2072 | JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayWithBuffer(JSContext *cx, \ |
michael@0 | 2073 | HandleObject arrayBuffer, uint32_t byteOffset, int32_t length) \ |
michael@0 | 2074 | { \ |
michael@0 | 2075 | return TypedArrayObjectTemplate<NativeType>::fromBuffer(cx, arrayBuffer, byteOffset, \ |
michael@0 | 2076 | length, js::NullPtr()); \ |
michael@0 | 2077 | } \ |
michael@0 | 2078 | JS_FRIEND_API(bool) JS_Is ## Name ## Array(JSObject *obj) \ |
michael@0 | 2079 | { \ |
michael@0 | 2080 | if (!(obj = CheckedUnwrap(obj))) \ |
michael@0 | 2081 | return false; \ |
michael@0 | 2082 | const Class *clasp = obj->getClass(); \ |
michael@0 | 2083 | return clasp == &TypedArrayObject::classes[TypedArrayObjectTemplate<NativeType>::ArrayTypeID()]; \ |
michael@0 | 2084 | } \ |
michael@0 | 2085 | JS_FRIEND_API(JSObject *) js::Unwrap ## Name ## Array(JSObject *obj) \ |
michael@0 | 2086 | { \ |
michael@0 | 2087 | obj = CheckedUnwrap(obj); \ |
michael@0 | 2088 | if (!obj) \ |
michael@0 | 2089 | return nullptr; \ |
michael@0 | 2090 | const Class *clasp = obj->getClass(); \ |
michael@0 | 2091 | if (clasp == &TypedArrayObject::classes[TypedArrayObjectTemplate<NativeType>::ArrayTypeID()]) \ |
michael@0 | 2092 | return obj; \ |
michael@0 | 2093 | return nullptr; \ |
michael@0 | 2094 | } \ |
michael@0 | 2095 | const js::Class* const js::detail::Name ## ArrayClassPtr = \ |
michael@0 | 2096 | &js::TypedArrayObject::classes[TypedArrayObjectTemplate<NativeType>::ArrayTypeID()]; |
michael@0 | 2097 | |
michael@0 | 2098 | IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int8, int8_t) |
michael@0 | 2099 | IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8, uint8_t) |
michael@0 | 2100 | IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8Clamped, uint8_clamped) |
michael@0 | 2101 | IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int16, int16_t) |
michael@0 | 2102 | IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint16, uint16_t) |
michael@0 | 2103 | IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int32, int32_t) |
michael@0 | 2104 | IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint32, uint32_t) |
michael@0 | 2105 | IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float32, float) |
michael@0 | 2106 | IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double) |
michael@0 | 2107 | |
michael@0 | 2108 | #define IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Name, ExternalType, InternalType) \ |
michael@0 | 2109 | JS_FRIEND_API(JSObject *) JS_GetObjectAs ## Name ## Array(JSObject *obj, \ |
michael@0 | 2110 | uint32_t *length, \ |
michael@0 | 2111 | ExternalType **data) \ |
michael@0 | 2112 | { \ |
michael@0 | 2113 | if (!(obj = CheckedUnwrap(obj))) \ |
michael@0 | 2114 | return nullptr; \ |
michael@0 | 2115 | \ |
michael@0 | 2116 | const Class *clasp = obj->getClass(); \ |
michael@0 | 2117 | if (clasp != &TypedArrayObject::classes[TypedArrayObjectTemplate<InternalType>::ArrayTypeID()]) \ |
michael@0 | 2118 | return nullptr; \ |
michael@0 | 2119 | \ |
michael@0 | 2120 | TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); \ |
michael@0 | 2121 | *length = tarr->length(); \ |
michael@0 | 2122 | *data = static_cast<ExternalType *>(tarr->viewData()); \ |
michael@0 | 2123 | \ |
michael@0 | 2124 | return obj; \ |
michael@0 | 2125 | } |
michael@0 | 2126 | |
michael@0 | 2127 | IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int8, int8_t, int8_t) |
michael@0 | 2128 | IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint8, uint8_t, uint8_t) |
michael@0 | 2129 | IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint8Clamped, uint8_t, uint8_clamped) |
michael@0 | 2130 | IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int16, int16_t, int16_t) |
michael@0 | 2131 | IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint16, uint16_t, uint16_t) |
michael@0 | 2132 | IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int32, int32_t, int32_t) |
michael@0 | 2133 | IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint32, uint32_t, uint32_t) |
michael@0 | 2134 | IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float32, float, float) |
michael@0 | 2135 | IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double) |
michael@0 | 2136 | |
michael@0 | 2137 | #define IMPL_TYPED_ARRAY_PROTO_CLASS(_typedArray) \ |
michael@0 | 2138 | { \ |
michael@0 | 2139 | #_typedArray "Prototype", \ |
michael@0 | 2140 | JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) | \ |
michael@0 | 2141 | JSCLASS_HAS_PRIVATE | \ |
michael@0 | 2142 | JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray), \ |
michael@0 | 2143 | JS_PropertyStub, /* addProperty */ \ |
michael@0 | 2144 | JS_DeletePropertyStub, /* delProperty */ \ |
michael@0 | 2145 | JS_PropertyStub, /* getProperty */ \ |
michael@0 | 2146 | JS_StrictPropertyStub, /* setProperty */ \ |
michael@0 | 2147 | JS_EnumerateStub, \ |
michael@0 | 2148 | JS_ResolveStub, \ |
michael@0 | 2149 | JS_ConvertStub \ |
michael@0 | 2150 | } |
michael@0 | 2151 | |
michael@0 | 2152 | #define IMPL_TYPED_ARRAY_FAST_CLASS(_typedArray) \ |
michael@0 | 2153 | { \ |
michael@0 | 2154 | #_typedArray, \ |
michael@0 | 2155 | JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) | \ |
michael@0 | 2156 | JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | \ |
michael@0 | 2157 | JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray), \ |
michael@0 | 2158 | JS_PropertyStub, /* addProperty */ \ |
michael@0 | 2159 | JS_DeletePropertyStub, /* delProperty */ \ |
michael@0 | 2160 | JS_PropertyStub, /* getProperty */ \ |
michael@0 | 2161 | JS_StrictPropertyStub, /* setProperty */ \ |
michael@0 | 2162 | JS_EnumerateStub, \ |
michael@0 | 2163 | JS_ResolveStub, \ |
michael@0 | 2164 | JS_ConvertStub, \ |
michael@0 | 2165 | nullptr, /* finalize */ \ |
michael@0 | 2166 | nullptr, /* call */ \ |
michael@0 | 2167 | nullptr, /* hasInstance */ \ |
michael@0 | 2168 | nullptr, /* construct */ \ |
michael@0 | 2169 | ArrayBufferViewObject::trace, /* trace */ \ |
michael@0 | 2170 | } |
michael@0 | 2171 | |
michael@0 | 2172 | template<class ArrayType> |
michael@0 | 2173 | static inline bool |
michael@0 | 2174 | InitTypedArrayClass(JSContext *cx) |
michael@0 | 2175 | { |
michael@0 | 2176 | Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal()); |
michael@0 | 2177 | if (global->isStandardClassResolved(ArrayType::key)) |
michael@0 | 2178 | return true; |
michael@0 | 2179 | |
michael@0 | 2180 | RootedObject proto(cx, global->createBlankPrototype(cx, ArrayType::protoClass())); |
michael@0 | 2181 | if (!proto) |
michael@0 | 2182 | return false; |
michael@0 | 2183 | |
michael@0 | 2184 | RootedFunction ctor(cx); |
michael@0 | 2185 | ctor = global->createConstructor(cx, ArrayType::class_constructor, |
michael@0 | 2186 | ClassName(ArrayType::key, cx), 3); |
michael@0 | 2187 | if (!ctor) |
michael@0 | 2188 | return false; |
michael@0 | 2189 | |
michael@0 | 2190 | if (!LinkConstructorAndPrototype(cx, ctor, proto)) |
michael@0 | 2191 | return false; |
michael@0 | 2192 | |
michael@0 | 2193 | RootedValue bytesValue(cx, Int32Value(ArrayType::BYTES_PER_ELEMENT)); |
michael@0 | 2194 | |
michael@0 | 2195 | if (!JSObject::defineProperty(cx, ctor, |
michael@0 | 2196 | cx->names().BYTES_PER_ELEMENT, bytesValue, |
michael@0 | 2197 | JS_PropertyStub, JS_StrictPropertyStub, |
michael@0 | 2198 | JSPROP_PERMANENT | JSPROP_READONLY) || |
michael@0 | 2199 | !JSObject::defineProperty(cx, proto, |
michael@0 | 2200 | cx->names().BYTES_PER_ELEMENT, bytesValue, |
michael@0 | 2201 | JS_PropertyStub, JS_StrictPropertyStub, |
michael@0 | 2202 | JSPROP_PERMANENT | JSPROP_READONLY)) |
michael@0 | 2203 | { |
michael@0 | 2204 | return false; |
michael@0 | 2205 | } |
michael@0 | 2206 | |
michael@0 | 2207 | if (!ArrayType::defineGetters(cx, proto)) |
michael@0 | 2208 | return false; |
michael@0 | 2209 | |
michael@0 | 2210 | if (!JS_DefineFunctions(cx, proto, ArrayType::jsfuncs)) |
michael@0 | 2211 | return false; |
michael@0 | 2212 | |
michael@0 | 2213 | RootedFunction fun(cx); |
michael@0 | 2214 | fun = |
michael@0 | 2215 | NewFunction(cx, NullPtr(), |
michael@0 | 2216 | ArrayBufferObject::createTypedArrayFromBuffer<typename ArrayType::ThisType>, |
michael@0 | 2217 | 0, JSFunction::NATIVE_FUN, global, NullPtr()); |
michael@0 | 2218 | if (!fun) |
michael@0 | 2219 | return false; |
michael@0 | 2220 | |
michael@0 | 2221 | if (!GlobalObject::initBuiltinConstructor(cx, global, ArrayType::key, ctor, proto)) |
michael@0 | 2222 | return false; |
michael@0 | 2223 | |
michael@0 | 2224 | global->setCreateArrayFromBuffer<typename ArrayType::ThisType>(fun); |
michael@0 | 2225 | |
michael@0 | 2226 | return true; |
michael@0 | 2227 | } |
michael@0 | 2228 | |
michael@0 | 2229 | IMPL_TYPED_ARRAY_STATICS(Int8Array); |
michael@0 | 2230 | IMPL_TYPED_ARRAY_STATICS(Uint8Array); |
michael@0 | 2231 | IMPL_TYPED_ARRAY_STATICS(Int16Array); |
michael@0 | 2232 | IMPL_TYPED_ARRAY_STATICS(Uint16Array); |
michael@0 | 2233 | IMPL_TYPED_ARRAY_STATICS(Int32Array); |
michael@0 | 2234 | IMPL_TYPED_ARRAY_STATICS(Uint32Array); |
michael@0 | 2235 | IMPL_TYPED_ARRAY_STATICS(Float32Array); |
michael@0 | 2236 | IMPL_TYPED_ARRAY_STATICS(Float64Array); |
michael@0 | 2237 | IMPL_TYPED_ARRAY_STATICS(Uint8ClampedArray); |
michael@0 | 2238 | |
michael@0 | 2239 | const Class TypedArrayObject::classes[ScalarTypeDescr::TYPE_MAX] = { |
michael@0 | 2240 | IMPL_TYPED_ARRAY_FAST_CLASS(Int8Array), |
michael@0 | 2241 | IMPL_TYPED_ARRAY_FAST_CLASS(Uint8Array), |
michael@0 | 2242 | IMPL_TYPED_ARRAY_FAST_CLASS(Int16Array), |
michael@0 | 2243 | IMPL_TYPED_ARRAY_FAST_CLASS(Uint16Array), |
michael@0 | 2244 | IMPL_TYPED_ARRAY_FAST_CLASS(Int32Array), |
michael@0 | 2245 | IMPL_TYPED_ARRAY_FAST_CLASS(Uint32Array), |
michael@0 | 2246 | IMPL_TYPED_ARRAY_FAST_CLASS(Float32Array), |
michael@0 | 2247 | IMPL_TYPED_ARRAY_FAST_CLASS(Float64Array), |
michael@0 | 2248 | IMPL_TYPED_ARRAY_FAST_CLASS(Uint8ClampedArray) |
michael@0 | 2249 | }; |
michael@0 | 2250 | |
michael@0 | 2251 | const Class TypedArrayObject::protoClasses[ScalarTypeDescr::TYPE_MAX] = { |
michael@0 | 2252 | IMPL_TYPED_ARRAY_PROTO_CLASS(Int8Array), |
michael@0 | 2253 | IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8Array), |
michael@0 | 2254 | IMPL_TYPED_ARRAY_PROTO_CLASS(Int16Array), |
michael@0 | 2255 | IMPL_TYPED_ARRAY_PROTO_CLASS(Uint16Array), |
michael@0 | 2256 | IMPL_TYPED_ARRAY_PROTO_CLASS(Int32Array), |
michael@0 | 2257 | IMPL_TYPED_ARRAY_PROTO_CLASS(Uint32Array), |
michael@0 | 2258 | IMPL_TYPED_ARRAY_PROTO_CLASS(Float32Array), |
michael@0 | 2259 | IMPL_TYPED_ARRAY_PROTO_CLASS(Float64Array), |
michael@0 | 2260 | IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8ClampedArray) |
michael@0 | 2261 | }; |
michael@0 | 2262 | |
michael@0 | 2263 | #define CHECK(t, a) { if (t == a::IsThisClass) return true; } |
michael@0 | 2264 | JS_FRIEND_API(bool) |
michael@0 | 2265 | js::IsTypedArrayThisCheck(JS::IsAcceptableThis test) |
michael@0 | 2266 | { |
michael@0 | 2267 | CHECK(test, Int8ArrayObject); |
michael@0 | 2268 | CHECK(test, Uint8ArrayObject); |
michael@0 | 2269 | CHECK(test, Int16ArrayObject); |
michael@0 | 2270 | CHECK(test, Uint16ArrayObject); |
michael@0 | 2271 | CHECK(test, Int32ArrayObject); |
michael@0 | 2272 | CHECK(test, Uint32ArrayObject); |
michael@0 | 2273 | CHECK(test, Float32ArrayObject); |
michael@0 | 2274 | CHECK(test, Float64ArrayObject); |
michael@0 | 2275 | CHECK(test, Uint8ClampedArrayObject); |
michael@0 | 2276 | return false; |
michael@0 | 2277 | } |
michael@0 | 2278 | #undef CHECK |
michael@0 | 2279 | |
michael@0 | 2280 | static JSObject * |
michael@0 | 2281 | InitArrayBufferClass(JSContext *cx) |
michael@0 | 2282 | { |
michael@0 | 2283 | Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal()); |
michael@0 | 2284 | if (global->isStandardClassResolved(JSProto_ArrayBuffer)) |
michael@0 | 2285 | return &global->getPrototype(JSProto_ArrayBuffer).toObject(); |
michael@0 | 2286 | |
michael@0 | 2287 | RootedObject arrayBufferProto(cx, global->createBlankPrototype(cx, &ArrayBufferObject::protoClass)); |
michael@0 | 2288 | if (!arrayBufferProto) |
michael@0 | 2289 | return nullptr; |
michael@0 | 2290 | |
michael@0 | 2291 | RootedFunction ctor(cx, global->createConstructor(cx, ArrayBufferObject::class_constructor, |
michael@0 | 2292 | cx->names().ArrayBuffer, 1)); |
michael@0 | 2293 | if (!ctor) |
michael@0 | 2294 | return nullptr; |
michael@0 | 2295 | |
michael@0 | 2296 | if (!LinkConstructorAndPrototype(cx, ctor, arrayBufferProto)) |
michael@0 | 2297 | return nullptr; |
michael@0 | 2298 | |
michael@0 | 2299 | RootedId byteLengthId(cx, NameToId(cx->names().byteLength)); |
michael@0 | 2300 | unsigned attrs = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT; |
michael@0 | 2301 | JSObject *getter = NewFunction(cx, NullPtr(), ArrayBufferObject::byteLengthGetter, 0, |
michael@0 | 2302 | JSFunction::NATIVE_FUN, global, NullPtr()); |
michael@0 | 2303 | if (!getter) |
michael@0 | 2304 | return nullptr; |
michael@0 | 2305 | |
michael@0 | 2306 | if (!DefineNativeProperty(cx, arrayBufferProto, byteLengthId, UndefinedHandleValue, |
michael@0 | 2307 | JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr, attrs)) |
michael@0 | 2308 | return nullptr; |
michael@0 | 2309 | |
michael@0 | 2310 | if (!JS_DefineFunctions(cx, ctor, ArrayBufferObject::jsstaticfuncs)) |
michael@0 | 2311 | return nullptr; |
michael@0 | 2312 | |
michael@0 | 2313 | if (!JS_DefineFunctions(cx, arrayBufferProto, ArrayBufferObject::jsfuncs)) |
michael@0 | 2314 | return nullptr; |
michael@0 | 2315 | |
michael@0 | 2316 | if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_ArrayBuffer, |
michael@0 | 2317 | ctor, arrayBufferProto)) |
michael@0 | 2318 | { |
michael@0 | 2319 | return nullptr; |
michael@0 | 2320 | } |
michael@0 | 2321 | |
michael@0 | 2322 | return arrayBufferProto; |
michael@0 | 2323 | } |
michael@0 | 2324 | |
michael@0 | 2325 | const Class DataViewObject::protoClass = { |
michael@0 | 2326 | "DataViewPrototype", |
michael@0 | 2327 | JSCLASS_HAS_PRIVATE | |
michael@0 | 2328 | JSCLASS_HAS_RESERVED_SLOTS(DataViewObject::RESERVED_SLOTS) | |
michael@0 | 2329 | JSCLASS_HAS_CACHED_PROTO(JSProto_DataView), |
michael@0 | 2330 | JS_PropertyStub, /* addProperty */ |
michael@0 | 2331 | JS_DeletePropertyStub, /* delProperty */ |
michael@0 | 2332 | JS_PropertyStub, /* getProperty */ |
michael@0 | 2333 | JS_StrictPropertyStub, /* setProperty */ |
michael@0 | 2334 | JS_EnumerateStub, |
michael@0 | 2335 | JS_ResolveStub, |
michael@0 | 2336 | JS_ConvertStub |
michael@0 | 2337 | }; |
michael@0 | 2338 | |
michael@0 | 2339 | const Class DataViewObject::class_ = { |
michael@0 | 2340 | "DataView", |
michael@0 | 2341 | JSCLASS_HAS_PRIVATE | |
michael@0 | 2342 | JSCLASS_IMPLEMENTS_BARRIERS | |
michael@0 | 2343 | JSCLASS_HAS_RESERVED_SLOTS(DataViewObject::RESERVED_SLOTS) | |
michael@0 | 2344 | JSCLASS_HAS_CACHED_PROTO(JSProto_DataView), |
michael@0 | 2345 | JS_PropertyStub, /* addProperty */ |
michael@0 | 2346 | JS_DeletePropertyStub, /* delProperty */ |
michael@0 | 2347 | JS_PropertyStub, /* getProperty */ |
michael@0 | 2348 | JS_StrictPropertyStub, /* setProperty */ |
michael@0 | 2349 | JS_EnumerateStub, |
michael@0 | 2350 | JS_ResolveStub, |
michael@0 | 2351 | JS_ConvertStub, |
michael@0 | 2352 | nullptr, /* finalize */ |
michael@0 | 2353 | nullptr, /* call */ |
michael@0 | 2354 | nullptr, /* hasInstance */ |
michael@0 | 2355 | nullptr, /* construct */ |
michael@0 | 2356 | ArrayBufferViewObject::trace, /* trace */ |
michael@0 | 2357 | }; |
michael@0 | 2358 | |
michael@0 | 2359 | const JSFunctionSpec DataViewObject::jsfuncs[] = { |
michael@0 | 2360 | JS_FN("getInt8", DataViewObject::fun_getInt8, 1,0), |
michael@0 | 2361 | JS_FN("getUint8", DataViewObject::fun_getUint8, 1,0), |
michael@0 | 2362 | JS_FN("getInt16", DataViewObject::fun_getInt16, 2,0), |
michael@0 | 2363 | JS_FN("getUint16", DataViewObject::fun_getUint16, 2,0), |
michael@0 | 2364 | JS_FN("getInt32", DataViewObject::fun_getInt32, 2,0), |
michael@0 | 2365 | JS_FN("getUint32", DataViewObject::fun_getUint32, 2,0), |
michael@0 | 2366 | JS_FN("getFloat32", DataViewObject::fun_getFloat32, 2,0), |
michael@0 | 2367 | JS_FN("getFloat64", DataViewObject::fun_getFloat64, 2,0), |
michael@0 | 2368 | JS_FN("setInt8", DataViewObject::fun_setInt8, 2,0), |
michael@0 | 2369 | JS_FN("setUint8", DataViewObject::fun_setUint8, 2,0), |
michael@0 | 2370 | JS_FN("setInt16", DataViewObject::fun_setInt16, 3,0), |
michael@0 | 2371 | JS_FN("setUint16", DataViewObject::fun_setUint16, 3,0), |
michael@0 | 2372 | JS_FN("setInt32", DataViewObject::fun_setInt32, 3,0), |
michael@0 | 2373 | JS_FN("setUint32", DataViewObject::fun_setUint32, 3,0), |
michael@0 | 2374 | JS_FN("setFloat32", DataViewObject::fun_setFloat32, 3,0), |
michael@0 | 2375 | JS_FN("setFloat64", DataViewObject::fun_setFloat64, 3,0), |
michael@0 | 2376 | JS_FS_END |
michael@0 | 2377 | }; |
michael@0 | 2378 | |
michael@0 | 2379 | template<Value ValueGetter(DataViewObject *view)> |
michael@0 | 2380 | bool |
michael@0 | 2381 | DataViewObject::getterImpl(JSContext *cx, CallArgs args) |
michael@0 | 2382 | { |
michael@0 | 2383 | args.rval().set(ValueGetter(&args.thisv().toObject().as<DataViewObject>())); |
michael@0 | 2384 | return true; |
michael@0 | 2385 | } |
michael@0 | 2386 | |
michael@0 | 2387 | template<Value ValueGetter(DataViewObject *view)> |
michael@0 | 2388 | bool |
michael@0 | 2389 | DataViewObject::getter(JSContext *cx, unsigned argc, Value *vp) |
michael@0 | 2390 | { |
michael@0 | 2391 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 2392 | return CallNonGenericMethod<is, getterImpl<ValueGetter> >(cx, args); |
michael@0 | 2393 | } |
michael@0 | 2394 | |
michael@0 | 2395 | template<Value ValueGetter(DataViewObject *view)> |
michael@0 | 2396 | bool |
michael@0 | 2397 | DataViewObject::defineGetter(JSContext *cx, PropertyName *name, HandleObject proto) |
michael@0 | 2398 | { |
michael@0 | 2399 | RootedId id(cx, NameToId(name)); |
michael@0 | 2400 | unsigned attrs = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT; |
michael@0 | 2401 | |
michael@0 | 2402 | Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal()); |
michael@0 | 2403 | JSObject *getter = NewFunction(cx, NullPtr(), DataViewObject::getter<ValueGetter>, 0, |
michael@0 | 2404 | JSFunction::NATIVE_FUN, global, NullPtr()); |
michael@0 | 2405 | if (!getter) |
michael@0 | 2406 | return false; |
michael@0 | 2407 | |
michael@0 | 2408 | return DefineNativeProperty(cx, proto, id, UndefinedHandleValue, |
michael@0 | 2409 | JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr, attrs); |
michael@0 | 2410 | } |
michael@0 | 2411 | |
michael@0 | 2412 | /* static */ bool |
michael@0 | 2413 | DataViewObject::initClass(JSContext *cx) |
michael@0 | 2414 | { |
michael@0 | 2415 | Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal()); |
michael@0 | 2416 | if (global->isStandardClassResolved(JSProto_DataView)) |
michael@0 | 2417 | return true; |
michael@0 | 2418 | |
michael@0 | 2419 | RootedObject proto(cx, global->createBlankPrototype(cx, &DataViewObject::protoClass)); |
michael@0 | 2420 | if (!proto) |
michael@0 | 2421 | return false; |
michael@0 | 2422 | |
michael@0 | 2423 | RootedFunction ctor(cx, global->createConstructor(cx, DataViewObject::class_constructor, |
michael@0 | 2424 | cx->names().DataView, 3)); |
michael@0 | 2425 | if (!ctor) |
michael@0 | 2426 | return false; |
michael@0 | 2427 | |
michael@0 | 2428 | if (!LinkConstructorAndPrototype(cx, ctor, proto)) |
michael@0 | 2429 | return false; |
michael@0 | 2430 | |
michael@0 | 2431 | if (!defineGetter<bufferValue>(cx, cx->names().buffer, proto)) |
michael@0 | 2432 | return false; |
michael@0 | 2433 | |
michael@0 | 2434 | if (!defineGetter<byteLengthValue>(cx, cx->names().byteLength, proto)) |
michael@0 | 2435 | return false; |
michael@0 | 2436 | |
michael@0 | 2437 | if (!defineGetter<byteOffsetValue>(cx, cx->names().byteOffset, proto)) |
michael@0 | 2438 | return false; |
michael@0 | 2439 | |
michael@0 | 2440 | if (!JS_DefineFunctions(cx, proto, DataViewObject::jsfuncs)) |
michael@0 | 2441 | return false; |
michael@0 | 2442 | |
michael@0 | 2443 | /* |
michael@0 | 2444 | * Create a helper function to implement the craziness of |
michael@0 | 2445 | * |new DataView(new otherWindow.ArrayBuffer())|, and install it in the |
michael@0 | 2446 | * global for use by the DataViewObject constructor. |
michael@0 | 2447 | */ |
michael@0 | 2448 | RootedFunction fun(cx, NewFunction(cx, NullPtr(), ArrayBufferObject::createDataViewForThis, |
michael@0 | 2449 | 0, JSFunction::NATIVE_FUN, global, NullPtr())); |
michael@0 | 2450 | if (!fun) |
michael@0 | 2451 | return false; |
michael@0 | 2452 | |
michael@0 | 2453 | if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_DataView, ctor, proto)) |
michael@0 | 2454 | return false; |
michael@0 | 2455 | |
michael@0 | 2456 | global->setCreateDataViewForThis(fun); |
michael@0 | 2457 | |
michael@0 | 2458 | return true; |
michael@0 | 2459 | } |
michael@0 | 2460 | |
michael@0 | 2461 | void |
michael@0 | 2462 | DataViewObject::neuter(void *newData) |
michael@0 | 2463 | { |
michael@0 | 2464 | setSlot(BYTELENGTH_SLOT, Int32Value(0)); |
michael@0 | 2465 | setSlot(BYTEOFFSET_SLOT, Int32Value(0)); |
michael@0 | 2466 | setPrivate(newData); |
michael@0 | 2467 | } |
michael@0 | 2468 | |
michael@0 | 2469 | JSObject * |
michael@0 | 2470 | js_InitTypedArrayClasses(JSContext *cx, HandleObject obj) |
michael@0 | 2471 | { |
michael@0 | 2472 | if (!InitTypedArrayClass<Int8ArrayObject>(cx) || |
michael@0 | 2473 | !InitTypedArrayClass<Uint8ArrayObject>(cx) || |
michael@0 | 2474 | !InitTypedArrayClass<Int16ArrayObject>(cx) || |
michael@0 | 2475 | !InitTypedArrayClass<Uint16ArrayObject>(cx) || |
michael@0 | 2476 | !InitTypedArrayClass<Int32ArrayObject>(cx) || |
michael@0 | 2477 | !InitTypedArrayClass<Uint32ArrayObject>(cx) || |
michael@0 | 2478 | !InitTypedArrayClass<Float32ArrayObject>(cx) || |
michael@0 | 2479 | !InitTypedArrayClass<Float64ArrayObject>(cx) || |
michael@0 | 2480 | !InitTypedArrayClass<Uint8ClampedArrayObject>(cx) || |
michael@0 | 2481 | !DataViewObject::initClass(cx)) |
michael@0 | 2482 | { |
michael@0 | 2483 | return nullptr; |
michael@0 | 2484 | } |
michael@0 | 2485 | |
michael@0 | 2486 | return InitArrayBufferClass(cx); |
michael@0 | 2487 | } |
michael@0 | 2488 | |
michael@0 | 2489 | bool |
michael@0 | 2490 | js::IsTypedArrayConstructor(HandleValue v, uint32_t type) |
michael@0 | 2491 | { |
michael@0 | 2492 | switch (type) { |
michael@0 | 2493 | case ScalarTypeDescr::TYPE_INT8: |
michael@0 | 2494 | return IsNativeFunction(v, Int8ArrayObject::class_constructor); |
michael@0 | 2495 | case ScalarTypeDescr::TYPE_UINT8: |
michael@0 | 2496 | return IsNativeFunction(v, Uint8ArrayObject::class_constructor); |
michael@0 | 2497 | case ScalarTypeDescr::TYPE_INT16: |
michael@0 | 2498 | return IsNativeFunction(v, Int16ArrayObject::class_constructor); |
michael@0 | 2499 | case ScalarTypeDescr::TYPE_UINT16: |
michael@0 | 2500 | return IsNativeFunction(v, Uint16ArrayObject::class_constructor); |
michael@0 | 2501 | case ScalarTypeDescr::TYPE_INT32: |
michael@0 | 2502 | return IsNativeFunction(v, Int32ArrayObject::class_constructor); |
michael@0 | 2503 | case ScalarTypeDescr::TYPE_UINT32: |
michael@0 | 2504 | return IsNativeFunction(v, Uint32ArrayObject::class_constructor); |
michael@0 | 2505 | case ScalarTypeDescr::TYPE_FLOAT32: |
michael@0 | 2506 | return IsNativeFunction(v, Float32ArrayObject::class_constructor); |
michael@0 | 2507 | case ScalarTypeDescr::TYPE_FLOAT64: |
michael@0 | 2508 | return IsNativeFunction(v, Float64ArrayObject::class_constructor); |
michael@0 | 2509 | case ScalarTypeDescr::TYPE_UINT8_CLAMPED: |
michael@0 | 2510 | return IsNativeFunction(v, Uint8ClampedArrayObject::class_constructor); |
michael@0 | 2511 | } |
michael@0 | 2512 | MOZ_ASSUME_UNREACHABLE("unexpected typed array type"); |
michael@0 | 2513 | } |
michael@0 | 2514 | |
michael@0 | 2515 | bool |
michael@0 | 2516 | js::IsTypedArrayBuffer(HandleValue v) |
michael@0 | 2517 | { |
michael@0 | 2518 | return v.isObject() && |
michael@0 | 2519 | (v.toObject().is<ArrayBufferObject>() || |
michael@0 | 2520 | v.toObject().is<SharedArrayBufferObject>()); |
michael@0 | 2521 | } |
michael@0 | 2522 | |
michael@0 | 2523 | ArrayBufferObject & |
michael@0 | 2524 | js::AsTypedArrayBuffer(HandleValue v) |
michael@0 | 2525 | { |
michael@0 | 2526 | JS_ASSERT(IsTypedArrayBuffer(v)); |
michael@0 | 2527 | if (v.toObject().is<ArrayBufferObject>()) |
michael@0 | 2528 | return v.toObject().as<ArrayBufferObject>(); |
michael@0 | 2529 | return v.toObject().as<SharedArrayBufferObject>(); |
michael@0 | 2530 | } |
michael@0 | 2531 | |
michael@0 | 2532 | bool |
michael@0 | 2533 | js::StringIsTypedArrayIndex(JSLinearString *str, uint64_t *indexp) |
michael@0 | 2534 | { |
michael@0 | 2535 | const jschar *s = str->chars(); |
michael@0 | 2536 | const jschar *end = s + str->length(); |
michael@0 | 2537 | |
michael@0 | 2538 | if (s == end) |
michael@0 | 2539 | return false; |
michael@0 | 2540 | |
michael@0 | 2541 | bool negative = false; |
michael@0 | 2542 | if (*s == '-') { |
michael@0 | 2543 | negative = true; |
michael@0 | 2544 | if (++s == end) |
michael@0 | 2545 | return false; |
michael@0 | 2546 | } |
michael@0 | 2547 | |
michael@0 | 2548 | if (!JS7_ISDEC(*s)) |
michael@0 | 2549 | return false; |
michael@0 | 2550 | |
michael@0 | 2551 | uint64_t index = 0; |
michael@0 | 2552 | uint32_t digit = JS7_UNDEC(*s++); |
michael@0 | 2553 | |
michael@0 | 2554 | /* Don't allow leading zeros. */ |
michael@0 | 2555 | if (digit == 0 && s != end) |
michael@0 | 2556 | return false; |
michael@0 | 2557 | |
michael@0 | 2558 | index = digit; |
michael@0 | 2559 | |
michael@0 | 2560 | for (; s < end; s++) { |
michael@0 | 2561 | if (!JS7_ISDEC(*s)) |
michael@0 | 2562 | return false; |
michael@0 | 2563 | |
michael@0 | 2564 | digit = JS7_UNDEC(*s); |
michael@0 | 2565 | |
michael@0 | 2566 | /* Watch for overflows. */ |
michael@0 | 2567 | if ((UINT64_MAX - digit) / 10 < index) |
michael@0 | 2568 | index = UINT64_MAX; |
michael@0 | 2569 | else |
michael@0 | 2570 | index = 10 * index + digit; |
michael@0 | 2571 | } |
michael@0 | 2572 | |
michael@0 | 2573 | if (negative) |
michael@0 | 2574 | *indexp = UINT64_MAX; |
michael@0 | 2575 | else |
michael@0 | 2576 | *indexp = index; |
michael@0 | 2577 | return true; |
michael@0 | 2578 | } |
michael@0 | 2579 | |
michael@0 | 2580 | /* JS Friend API */ |
michael@0 | 2581 | |
michael@0 | 2582 | JS_FRIEND_API(bool) |
michael@0 | 2583 | JS_IsTypedArrayObject(JSObject *obj) |
michael@0 | 2584 | { |
michael@0 | 2585 | obj = CheckedUnwrap(obj); |
michael@0 | 2586 | return obj ? obj->is<TypedArrayObject>() : false; |
michael@0 | 2587 | } |
michael@0 | 2588 | |
michael@0 | 2589 | JS_FRIEND_API(uint32_t) |
michael@0 | 2590 | JS_GetTypedArrayLength(JSObject *obj) |
michael@0 | 2591 | { |
michael@0 | 2592 | obj = CheckedUnwrap(obj); |
michael@0 | 2593 | if (!obj) |
michael@0 | 2594 | return 0; |
michael@0 | 2595 | return obj->as<TypedArrayObject>().length(); |
michael@0 | 2596 | } |
michael@0 | 2597 | |
michael@0 | 2598 | JS_FRIEND_API(uint32_t) |
michael@0 | 2599 | JS_GetTypedArrayByteOffset(JSObject *obj) |
michael@0 | 2600 | { |
michael@0 | 2601 | obj = CheckedUnwrap(obj); |
michael@0 | 2602 | if (!obj) |
michael@0 | 2603 | return 0; |
michael@0 | 2604 | return obj->as<TypedArrayObject>().byteOffset(); |
michael@0 | 2605 | } |
michael@0 | 2606 | |
michael@0 | 2607 | JS_FRIEND_API(uint32_t) |
michael@0 | 2608 | JS_GetTypedArrayByteLength(JSObject *obj) |
michael@0 | 2609 | { |
michael@0 | 2610 | obj = CheckedUnwrap(obj); |
michael@0 | 2611 | if (!obj) |
michael@0 | 2612 | return 0; |
michael@0 | 2613 | return obj->as<TypedArrayObject>().byteLength(); |
michael@0 | 2614 | } |
michael@0 | 2615 | |
michael@0 | 2616 | JS_FRIEND_API(JSArrayBufferViewType) |
michael@0 | 2617 | JS_GetArrayBufferViewType(JSObject *obj) |
michael@0 | 2618 | { |
michael@0 | 2619 | obj = CheckedUnwrap(obj); |
michael@0 | 2620 | if (!obj) |
michael@0 | 2621 | return ArrayBufferView::TYPE_MAX; |
michael@0 | 2622 | |
michael@0 | 2623 | if (obj->is<TypedArrayObject>()) |
michael@0 | 2624 | return static_cast<JSArrayBufferViewType>(obj->as<TypedArrayObject>().type()); |
michael@0 | 2625 | else if (obj->is<DataViewObject>()) |
michael@0 | 2626 | return ArrayBufferView::TYPE_DATAVIEW; |
michael@0 | 2627 | MOZ_ASSUME_UNREACHABLE("invalid ArrayBufferView type"); |
michael@0 | 2628 | } |
michael@0 | 2629 | |
michael@0 | 2630 | JS_FRIEND_API(int8_t *) |
michael@0 | 2631 | JS_GetInt8ArrayData(JSObject *obj) |
michael@0 | 2632 | { |
michael@0 | 2633 | obj = CheckedUnwrap(obj); |
michael@0 | 2634 | if (!obj) |
michael@0 | 2635 | return nullptr; |
michael@0 | 2636 | TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
michael@0 | 2637 | JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_INT8); |
michael@0 | 2638 | return static_cast<int8_t *>(tarr->viewData()); |
michael@0 | 2639 | } |
michael@0 | 2640 | |
michael@0 | 2641 | JS_FRIEND_API(uint8_t *) |
michael@0 | 2642 | JS_GetUint8ArrayData(JSObject *obj) |
michael@0 | 2643 | { |
michael@0 | 2644 | obj = CheckedUnwrap(obj); |
michael@0 | 2645 | if (!obj) |
michael@0 | 2646 | return nullptr; |
michael@0 | 2647 | TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
michael@0 | 2648 | JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_UINT8); |
michael@0 | 2649 | return static_cast<uint8_t *>(tarr->viewData()); |
michael@0 | 2650 | } |
michael@0 | 2651 | |
michael@0 | 2652 | JS_FRIEND_API(uint8_t *) |
michael@0 | 2653 | JS_GetUint8ClampedArrayData(JSObject *obj) |
michael@0 | 2654 | { |
michael@0 | 2655 | obj = CheckedUnwrap(obj); |
michael@0 | 2656 | if (!obj) |
michael@0 | 2657 | return nullptr; |
michael@0 | 2658 | TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
michael@0 | 2659 | JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_UINT8_CLAMPED); |
michael@0 | 2660 | return static_cast<uint8_t *>(tarr->viewData()); |
michael@0 | 2661 | } |
michael@0 | 2662 | |
michael@0 | 2663 | JS_FRIEND_API(int16_t *) |
michael@0 | 2664 | JS_GetInt16ArrayData(JSObject *obj) |
michael@0 | 2665 | { |
michael@0 | 2666 | obj = CheckedUnwrap(obj); |
michael@0 | 2667 | if (!obj) |
michael@0 | 2668 | return nullptr; |
michael@0 | 2669 | TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
michael@0 | 2670 | JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_INT16); |
michael@0 | 2671 | return static_cast<int16_t *>(tarr->viewData()); |
michael@0 | 2672 | } |
michael@0 | 2673 | |
michael@0 | 2674 | JS_FRIEND_API(uint16_t *) |
michael@0 | 2675 | JS_GetUint16ArrayData(JSObject *obj) |
michael@0 | 2676 | { |
michael@0 | 2677 | obj = CheckedUnwrap(obj); |
michael@0 | 2678 | if (!obj) |
michael@0 | 2679 | return nullptr; |
michael@0 | 2680 | TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
michael@0 | 2681 | JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_UINT16); |
michael@0 | 2682 | return static_cast<uint16_t *>(tarr->viewData()); |
michael@0 | 2683 | } |
michael@0 | 2684 | |
michael@0 | 2685 | JS_FRIEND_API(int32_t *) |
michael@0 | 2686 | JS_GetInt32ArrayData(JSObject *obj) |
michael@0 | 2687 | { |
michael@0 | 2688 | obj = CheckedUnwrap(obj); |
michael@0 | 2689 | if (!obj) |
michael@0 | 2690 | return nullptr; |
michael@0 | 2691 | TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
michael@0 | 2692 | JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_INT32); |
michael@0 | 2693 | return static_cast<int32_t *>(tarr->viewData()); |
michael@0 | 2694 | } |
michael@0 | 2695 | |
michael@0 | 2696 | JS_FRIEND_API(uint32_t *) |
michael@0 | 2697 | JS_GetUint32ArrayData(JSObject *obj) |
michael@0 | 2698 | { |
michael@0 | 2699 | obj = CheckedUnwrap(obj); |
michael@0 | 2700 | if (!obj) |
michael@0 | 2701 | return nullptr; |
michael@0 | 2702 | TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
michael@0 | 2703 | JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_UINT32); |
michael@0 | 2704 | return static_cast<uint32_t *>(tarr->viewData()); |
michael@0 | 2705 | } |
michael@0 | 2706 | |
michael@0 | 2707 | JS_FRIEND_API(float *) |
michael@0 | 2708 | JS_GetFloat32ArrayData(JSObject *obj) |
michael@0 | 2709 | { |
michael@0 | 2710 | obj = CheckedUnwrap(obj); |
michael@0 | 2711 | if (!obj) |
michael@0 | 2712 | return nullptr; |
michael@0 | 2713 | TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
michael@0 | 2714 | JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_FLOAT32); |
michael@0 | 2715 | return static_cast<float *>(tarr->viewData()); |
michael@0 | 2716 | } |
michael@0 | 2717 | |
michael@0 | 2718 | JS_FRIEND_API(double *) |
michael@0 | 2719 | JS_GetFloat64ArrayData(JSObject *obj) |
michael@0 | 2720 | { |
michael@0 | 2721 | obj = CheckedUnwrap(obj); |
michael@0 | 2722 | if (!obj) |
michael@0 | 2723 | return nullptr; |
michael@0 | 2724 | TypedArrayObject *tarr = &obj->as<TypedArrayObject>(); |
michael@0 | 2725 | JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_FLOAT64); |
michael@0 | 2726 | return static_cast<double *>(tarr->viewData()); |
michael@0 | 2727 | } |
michael@0 | 2728 | |
michael@0 | 2729 | JS_FRIEND_API(bool) |
michael@0 | 2730 | JS_IsDataViewObject(JSObject *obj) |
michael@0 | 2731 | { |
michael@0 | 2732 | obj = CheckedUnwrap(obj); |
michael@0 | 2733 | return obj ? obj->is<DataViewObject>() : false; |
michael@0 | 2734 | } |
michael@0 | 2735 | |
michael@0 | 2736 | JS_FRIEND_API(uint32_t) |
michael@0 | 2737 | JS_GetDataViewByteOffset(JSObject *obj) |
michael@0 | 2738 | { |
michael@0 | 2739 | obj = CheckedUnwrap(obj); |
michael@0 | 2740 | if (!obj) |
michael@0 | 2741 | return 0; |
michael@0 | 2742 | return obj->as<DataViewObject>().byteOffset(); |
michael@0 | 2743 | } |
michael@0 | 2744 | |
michael@0 | 2745 | JS_FRIEND_API(void *) |
michael@0 | 2746 | JS_GetDataViewData(JSObject *obj) |
michael@0 | 2747 | { |
michael@0 | 2748 | obj = CheckedUnwrap(obj); |
michael@0 | 2749 | if (!obj) |
michael@0 | 2750 | return nullptr; |
michael@0 | 2751 | return obj->as<DataViewObject>().dataPointer(); |
michael@0 | 2752 | } |
michael@0 | 2753 | |
michael@0 | 2754 | JS_FRIEND_API(uint32_t) |
michael@0 | 2755 | JS_GetDataViewByteLength(JSObject *obj) |
michael@0 | 2756 | { |
michael@0 | 2757 | obj = CheckedUnwrap(obj); |
michael@0 | 2758 | if (!obj) |
michael@0 | 2759 | return 0; |
michael@0 | 2760 | return obj->as<DataViewObject>().byteLength(); |
michael@0 | 2761 | } |