js/src/vm/TypedArrayObject.cpp

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

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

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

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "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 }

mercurial