michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "vm/TypedArrayObject.h" michael@0: michael@0: #include "mozilla/Alignment.h" michael@0: #include "mozilla/FloatingPoint.h" michael@0: #include "mozilla/PodOperations.h" michael@0: michael@0: #include michael@0: #ifndef XP_WIN michael@0: # include michael@0: #endif michael@0: michael@0: #include "jsapi.h" michael@0: #include "jsarray.h" michael@0: #include "jscntxt.h" michael@0: #include "jscpucfg.h" michael@0: #include "jsnum.h" michael@0: #include "jsobj.h" michael@0: #include "jstypes.h" michael@0: #include "jsutil.h" michael@0: #ifdef XP_WIN michael@0: # include "jswin.h" michael@0: #endif michael@0: #include "jswrapper.h" michael@0: michael@0: #include "gc/Barrier.h" michael@0: #include "gc/Marking.h" michael@0: #include "jit/AsmJS.h" michael@0: #include "jit/AsmJSModule.h" michael@0: #include "vm/ArrayBufferObject.h" michael@0: #include "vm/GlobalObject.h" michael@0: #include "vm/Interpreter.h" michael@0: #include "vm/NumericConversions.h" michael@0: #include "vm/SharedArrayObject.h" michael@0: #include "vm/WrapperObject.h" michael@0: michael@0: #include "jsatominlines.h" michael@0: #include "jsinferinlines.h" michael@0: #include "jsobjinlines.h" michael@0: michael@0: #include "vm/Shape-inl.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::gc; michael@0: using namespace js::types; michael@0: michael@0: using mozilla::IsNaN; michael@0: using mozilla::NegativeInfinity; michael@0: using mozilla::PodCopy; michael@0: using mozilla::PositiveInfinity; michael@0: using JS::CanonicalizeNaN; michael@0: using JS::GenericNaN; michael@0: michael@0: static bool michael@0: ValueIsLength(const Value &v, uint32_t *len) michael@0: { michael@0: if (v.isInt32()) { michael@0: int32_t i = v.toInt32(); michael@0: if (i < 0) michael@0: return false; michael@0: *len = i; michael@0: return true; michael@0: } michael@0: michael@0: if (v.isDouble()) { michael@0: double d = v.toDouble(); michael@0: if (IsNaN(d)) michael@0: return false; michael@0: michael@0: uint32_t length = uint32_t(d); michael@0: if (d != double(length)) michael@0: return false; michael@0: michael@0: *len = length; michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: /* michael@0: * TypedArrayObject michael@0: * michael@0: * The non-templated base class for the specific typed implementations. michael@0: * This class holds all the member variables that are used by michael@0: * the subclasses. michael@0: */ michael@0: michael@0: void michael@0: TypedArrayObject::neuter(void *newData) michael@0: { michael@0: setSlot(LENGTH_SLOT, Int32Value(0)); michael@0: setSlot(BYTELENGTH_SLOT, Int32Value(0)); michael@0: setSlot(BYTEOFFSET_SLOT, Int32Value(0)); michael@0: setPrivate(newData); michael@0: } michael@0: michael@0: ArrayBufferObject * michael@0: TypedArrayObject::sharedBuffer() const michael@0: { michael@0: return &bufferValue(const_cast(this)).toObject().as(); michael@0: } michael@0: michael@0: /* static */ bool michael@0: TypedArrayObject::ensureHasBuffer(JSContext *cx, Handle tarray) michael@0: { michael@0: if (tarray->buffer()) michael@0: return true; michael@0: michael@0: Rooted buffer(cx, ArrayBufferObject::create(cx, tarray->byteLength())); michael@0: if (!buffer) michael@0: return false; michael@0: michael@0: buffer->addView(tarray); michael@0: michael@0: memcpy(buffer->dataPointer(), tarray->viewData(), tarray->byteLength()); michael@0: InitArrayBufferViewDataPointer(tarray, buffer, 0); michael@0: michael@0: tarray->setSlot(BUFFER_SLOT, ObjectValue(*buffer)); michael@0: return true; michael@0: } michael@0: michael@0: /* static */ int michael@0: TypedArrayObject::lengthOffset() michael@0: { michael@0: return JSObject::getFixedSlotOffset(LENGTH_SLOT); michael@0: } michael@0: michael@0: /* static */ int michael@0: TypedArrayObject::dataOffset() michael@0: { michael@0: return JSObject::getPrivateDataOffset(DATA_SLOT); michael@0: } michael@0: michael@0: /* Helper clamped uint8_t type */ michael@0: michael@0: uint32_t JS_FASTCALL michael@0: js::ClampDoubleToUint8(const double x) michael@0: { michael@0: // Not < so that NaN coerces to 0 michael@0: if (!(x >= 0)) michael@0: return 0; michael@0: michael@0: if (x > 255) michael@0: return 255; michael@0: michael@0: double toTruncate = x + 0.5; michael@0: uint8_t y = uint8_t(toTruncate); michael@0: michael@0: /* michael@0: * now val is rounded to nearest, ties rounded up. We want michael@0: * rounded to nearest ties to even, so check whether we had a michael@0: * tie. michael@0: */ michael@0: if (y == toTruncate) { michael@0: /* michael@0: * It was a tie (since adding 0.5 gave us the exact integer michael@0: * we want). Since we rounded up, we either already have an michael@0: * even number or we have an odd number but the number we michael@0: * want is one less. So just unconditionally masking out the michael@0: * ones bit should do the trick to get us the value we michael@0: * want. michael@0: */ michael@0: return y & ~1; michael@0: } michael@0: michael@0: return y; michael@0: } michael@0: michael@0: template static inline const int TypeIDOfType(); michael@0: template<> inline const int TypeIDOfType() { return ScalarTypeDescr::TYPE_INT8; } michael@0: template<> inline const int TypeIDOfType() { return ScalarTypeDescr::TYPE_UINT8; } michael@0: template<> inline const int TypeIDOfType() { return ScalarTypeDescr::TYPE_INT16; } michael@0: template<> inline const int TypeIDOfType() { return ScalarTypeDescr::TYPE_UINT16; } michael@0: template<> inline const int TypeIDOfType() { return ScalarTypeDescr::TYPE_INT32; } michael@0: template<> inline const int TypeIDOfType() { return ScalarTypeDescr::TYPE_UINT32; } michael@0: template<> inline const int TypeIDOfType() { return ScalarTypeDescr::TYPE_FLOAT32; } michael@0: template<> inline const int TypeIDOfType() { return ScalarTypeDescr::TYPE_FLOAT64; } michael@0: template<> inline const int TypeIDOfType() { return ScalarTypeDescr::TYPE_UINT8_CLAMPED; } michael@0: michael@0: template michael@0: static inline JSObject * michael@0: NewArray(JSContext *cx, uint32_t nelements); michael@0: michael@0: namespace { michael@0: michael@0: template michael@0: class TypedArrayObjectTemplate : public TypedArrayObject michael@0: { michael@0: public: michael@0: typedef NativeType ThisType; michael@0: typedef TypedArrayObjectTemplate ThisTypedArrayObject; michael@0: static const int ArrayTypeID() { return TypeIDOfType(); } michael@0: static const bool ArrayTypeIsUnsigned() { return TypeIsUnsigned(); } michael@0: static const bool ArrayTypeIsFloatingPoint() { return TypeIsFloatingPoint(); } michael@0: michael@0: static const size_t BYTES_PER_ELEMENT = sizeof(ThisType); michael@0: michael@0: static inline const Class *protoClass() michael@0: { michael@0: return &TypedArrayObject::protoClasses[ArrayTypeID()]; michael@0: } michael@0: michael@0: static inline const Class *instanceClass() michael@0: { michael@0: return &TypedArrayObject::classes[ArrayTypeID()]; michael@0: } michael@0: michael@0: static bool is(HandleValue v) { michael@0: return v.isObject() && v.toObject().hasClass(instanceClass()); michael@0: } michael@0: michael@0: static void michael@0: setIndexValue(TypedArrayObject &tarray, uint32_t index, double d) michael@0: { michael@0: // If the array is an integer array, we only handle up to michael@0: // 32-bit ints from this point on. if we want to handle michael@0: // 64-bit ints, we'll need some changes. michael@0: michael@0: // Assign based on characteristics of the destination type michael@0: if (ArrayTypeIsFloatingPoint()) { michael@0: setIndex(tarray, index, NativeType(d)); michael@0: } else if (ArrayTypeIsUnsigned()) { michael@0: JS_ASSERT(sizeof(NativeType) <= 4); michael@0: uint32_t n = ToUint32(d); michael@0: setIndex(tarray, index, NativeType(n)); michael@0: } else if (ArrayTypeID() == ScalarTypeDescr::TYPE_UINT8_CLAMPED) { michael@0: // The uint8_clamped type has a special rounding converter michael@0: // for doubles. michael@0: setIndex(tarray, index, NativeType(d)); michael@0: } else { michael@0: JS_ASSERT(sizeof(NativeType) <= 4); michael@0: int32_t n = ToInt32(d); michael@0: setIndex(tarray, index, NativeType(n)); michael@0: } michael@0: } michael@0: michael@0: static TypedArrayObject * michael@0: makeProtoInstance(JSContext *cx, HandleObject proto, AllocKind allocKind) michael@0: { michael@0: JS_ASSERT(proto); michael@0: michael@0: RootedObject obj(cx, NewBuiltinClassInstance(cx, instanceClass(), allocKind)); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: types::TypeObject *type = cx->getNewType(obj->getClass(), proto.get()); michael@0: if (!type) michael@0: return nullptr; michael@0: obj->setType(type); michael@0: michael@0: return &obj->as(); michael@0: } michael@0: michael@0: static TypedArrayObject * michael@0: makeTypedInstance(JSContext *cx, uint32_t len, AllocKind allocKind) michael@0: { michael@0: if (len * sizeof(NativeType) >= TypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH) { michael@0: return &NewBuiltinClassInstance(cx, instanceClass(), allocKind, michael@0: SingletonObject)->as(); michael@0: } michael@0: michael@0: jsbytecode *pc; michael@0: RootedScript script(cx, cx->currentScript(&pc)); michael@0: NewObjectKind newKind = script michael@0: ? UseNewTypeForInitializer(script, pc, instanceClass()) michael@0: : GenericObject; michael@0: RootedObject obj(cx, NewBuiltinClassInstance(cx, instanceClass(), allocKind, newKind)); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: if (script) { michael@0: if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind)) michael@0: return nullptr; michael@0: } michael@0: michael@0: return &obj->as(); michael@0: } michael@0: michael@0: static JSObject * michael@0: makeInstance(JSContext *cx, Handle buffer, uint32_t byteOffset, uint32_t len, michael@0: HandleObject proto) michael@0: { michael@0: JS_ASSERT_IF(!buffer, byteOffset == 0); michael@0: michael@0: gc::AllocKind allocKind = buffer michael@0: ? GetGCObjectKind(instanceClass()) michael@0: : AllocKindForLazyBuffer(len * sizeof(NativeType)); michael@0: michael@0: Rooted obj(cx); michael@0: if (proto) michael@0: obj = makeProtoInstance(cx, proto, allocKind); michael@0: else michael@0: obj = makeTypedInstance(cx, len, allocKind); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: obj->setSlot(TYPE_SLOT, Int32Value(ArrayTypeID())); michael@0: obj->setSlot(BUFFER_SLOT, ObjectOrNullValue(buffer)); michael@0: michael@0: if (buffer) { michael@0: InitArrayBufferViewDataPointer(obj, buffer, byteOffset); michael@0: } else { michael@0: void *data = obj->fixedData(FIXED_DATA_START); michael@0: obj->initPrivate(data); michael@0: memset(data, 0, len * sizeof(NativeType)); michael@0: } michael@0: michael@0: obj->setSlot(LENGTH_SLOT, Int32Value(len)); michael@0: obj->setSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset)); michael@0: obj->setSlot(BYTELENGTH_SLOT, Int32Value(len * sizeof(NativeType))); michael@0: obj->setSlot(NEXT_VIEW_SLOT, PrivateValue(nullptr)); michael@0: michael@0: #ifdef DEBUG michael@0: if (buffer) { michael@0: uint32_t arrayByteLength = obj->byteLength(); michael@0: uint32_t arrayByteOffset = obj->byteOffset(); michael@0: uint32_t bufferByteLength = buffer->byteLength(); michael@0: JS_ASSERT_IF(!buffer->isNeutered(), buffer->dataPointer() <= obj->viewData()); michael@0: JS_ASSERT(bufferByteLength - arrayByteOffset >= arrayByteLength); michael@0: JS_ASSERT(arrayByteOffset <= bufferByteLength); michael@0: } michael@0: michael@0: // Verify that the private slot is at the expected place michael@0: JS_ASSERT(obj->numFixedSlots() == DATA_SLOT); michael@0: #endif michael@0: michael@0: if (buffer) michael@0: buffer->addView(obj); michael@0: michael@0: return obj; michael@0: } michael@0: michael@0: static JSObject * michael@0: makeInstance(JSContext *cx, Handle bufobj, uint32_t byteOffset, uint32_t len) michael@0: { michael@0: RootedObject nullproto(cx, nullptr); michael@0: return makeInstance(cx, bufobj, byteOffset, len, nullproto); michael@0: } michael@0: michael@0: /* michael@0: * new [Type]Array(length) michael@0: * new [Type]Array(otherTypedArray) michael@0: * new [Type]Array(JSArray) michael@0: * new [Type]Array(ArrayBuffer, [optional] byteOffset, [optional] length) michael@0: */ michael@0: static bool michael@0: class_constructor(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: /* N.B. this is a constructor for protoClass, not instanceClass! */ michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: JSObject *obj = create(cx, args); michael@0: if (!obj) michael@0: return false; michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: static JSObject * michael@0: create(JSContext *cx, const CallArgs& args) michael@0: { michael@0: /* () or (number) */ michael@0: uint32_t len = 0; michael@0: if (args.length() == 0 || ValueIsLength(args[0], &len)) michael@0: return fromLength(cx, len); michael@0: michael@0: /* (not an object) */ michael@0: if (!args[0].isObject()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return nullptr; michael@0: } michael@0: michael@0: RootedObject dataObj(cx, &args.get(0).toObject()); michael@0: michael@0: /* michael@0: * (typedArray) michael@0: * (type[] array) michael@0: * michael@0: * Otherwise create a new typed array and copy elements 0..len-1 michael@0: * properties from the object, treating it as some sort of array. michael@0: * Note that offset and length will be ignored michael@0: */ michael@0: if (!UncheckedUnwrap(dataObj)->is() && michael@0: !UncheckedUnwrap(dataObj)->is()) michael@0: { michael@0: return fromArray(cx, dataObj); michael@0: } michael@0: michael@0: /* (ArrayBuffer, [byteOffset, [length]]) */ michael@0: int32_t byteOffset = 0; michael@0: int32_t length = -1; michael@0: michael@0: if (args.length() > 1) { michael@0: if (!ToInt32(cx, args[1], &byteOffset)) michael@0: return nullptr; michael@0: if (byteOffset < 0) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "1"); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (args.length() > 2) { michael@0: if (!ToInt32(cx, args[2], &length)) michael@0: return nullptr; michael@0: if (length < 0) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_TYPED_ARRAY_NEGATIVE_ARG, "2"); michael@0: return nullptr; michael@0: } michael@0: } michael@0: } michael@0: michael@0: Rooted proto(cx, nullptr); michael@0: return fromBuffer(cx, dataObj, byteOffset, length, proto); michael@0: } michael@0: michael@0: static bool IsThisClass(HandleValue v) { michael@0: return v.isObject() && v.toObject().hasClass(instanceClass()); michael@0: } michael@0: michael@0: template michael@0: static bool michael@0: GetterImpl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(IsThisClass(args.thisv())); michael@0: args.rval().set(ValueGetter(&args.thisv().toObject().as())); michael@0: return true; michael@0: } michael@0: michael@0: // ValueGetter is a function that takes an unwrapped typed array object and michael@0: // returns a Value. Given such a function, Getter<> is a native that michael@0: // retrieves a given Value, probably from a slot on the object. michael@0: template michael@0: static bool michael@0: Getter(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod >(cx, args); michael@0: } michael@0: michael@0: static bool michael@0: BufferGetterImpl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(IsThisClass(args.thisv())); michael@0: Rooted tarray(cx, &args.thisv().toObject().as()); michael@0: if (!ensureHasBuffer(cx, tarray)) michael@0: return false; michael@0: args.rval().set(bufferValue(tarray)); michael@0: return true; michael@0: } michael@0: michael@0: // BufferGetter is a function that lazily constructs the array buffer for a michael@0: // typed array before fetching it. michael@0: static bool michael@0: BufferGetter(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: // Define an accessor for a read-only property that invokes a native getter michael@0: static bool michael@0: DefineGetter(JSContext *cx, HandleObject proto, PropertyName *name, Native native) michael@0: { michael@0: RootedId id(cx, NameToId(name)); michael@0: unsigned attrs = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT; michael@0: michael@0: Rooted global(cx, cx->compartment()->maybeGlobal()); michael@0: JSObject *getter = NewFunction(cx, NullPtr(), native, 0, michael@0: JSFunction::NATIVE_FUN, global, NullPtr()); michael@0: if (!getter) michael@0: return false; michael@0: michael@0: return DefineNativeProperty(cx, proto, id, UndefinedHandleValue, michael@0: JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr, michael@0: attrs); michael@0: } michael@0: michael@0: static michael@0: bool defineGetters(JSContext *cx, HandleObject proto) michael@0: { michael@0: if (!DefineGetter(cx, proto, cx->names().length, Getter)) michael@0: return false; michael@0: michael@0: if (!DefineGetter(cx, proto, cx->names().buffer, BufferGetter)) michael@0: return false; michael@0: michael@0: if (!DefineGetter(cx, proto, cx->names().byteLength, Getter)) michael@0: return false; michael@0: michael@0: if (!DefineGetter(cx, proto, cx->names().byteOffset, Getter)) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /* subarray(start[, end]) */ michael@0: static bool michael@0: fun_subarray_impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(IsThisClass(args.thisv())); michael@0: Rooted tarray(cx, &args.thisv().toObject().as()); michael@0: michael@0: // these are the default values michael@0: uint32_t length = tarray->length(); michael@0: uint32_t begin = 0, end = length; michael@0: michael@0: if (args.length() > 0) { michael@0: if (!ToClampedIndex(cx, args[0], length, &begin)) michael@0: return false; michael@0: michael@0: if (args.length() > 1) { michael@0: if (!ToClampedIndex(cx, args[1], length, &end)) michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (begin > end) michael@0: begin = end; michael@0: michael@0: JSObject *nobj = createSubarray(cx, tarray, begin, end); michael@0: if (!nobj) michael@0: return false; michael@0: args.rval().setObject(*nobj); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: fun_subarray(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: /* move(begin, end, dest) */ michael@0: static bool michael@0: fun_move_impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(IsThisClass(args.thisv())); michael@0: Rooted tarray(cx, &args.thisv().toObject().as()); michael@0: michael@0: if (args.length() < 3) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: uint32_t srcBegin; michael@0: uint32_t srcEnd; michael@0: uint32_t dest; michael@0: michael@0: uint32_t originalLength = tarray->length(); michael@0: if (!ToClampedIndex(cx, args[0], originalLength, &srcBegin) || michael@0: !ToClampedIndex(cx, args[1], originalLength, &srcEnd) || michael@0: !ToClampedIndex(cx, args[2], originalLength, &dest)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: if (srcBegin > srcEnd) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_INDEX); michael@0: return false; michael@0: } michael@0: michael@0: uint32_t lengthDuringMove = tarray->length(); // beware ToClampedIndex michael@0: uint32_t nelts = srcEnd - srcBegin; michael@0: michael@0: MOZ_ASSERT(dest <= INT32_MAX, "size limited to 2**31"); michael@0: MOZ_ASSERT(nelts <= INT32_MAX, "size limited to 2**31"); michael@0: if (dest + nelts > lengthDuringMove || srcEnd > lengthDuringMove) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: uint32_t byteDest = dest * sizeof(NativeType); michael@0: uint32_t byteSrc = srcBegin * sizeof(NativeType); michael@0: uint32_t byteSize = nelts * sizeof(NativeType); michael@0: michael@0: #ifdef DEBUG michael@0: uint32_t viewByteLength = tarray->byteLength(); michael@0: JS_ASSERT(byteDest <= viewByteLength); michael@0: JS_ASSERT(byteSrc <= viewByteLength); michael@0: JS_ASSERT(byteDest + byteSize <= viewByteLength); michael@0: JS_ASSERT(byteSrc + byteSize <= viewByteLength); michael@0: michael@0: // Should not overflow because size is limited to 2^31 michael@0: JS_ASSERT(byteDest + byteSize >= byteDest); michael@0: JS_ASSERT(byteSrc + byteSize >= byteSrc); michael@0: #endif michael@0: michael@0: uint8_t *data = static_cast(tarray->viewData()); michael@0: memmove(&data[byteDest], &data[byteSrc], byteSize); michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: fun_move(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: /* set(array[, offset]) */ michael@0: static bool michael@0: fun_set_impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(IsThisClass(args.thisv())); michael@0: Rooted tarray(cx, &args.thisv().toObject().as()); michael@0: michael@0: // first arg must be either a typed array or a JS array michael@0: if (args.length() == 0 || !args[0].isObject()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: int32_t offset = 0; michael@0: if (args.length() > 1) { michael@0: if (!ToInt32(cx, args[1], &offset)) michael@0: return false; michael@0: michael@0: if (offset < 0 || uint32_t(offset) > tarray->length()) { michael@0: // the given offset is bogus michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_TYPED_ARRAY_BAD_INDEX, "2"); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: if (!args[0].isObject()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: RootedObject arg0(cx, args[0].toObjectOrNull()); michael@0: if (arg0->is()) { michael@0: if (arg0->as().length() > tarray->length() - offset) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); michael@0: return false; michael@0: } michael@0: michael@0: if (!copyFromTypedArray(cx, tarray, arg0, offset)) michael@0: return false; michael@0: } else { michael@0: uint32_t len; michael@0: if (!GetLengthProperty(cx, arg0, &len)) michael@0: return false; michael@0: michael@0: if (uint32_t(offset) > tarray->length() || len > tarray->length() - offset) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); michael@0: return false; michael@0: } michael@0: michael@0: if (!copyFromArray(cx, tarray, arg0, len, offset)) michael@0: return false; michael@0: } michael@0: michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: fun_set(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: public: michael@0: static JSObject * michael@0: fromBuffer(JSContext *cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt, michael@0: HandleObject proto) michael@0: { michael@0: if (!ObjectClassIs(bufobj, ESClass_ArrayBuffer, cx)) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return nullptr; // must be arrayBuffer michael@0: } michael@0: michael@0: JS_ASSERT(IsArrayBuffer(bufobj) || bufobj->is()); michael@0: if (bufobj->is()) { michael@0: /* michael@0: * Normally, NonGenericMethodGuard handles the case of transparent michael@0: * wrappers. However, we have a peculiar situation: we want to michael@0: * construct the new typed array in the compartment of the buffer, michael@0: * so that the typed array can point directly at their buffer's michael@0: * data without crossing compartment boundaries. So we use the michael@0: * machinery underlying NonGenericMethodGuard directly to proxy the michael@0: * native call. We will end up with a wrapper in the origin michael@0: * compartment for a view in the target compartment referencing the michael@0: * ArrayBufferObject in that same compartment. michael@0: */ michael@0: JSObject *wrapped = CheckedUnwrap(bufobj); michael@0: if (!wrapped) { michael@0: JS_ReportError(cx, "Permission denied to access object"); michael@0: return nullptr; michael@0: } michael@0: if (IsArrayBuffer(wrapped)) { michael@0: /* michael@0: * And for even more fun, the new view's prototype should be michael@0: * set to the origin compartment's prototype object, not the michael@0: * target's (specifically, the actual view in the target michael@0: * compartment will use as its prototype a wrapper around the michael@0: * origin compartment's view.prototype object). michael@0: * michael@0: * Rather than hack some crazy solution together, implement michael@0: * this all using a private helper function, created when michael@0: * ArrayBufferObject was initialized and cached in the global. michael@0: * This reuses all the existing cross-compartment crazy so we michael@0: * don't have to do anything *uniquely* crazy here. michael@0: */ michael@0: michael@0: Rooted proto(cx); michael@0: if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &proto)) michael@0: return nullptr; michael@0: michael@0: InvokeArgs args(cx); michael@0: if (!args.init(3)) michael@0: return nullptr; michael@0: michael@0: args.setCallee(cx->compartment()->maybeGlobal()->createArrayFromBuffer()); michael@0: args.setThis(ObjectValue(*bufobj)); michael@0: args[0].setNumber(byteOffset); michael@0: args[1].setInt32(lengthInt); michael@0: args[2].setObject(*proto); michael@0: michael@0: if (!Invoke(cx, args)) michael@0: return nullptr; michael@0: return &args.rval().toObject(); michael@0: } michael@0: } michael@0: michael@0: if (!IsArrayBuffer(bufobj)) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return nullptr; // must be arrayBuffer michael@0: } michael@0: michael@0: Rooted buffer(cx, &AsArrayBuffer(bufobj)); michael@0: michael@0: if (byteOffset > buffer->byteLength() || byteOffset % sizeof(NativeType) != 0) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return nullptr; // invalid byteOffset michael@0: } michael@0: michael@0: uint32_t len; michael@0: if (lengthInt == -1) { michael@0: len = (buffer->byteLength() - byteOffset) / sizeof(NativeType); michael@0: if (len * sizeof(NativeType) != buffer->byteLength() - byteOffset) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return nullptr; // given byte array doesn't map exactly to sizeof(NativeType) * N michael@0: } michael@0: } else { michael@0: len = uint32_t(lengthInt); michael@0: } michael@0: michael@0: // Go slowly and check for overflow. michael@0: uint32_t arrayByteLength = len * sizeof(NativeType); michael@0: if (len >= INT32_MAX / sizeof(NativeType) || byteOffset >= INT32_MAX - arrayByteLength) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return nullptr; // overflow when calculating byteOffset + len * sizeof(NativeType) michael@0: } michael@0: michael@0: if (arrayByteLength + byteOffset > buffer->byteLength()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return nullptr; // byteOffset + len is too big for the arraybuffer michael@0: } michael@0: michael@0: return makeInstance(cx, buffer, byteOffset, len, proto); michael@0: } michael@0: michael@0: static bool michael@0: maybeCreateArrayBuffer(JSContext *cx, uint32_t nelements, MutableHandle buffer) michael@0: { michael@0: // Make sure that array elements evenly divide into the inline buffer's michael@0: // size, for the test below. michael@0: JS_STATIC_ASSERT((INLINE_BUFFER_LIMIT / sizeof(NativeType)) * sizeof(NativeType) == INLINE_BUFFER_LIMIT); michael@0: michael@0: if (nelements <= INLINE_BUFFER_LIMIT / sizeof(NativeType)) { michael@0: // The array's data can be inline, and the buffer created lazily. michael@0: return true; michael@0: } michael@0: michael@0: if (nelements >= INT32_MAX / sizeof(NativeType)) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_NEED_DIET, "size and count"); michael@0: return false; michael@0: } michael@0: michael@0: buffer.set(ArrayBufferObject::create(cx, nelements * sizeof(NativeType))); michael@0: return !!buffer; michael@0: } michael@0: michael@0: static JSObject * michael@0: fromLength(JSContext *cx, uint32_t nelements) michael@0: { michael@0: Rooted buffer(cx); michael@0: if (!maybeCreateArrayBuffer(cx, nelements, &buffer)) michael@0: return nullptr; michael@0: return makeInstance(cx, buffer, 0, nelements); michael@0: } michael@0: michael@0: static JSObject * michael@0: fromArray(JSContext *cx, HandleObject other) michael@0: { michael@0: uint32_t len; michael@0: if (other->is()) { michael@0: len = other->as().length(); michael@0: } else if (!GetLengthProperty(cx, other, &len)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: Rooted buffer(cx); michael@0: if (!maybeCreateArrayBuffer(cx, len, &buffer)) michael@0: return nullptr; michael@0: michael@0: RootedObject obj(cx, makeInstance(cx, buffer, 0, len)); michael@0: if (!obj || !copyFromArray(cx, obj, other, len)) michael@0: return nullptr; michael@0: return obj; michael@0: } michael@0: michael@0: static const NativeType michael@0: getIndex(JSObject *obj, uint32_t index) michael@0: { michael@0: TypedArrayObject &tarray = obj->as(); michael@0: MOZ_ASSERT(index < tarray.length()); michael@0: return static_cast(tarray.viewData())[index]; michael@0: } michael@0: michael@0: static void michael@0: setIndex(TypedArrayObject &tarray, uint32_t index, NativeType val) michael@0: { michael@0: MOZ_ASSERT(index < tarray.length()); michael@0: static_cast(tarray.viewData())[index] = val; michael@0: } michael@0: michael@0: static Value getIndexValue(JSObject *tarray, uint32_t index); michael@0: michael@0: static JSObject * michael@0: createSubarray(JSContext *cx, HandleObject tarrayArg, uint32_t begin, uint32_t end) michael@0: { michael@0: Rooted tarray(cx, &tarrayArg->as()); michael@0: michael@0: if (begin > tarray->length() || end > tarray->length() || begin > end) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_INDEX); michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!ensureHasBuffer(cx, tarray)) michael@0: return nullptr; michael@0: michael@0: Rooted bufobj(cx, tarray->buffer()); michael@0: JS_ASSERT(bufobj); michael@0: michael@0: uint32_t length = end - begin; michael@0: michael@0: JS_ASSERT(begin < UINT32_MAX / sizeof(NativeType)); michael@0: uint32_t arrayByteOffset = tarray->byteOffset(); michael@0: JS_ASSERT(UINT32_MAX - begin * sizeof(NativeType) >= arrayByteOffset); michael@0: uint32_t byteOffset = arrayByteOffset + begin * sizeof(NativeType); michael@0: michael@0: return makeInstance(cx, bufobj, byteOffset, length); michael@0: } michael@0: michael@0: protected: michael@0: static NativeType michael@0: doubleToNative(double d) michael@0: { michael@0: if (TypeIsFloatingPoint()) { michael@0: #ifdef JS_MORE_DETERMINISTIC michael@0: // The JS spec doesn't distinguish among different NaN values, and michael@0: // it deliberately doesn't specify the bit pattern written to a michael@0: // typed array when NaN is written into it. This bit-pattern michael@0: // inconsistency could confuse deterministic testing, so always michael@0: // canonicalize NaN values in more-deterministic builds. michael@0: d = CanonicalizeNaN(d); michael@0: #endif michael@0: return NativeType(d); michael@0: } michael@0: if (MOZ_UNLIKELY(IsNaN(d))) michael@0: return NativeType(0); michael@0: if (TypeIsUnsigned()) michael@0: return NativeType(ToUint32(d)); michael@0: return NativeType(ToInt32(d)); michael@0: } michael@0: michael@0: static bool michael@0: canConvertInfallibly(const Value &v) michael@0: { michael@0: return v.isNumber() || v.isBoolean() || v.isNull() || v.isUndefined(); michael@0: } michael@0: michael@0: static NativeType michael@0: infallibleValueToNative(const Value &v) michael@0: { michael@0: if (v.isInt32()) michael@0: return v.toInt32(); michael@0: if (v.isDouble()) michael@0: return doubleToNative(v.toDouble()); michael@0: if (v.isBoolean()) michael@0: return v.toBoolean(); michael@0: if (v.isNull()) michael@0: return 0; michael@0: michael@0: MOZ_ASSERT(v.isUndefined()); michael@0: return ArrayTypeIsFloatingPoint() ? NativeType(GenericNaN()) : NativeType(0); michael@0: } michael@0: michael@0: static bool michael@0: valueToNative(JSContext *cx, const Value &v, NativeType *result) michael@0: { michael@0: MOZ_ASSERT(!v.isMagic()); michael@0: michael@0: if (MOZ_LIKELY(canConvertInfallibly(v))) { michael@0: *result = infallibleValueToNative(v); michael@0: return true; michael@0: } michael@0: michael@0: double d; michael@0: MOZ_ASSERT(v.isString() || v.isObject()); michael@0: if (!(v.isString() ? StringToNumber(cx, v.toString(), &d) : ToNumber(cx, v, &d))) michael@0: return false; michael@0: michael@0: *result = doubleToNative(d); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: copyFromArray(JSContext *cx, HandleObject thisTypedArrayObj, michael@0: HandleObject source, uint32_t len, uint32_t offset = 0) michael@0: { michael@0: Rooted thisTypedArray(cx, &thisTypedArrayObj->as()); michael@0: JS_ASSERT(offset <= thisTypedArray->length()); michael@0: JS_ASSERT(len <= thisTypedArray->length() - offset); michael@0: if (source->is()) michael@0: return copyFromTypedArray(cx, thisTypedArray, source, offset); michael@0: michael@0: uint32_t i = 0; michael@0: if (source->isNative()) { michael@0: // Attempt fast-path infallible conversion of dense elements up to michael@0: // the first potentially side-effectful lookup or conversion. michael@0: uint32_t bound = Min(source->getDenseInitializedLength(), len); michael@0: michael@0: NativeType *dest = static_cast(thisTypedArray->viewData()) + offset; michael@0: michael@0: const Value *srcValues = source->getDenseElements(); michael@0: for (; i < bound; i++) { michael@0: // Note: holes don't convert infallibly. michael@0: if (!canConvertInfallibly(srcValues[i])) michael@0: break; michael@0: dest[i] = infallibleValueToNative(srcValues[i]); michael@0: } michael@0: if (i == len) michael@0: return true; michael@0: } michael@0: michael@0: // Convert and copy any remaining elements generically. michael@0: RootedValue v(cx); michael@0: for (; i < len; i++) { michael@0: if (!JSObject::getElement(cx, source, source, i, &v)) michael@0: return false; michael@0: michael@0: NativeType n; michael@0: if (!valueToNative(cx, v, &n)) michael@0: return false; michael@0: michael@0: len = Min(len, thisTypedArray->length()); michael@0: if (i >= len) michael@0: break; michael@0: michael@0: // Compute every iteration in case getElement acts wacky. michael@0: void *data = thisTypedArray->viewData(); michael@0: static_cast(data)[offset + i] = n; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: copyFromTypedArray(JSContext *cx, JSObject *thisTypedArrayObj, JSObject *tarrayObj, michael@0: uint32_t offset) michael@0: { michael@0: TypedArrayObject *thisTypedArray = &thisTypedArrayObj->as(); michael@0: TypedArrayObject *tarray = &tarrayObj->as(); michael@0: JS_ASSERT(offset <= thisTypedArray->length()); michael@0: JS_ASSERT(tarray->length() <= thisTypedArray->length() - offset); michael@0: if (tarray->buffer() == thisTypedArray->buffer()) michael@0: return copyFromWithOverlap(cx, thisTypedArray, tarray, offset); michael@0: michael@0: NativeType *dest = static_cast(thisTypedArray->viewData()) + offset; michael@0: michael@0: if (tarray->type() == thisTypedArray->type()) { michael@0: js_memcpy(dest, tarray->viewData(), tarray->byteLength()); michael@0: return true; michael@0: } michael@0: michael@0: unsigned srclen = tarray->length(); michael@0: switch (tarray->type()) { michael@0: case ScalarTypeDescr::TYPE_INT8: { michael@0: int8_t *src = static_cast(tarray->viewData()); michael@0: for (unsigned i = 0; i < srclen; ++i) michael@0: *dest++ = NativeType(*src++); michael@0: break; michael@0: } michael@0: case ScalarTypeDescr::TYPE_UINT8: michael@0: case ScalarTypeDescr::TYPE_UINT8_CLAMPED: { michael@0: uint8_t *src = static_cast(tarray->viewData()); michael@0: for (unsigned i = 0; i < srclen; ++i) michael@0: *dest++ = NativeType(*src++); michael@0: break; michael@0: } michael@0: case ScalarTypeDescr::TYPE_INT16: { michael@0: int16_t *src = static_cast(tarray->viewData()); michael@0: for (unsigned i = 0; i < srclen; ++i) michael@0: *dest++ = NativeType(*src++); michael@0: break; michael@0: } michael@0: case ScalarTypeDescr::TYPE_UINT16: { michael@0: uint16_t *src = static_cast(tarray->viewData()); michael@0: for (unsigned i = 0; i < srclen; ++i) michael@0: *dest++ = NativeType(*src++); michael@0: break; michael@0: } michael@0: case ScalarTypeDescr::TYPE_INT32: { michael@0: int32_t *src = static_cast(tarray->viewData()); michael@0: for (unsigned i = 0; i < srclen; ++i) michael@0: *dest++ = NativeType(*src++); michael@0: break; michael@0: } michael@0: case ScalarTypeDescr::TYPE_UINT32: { michael@0: uint32_t *src = static_cast(tarray->viewData()); michael@0: for (unsigned i = 0; i < srclen; ++i) michael@0: *dest++ = NativeType(*src++); michael@0: break; michael@0: } michael@0: case ScalarTypeDescr::TYPE_FLOAT32: { michael@0: float *src = static_cast(tarray->viewData()); michael@0: for (unsigned i = 0; i < srclen; ++i) michael@0: *dest++ = NativeType(*src++); michael@0: break; michael@0: } michael@0: case ScalarTypeDescr::TYPE_FLOAT64: { michael@0: double *src = static_cast(tarray->viewData()); michael@0: for (unsigned i = 0; i < srclen; ++i) michael@0: *dest++ = NativeType(*src++); michael@0: break; michael@0: } michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("copyFrom with a TypedArrayObject of unknown type"); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: copyFromWithOverlap(JSContext *cx, JSObject *selfObj, JSObject *tarrayObj, uint32_t offset) michael@0: { michael@0: TypedArrayObject *self = &selfObj->as(); michael@0: TypedArrayObject *tarray = &tarrayObj->as(); michael@0: michael@0: JS_ASSERT(offset <= self->length()); michael@0: michael@0: NativeType *dest = static_cast(self->viewData()) + offset; michael@0: uint32_t byteLength = tarray->byteLength(); michael@0: michael@0: if (tarray->type() == self->type()) { michael@0: memmove(dest, tarray->viewData(), byteLength); michael@0: return true; michael@0: } michael@0: michael@0: // We have to make a copy of the source array here, since michael@0: // there's overlap, and we have to convert types. michael@0: void *srcbuf = cx->malloc_(byteLength); michael@0: if (!srcbuf) michael@0: return false; michael@0: js_memcpy(srcbuf, tarray->viewData(), byteLength); michael@0: michael@0: uint32_t len = tarray->length(); michael@0: switch (tarray->type()) { michael@0: case ScalarTypeDescr::TYPE_INT8: { michael@0: int8_t *src = (int8_t*) srcbuf; michael@0: for (unsigned i = 0; i < len; ++i) michael@0: *dest++ = NativeType(*src++); michael@0: break; michael@0: } michael@0: case ScalarTypeDescr::TYPE_UINT8: michael@0: case ScalarTypeDescr::TYPE_UINT8_CLAMPED: { michael@0: uint8_t *src = (uint8_t*) srcbuf; michael@0: for (unsigned i = 0; i < len; ++i) michael@0: *dest++ = NativeType(*src++); michael@0: break; michael@0: } michael@0: case ScalarTypeDescr::TYPE_INT16: { michael@0: int16_t *src = (int16_t*) srcbuf; michael@0: for (unsigned i = 0; i < len; ++i) michael@0: *dest++ = NativeType(*src++); michael@0: break; michael@0: } michael@0: case ScalarTypeDescr::TYPE_UINT16: { michael@0: uint16_t *src = (uint16_t*) srcbuf; michael@0: for (unsigned i = 0; i < len; ++i) michael@0: *dest++ = NativeType(*src++); michael@0: break; michael@0: } michael@0: case ScalarTypeDescr::TYPE_INT32: { michael@0: int32_t *src = (int32_t*) srcbuf; michael@0: for (unsigned i = 0; i < len; ++i) michael@0: *dest++ = NativeType(*src++); michael@0: break; michael@0: } michael@0: case ScalarTypeDescr::TYPE_UINT32: { michael@0: uint32_t *src = (uint32_t*) srcbuf; michael@0: for (unsigned i = 0; i < len; ++i) michael@0: *dest++ = NativeType(*src++); michael@0: break; michael@0: } michael@0: case ScalarTypeDescr::TYPE_FLOAT32: { michael@0: float *src = (float*) srcbuf; michael@0: for (unsigned i = 0; i < len; ++i) michael@0: *dest++ = NativeType(*src++); michael@0: break; michael@0: } michael@0: case ScalarTypeDescr::TYPE_FLOAT64: { michael@0: double *src = (double*) srcbuf; michael@0: for (unsigned i = 0; i < len; ++i) michael@0: *dest++ = NativeType(*src++); michael@0: break; michael@0: } michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("copyFromWithOverlap with a TypedArrayObject of unknown type"); michael@0: } michael@0: michael@0: js_free(srcbuf); michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: class Int8ArrayObject : public TypedArrayObjectTemplate { michael@0: public: michael@0: enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_INT8 }; michael@0: static const JSProtoKey key = JSProto_Int8Array; michael@0: static const JSFunctionSpec jsfuncs[]; michael@0: }; michael@0: class Uint8ArrayObject : public TypedArrayObjectTemplate { michael@0: public: michael@0: enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_UINT8 }; michael@0: static const JSProtoKey key = JSProto_Uint8Array; michael@0: static const JSFunctionSpec jsfuncs[]; michael@0: }; michael@0: class Int16ArrayObject : public TypedArrayObjectTemplate { michael@0: public: michael@0: enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_INT16 }; michael@0: static const JSProtoKey key = JSProto_Int16Array; michael@0: static const JSFunctionSpec jsfuncs[]; michael@0: }; michael@0: class Uint16ArrayObject : public TypedArrayObjectTemplate { michael@0: public: michael@0: enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_UINT16 }; michael@0: static const JSProtoKey key = JSProto_Uint16Array; michael@0: static const JSFunctionSpec jsfuncs[]; michael@0: }; michael@0: class Int32ArrayObject : public TypedArrayObjectTemplate { michael@0: public: michael@0: enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_INT32 }; michael@0: static const JSProtoKey key = JSProto_Int32Array; michael@0: static const JSFunctionSpec jsfuncs[]; michael@0: }; michael@0: class Uint32ArrayObject : public TypedArrayObjectTemplate { michael@0: public: michael@0: enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_UINT32 }; michael@0: static const JSProtoKey key = JSProto_Uint32Array; michael@0: static const JSFunctionSpec jsfuncs[]; michael@0: }; michael@0: class Float32ArrayObject : public TypedArrayObjectTemplate { michael@0: public: michael@0: enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_FLOAT32 }; michael@0: static const JSProtoKey key = JSProto_Float32Array; michael@0: static const JSFunctionSpec jsfuncs[]; michael@0: }; michael@0: class Float64ArrayObject : public TypedArrayObjectTemplate { michael@0: public: michael@0: enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_FLOAT64 }; michael@0: static const JSProtoKey key = JSProto_Float64Array; michael@0: static const JSFunctionSpec jsfuncs[]; michael@0: }; michael@0: class Uint8ClampedArrayObject : public TypedArrayObjectTemplate { michael@0: public: michael@0: enum { ACTUAL_TYPE = ScalarTypeDescr::TYPE_UINT8_CLAMPED }; michael@0: static const JSProtoKey key = JSProto_Uint8ClampedArray; michael@0: static const JSFunctionSpec jsfuncs[]; michael@0: }; michael@0: michael@0: } /* anonymous namespace */ michael@0: michael@0: template michael@0: bool michael@0: ArrayBufferObject::createTypedArrayFromBufferImpl(JSContext *cx, CallArgs args) michael@0: { michael@0: typedef TypedArrayObjectTemplate ArrayType; michael@0: JS_ASSERT(IsArrayBuffer(args.thisv())); michael@0: JS_ASSERT(args.length() == 3); michael@0: michael@0: Rooted buffer(cx, &args.thisv().toObject()); michael@0: Rooted proto(cx, &args[2].toObject()); michael@0: michael@0: Rooted obj(cx); michael@0: double byteOffset = args[0].toNumber(); michael@0: MOZ_ASSERT(0 <= byteOffset); michael@0: MOZ_ASSERT(byteOffset <= UINT32_MAX); michael@0: MOZ_ASSERT(byteOffset == uint32_t(byteOffset)); michael@0: obj = ArrayType::fromBuffer(cx, buffer, uint32_t(byteOffset), args[1].toInt32(), proto); michael@0: if (!obj) michael@0: return false; michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: bool michael@0: ArrayBufferObject::createTypedArrayFromBuffer(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod >(cx, args); michael@0: } michael@0: michael@0: // this default implementation is only valid for integer types michael@0: // less than 32-bits in size. michael@0: template michael@0: Value michael@0: TypedArrayObjectTemplate::getIndexValue(JSObject *tarray, uint32_t index) michael@0: { michael@0: JS_STATIC_ASSERT(sizeof(NativeType) < 4); michael@0: michael@0: return Int32Value(getIndex(tarray, index)); michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: // and we need to specialize for 32-bit integers and floats michael@0: template<> michael@0: Value michael@0: TypedArrayObjectTemplate::getIndexValue(JSObject *tarray, uint32_t index) michael@0: { michael@0: return Int32Value(getIndex(tarray, index)); michael@0: } michael@0: michael@0: template<> michael@0: Value michael@0: TypedArrayObjectTemplate::getIndexValue(JSObject *tarray, uint32_t index) michael@0: { michael@0: uint32_t val = getIndex(tarray, index); michael@0: return NumberValue(val); michael@0: } michael@0: michael@0: template<> michael@0: Value michael@0: TypedArrayObjectTemplate::getIndexValue(JSObject *tarray, uint32_t index) michael@0: { michael@0: float val = getIndex(tarray, index); michael@0: double dval = val; michael@0: michael@0: /* michael@0: * Doubles in typed arrays could be typed-punned arrays of integers. This michael@0: * could allow user code to break the engine-wide invariant that only michael@0: * canonical nans are stored into jsvals, which means user code could michael@0: * confuse the engine into interpreting a double-typed jsval as an michael@0: * object-typed jsval. michael@0: * michael@0: * This could be removed for platforms/compilers known to convert a 32-bit michael@0: * non-canonical nan to a 64-bit canonical nan. michael@0: */ michael@0: return DoubleValue(CanonicalizeNaN(dval)); michael@0: } michael@0: michael@0: template<> michael@0: Value michael@0: TypedArrayObjectTemplate::getIndexValue(JSObject *tarray, uint32_t index) michael@0: { michael@0: double val = getIndex(tarray, index); michael@0: michael@0: /* michael@0: * Doubles in typed arrays could be typed-punned arrays of integers. This michael@0: * could allow user code to break the engine-wide invariant that only michael@0: * canonical nans are stored into jsvals, which means user code could michael@0: * confuse the engine into interpreting a double-typed jsval as an michael@0: * object-typed jsval. michael@0: */ michael@0: return DoubleValue(CanonicalizeNaN(val)); michael@0: } michael@0: michael@0: } /* anonymous namespace */ michael@0: michael@0: static NewObjectKind michael@0: DataViewNewObjectKind(JSContext *cx, uint32_t byteLength, JSObject *proto) michael@0: { michael@0: if (!proto && byteLength >= TypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH) michael@0: return SingletonObject; michael@0: jsbytecode *pc; michael@0: JSScript *script = cx->currentScript(&pc); michael@0: if (!script) michael@0: return GenericObject; michael@0: return types::UseNewTypeForInitializer(script, pc, &DataViewObject::class_); michael@0: } michael@0: michael@0: inline DataViewObject * michael@0: DataViewObject::create(JSContext *cx, uint32_t byteOffset, uint32_t byteLength, michael@0: Handle arrayBuffer, JSObject *protoArg) michael@0: { michael@0: JS_ASSERT(byteOffset <= INT32_MAX); michael@0: JS_ASSERT(byteLength <= INT32_MAX); michael@0: michael@0: RootedObject proto(cx, protoArg); michael@0: RootedObject obj(cx); michael@0: michael@0: // This is overflow-safe: 2 * INT32_MAX is still a valid uint32_t. michael@0: if (byteOffset + byteLength > arrayBuffer->byteLength()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); michael@0: return nullptr; michael@0: michael@0: } michael@0: michael@0: NewObjectKind newKind = DataViewNewObjectKind(cx, byteLength, proto); michael@0: obj = NewBuiltinClassInstance(cx, &class_, newKind); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: if (proto) { michael@0: types::TypeObject *type = cx->getNewType(&class_, TaggedProto(proto)); michael@0: if (!type) michael@0: return nullptr; michael@0: obj->setType(type); michael@0: } else if (byteLength >= TypedArrayObject::SINGLETON_TYPE_BYTE_LENGTH) { michael@0: JS_ASSERT(obj->hasSingletonType()); michael@0: } else { michael@0: jsbytecode *pc; michael@0: RootedScript script(cx, cx->currentScript(&pc)); michael@0: if (script) { michael@0: if (!types::SetInitializerObjectType(cx, script, pc, obj, newKind)) michael@0: return nullptr; michael@0: } michael@0: } michael@0: michael@0: DataViewObject &dvobj = obj->as(); michael@0: dvobj.setFixedSlot(BYTEOFFSET_SLOT, Int32Value(byteOffset)); michael@0: dvobj.setFixedSlot(BYTELENGTH_SLOT, Int32Value(byteLength)); michael@0: dvobj.setFixedSlot(BUFFER_SLOT, ObjectValue(*arrayBuffer)); michael@0: dvobj.setFixedSlot(NEXT_VIEW_SLOT, PrivateValue(nullptr)); michael@0: InitArrayBufferViewDataPointer(&dvobj, arrayBuffer, byteOffset); michael@0: JS_ASSERT(byteOffset + byteLength <= arrayBuffer->byteLength()); michael@0: michael@0: // Verify that the private slot is at the expected place michael@0: JS_ASSERT(dvobj.numFixedSlots() == DATA_SLOT); michael@0: michael@0: arrayBuffer->addView(&dvobj); michael@0: michael@0: return &dvobj; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::construct(JSContext *cx, JSObject *bufobj, const CallArgs &args, HandleObject proto) michael@0: { michael@0: if (!IsArrayBuffer(bufobj)) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, michael@0: "DataView", "ArrayBuffer", bufobj->getClass()->name); michael@0: return false; michael@0: } michael@0: michael@0: Rooted buffer(cx, &AsArrayBuffer(bufobj)); michael@0: uint32_t bufferLength = buffer->byteLength(); michael@0: uint32_t byteOffset = 0; michael@0: uint32_t byteLength = bufferLength; michael@0: michael@0: if (args.length() > 1) { michael@0: if (!ToUint32(cx, args[1], &byteOffset)) michael@0: return false; michael@0: if (byteOffset > INT32_MAX) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); michael@0: return false; michael@0: } michael@0: michael@0: if (args.length() > 2) { michael@0: if (!ToUint32(cx, args[2], &byteLength)) michael@0: return false; michael@0: if (byteLength > INT32_MAX) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_ARG_INDEX_OUT_OF_RANGE, "2"); michael@0: return false; michael@0: } michael@0: } else { michael@0: if (byteOffset > bufferLength) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); michael@0: return false; michael@0: } michael@0: michael@0: byteLength = bufferLength - byteOffset; michael@0: } michael@0: } michael@0: michael@0: /* The sum of these cannot overflow a uint32_t */ michael@0: JS_ASSERT(byteOffset <= INT32_MAX); michael@0: JS_ASSERT(byteLength <= INT32_MAX); michael@0: michael@0: if (byteOffset + byteLength > bufferLength) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); michael@0: return false; michael@0: } michael@0: michael@0: JSObject *obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, proto); michael@0: if (!obj) michael@0: return false; michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::class_constructor(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: michael@0: RootedObject bufobj(cx); michael@0: if (!GetFirstArgumentAsObject(cx, args, "DataView constructor", &bufobj)) michael@0: return false; michael@0: michael@0: if (bufobj->is() && IsArrayBuffer(UncheckedUnwrap(bufobj))) { michael@0: Rooted global(cx, cx->compartment()->maybeGlobal()); michael@0: Rooted proto(cx, global->getOrCreateDataViewPrototype(cx)); michael@0: if (!proto) michael@0: return false; michael@0: michael@0: InvokeArgs args2(cx); michael@0: if (!args2.init(args.length() + 1)) michael@0: return false; michael@0: args2.setCallee(global->createDataViewForThis()); michael@0: args2.setThis(ObjectValue(*bufobj)); michael@0: PodCopy(args2.array(), args.array(), args.length()); michael@0: args2[args.length()].setObject(*proto); michael@0: if (!Invoke(cx, args2)) michael@0: return false; michael@0: args.rval().set(args2.rval()); michael@0: return true; michael@0: } michael@0: michael@0: return construct(cx, bufobj, args, NullPtr()); michael@0: } michael@0: michael@0: template michael@0: /* static */ uint8_t * michael@0: DataViewObject::getDataPointer(JSContext *cx, Handle obj, uint32_t offset) michael@0: { michael@0: const size_t TypeSize = sizeof(NativeType); michael@0: if (offset > UINT32_MAX - TypeSize || offset + TypeSize > obj->byteLength()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1"); michael@0: return nullptr; michael@0: } michael@0: michael@0: return static_cast(obj->dataPointer()) + offset; michael@0: } michael@0: michael@0: static inline bool michael@0: needToSwapBytes(bool littleEndian) michael@0: { michael@0: #if IS_LITTLE_ENDIAN michael@0: return !littleEndian; michael@0: #else michael@0: return littleEndian; michael@0: #endif michael@0: } michael@0: michael@0: static inline uint8_t michael@0: swapBytes(uint8_t x) michael@0: { michael@0: return x; michael@0: } michael@0: michael@0: static inline uint16_t michael@0: swapBytes(uint16_t x) michael@0: { michael@0: return ((x & 0xff) << 8) | (x >> 8); michael@0: } michael@0: michael@0: static inline uint32_t michael@0: swapBytes(uint32_t x) michael@0: { michael@0: return ((x & 0xff) << 24) | michael@0: ((x & 0xff00) << 8) | michael@0: ((x & 0xff0000) >> 8) | michael@0: ((x & 0xff000000) >> 24); michael@0: } michael@0: michael@0: static inline uint64_t michael@0: swapBytes(uint64_t x) michael@0: { michael@0: uint32_t a = x & UINT32_MAX; michael@0: uint32_t b = x >> 32; michael@0: return (uint64_t(swapBytes(a)) << 32) | swapBytes(b); michael@0: } michael@0: michael@0: template struct DataToRepType { typedef DataType result; }; michael@0: template <> struct DataToRepType { typedef uint8_t result; }; michael@0: template <> struct DataToRepType { typedef uint8_t result; }; michael@0: template <> struct DataToRepType { typedef uint16_t result; }; michael@0: template <> struct DataToRepType { typedef uint16_t result; }; michael@0: template <> struct DataToRepType { typedef uint32_t result; }; michael@0: template <> struct DataToRepType { typedef uint32_t result; }; michael@0: template <> struct DataToRepType { typedef uint32_t result; }; michael@0: template <> struct DataToRepType { typedef uint64_t result; }; michael@0: michael@0: template michael@0: struct DataViewIO michael@0: { michael@0: typedef typename DataToRepType::result ReadWriteType; michael@0: michael@0: static void fromBuffer(DataType *dest, const uint8_t *unalignedBuffer, bool wantSwap) michael@0: { michael@0: JS_ASSERT((reinterpret_cast(dest) & (Min(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0); michael@0: memcpy((void *) dest, unalignedBuffer, sizeof(ReadWriteType)); michael@0: if (wantSwap) { michael@0: ReadWriteType *rwDest = reinterpret_cast(dest); michael@0: *rwDest = swapBytes(*rwDest); michael@0: } michael@0: } michael@0: michael@0: static void toBuffer(uint8_t *unalignedBuffer, const DataType *src, bool wantSwap) michael@0: { michael@0: JS_ASSERT((reinterpret_cast(src) & (Min(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0); michael@0: ReadWriteType temp = *reinterpret_cast(src); michael@0: if (wantSwap) michael@0: temp = swapBytes(temp); michael@0: memcpy(unalignedBuffer, (void *) &temp, sizeof(ReadWriteType)); michael@0: } michael@0: }; michael@0: michael@0: template michael@0: /* static */ bool michael@0: DataViewObject::read(JSContext *cx, Handle obj, michael@0: CallArgs &args, NativeType *val, const char *method) michael@0: { michael@0: if (args.length() < 1) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_MORE_ARGS_NEEDED, method, "0", "s"); michael@0: return false; michael@0: } michael@0: michael@0: uint32_t offset; michael@0: if (!ToUint32(cx, args[0], &offset)) michael@0: return false; michael@0: michael@0: bool fromLittleEndian = args.length() >= 2 && ToBoolean(args[1]); michael@0: michael@0: uint8_t *data = DataViewObject::getDataPointer(cx, obj, offset); michael@0: if (!data) michael@0: return false; michael@0: michael@0: DataViewIO::fromBuffer(val, data, needToSwapBytes(fromLittleEndian)); michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: static inline bool michael@0: WebIDLCast(JSContext *cx, HandleValue value, NativeType *out) michael@0: { michael@0: int32_t temp; michael@0: if (!ToInt32(cx, value, &temp)) michael@0: return false; michael@0: // Technically, the behavior of assigning an out of range value to a signed michael@0: // variable is undefined. In practice, compilers seem to do what we want michael@0: // without issuing any warnings. michael@0: *out = static_cast(temp); michael@0: return true; michael@0: } michael@0: michael@0: template <> michael@0: inline bool michael@0: WebIDLCast(JSContext *cx, HandleValue value, float *out) michael@0: { michael@0: double temp; michael@0: if (!ToNumber(cx, value, &temp)) michael@0: return false; michael@0: *out = static_cast(temp); michael@0: return true; michael@0: } michael@0: michael@0: template <> michael@0: inline bool michael@0: WebIDLCast(JSContext *cx, HandleValue value, double *out) michael@0: { michael@0: return ToNumber(cx, value, out); michael@0: } michael@0: michael@0: template michael@0: /* static */ bool michael@0: DataViewObject::write(JSContext *cx, Handle obj, michael@0: CallArgs &args, const char *method) michael@0: { michael@0: if (args.length() < 2) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, michael@0: JSMSG_MORE_ARGS_NEEDED, method, "1", ""); michael@0: return false; michael@0: } michael@0: michael@0: uint32_t offset; michael@0: if (!ToUint32(cx, args[0], &offset)) michael@0: return false; michael@0: michael@0: NativeType value; michael@0: if (!WebIDLCast(cx, args[1], &value)) michael@0: return false; michael@0: michael@0: bool toLittleEndian = args.length() >= 3 && ToBoolean(args[2]); michael@0: michael@0: uint8_t *data = DataViewObject::getDataPointer(cx, obj, offset); michael@0: if (!data) michael@0: return false; michael@0: michael@0: DataViewIO::toBuffer(data, &value, needToSwapBytes(toLittleEndian)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::getInt8Impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(is(args.thisv())); michael@0: michael@0: Rooted thisView(cx, &args.thisv().toObject().as()); michael@0: michael@0: int8_t val; michael@0: if (!read(cx, thisView, args, &val, "getInt8")) michael@0: return false; michael@0: args.rval().setInt32(val); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::fun_getInt8(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::getUint8Impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(is(args.thisv())); michael@0: michael@0: Rooted thisView(cx, &args.thisv().toObject().as()); michael@0: michael@0: uint8_t val; michael@0: if (!read(cx, thisView, args, &val, "getUint8")) michael@0: return false; michael@0: args.rval().setInt32(val); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::fun_getUint8(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::getInt16Impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(is(args.thisv())); michael@0: michael@0: Rooted thisView(cx, &args.thisv().toObject().as()); michael@0: michael@0: int16_t val; michael@0: if (!read(cx, thisView, args, &val, "getInt16")) michael@0: return false; michael@0: args.rval().setInt32(val); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::fun_getInt16(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::getUint16Impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(is(args.thisv())); michael@0: michael@0: Rooted thisView(cx, &args.thisv().toObject().as()); michael@0: michael@0: uint16_t val; michael@0: if (!read(cx, thisView, args, &val, "getUint16")) michael@0: return false; michael@0: args.rval().setInt32(val); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::fun_getUint16(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::getInt32Impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(is(args.thisv())); michael@0: michael@0: Rooted thisView(cx, &args.thisv().toObject().as()); michael@0: michael@0: int32_t val; michael@0: if (!read(cx, thisView, args, &val, "getInt32")) michael@0: return false; michael@0: args.rval().setInt32(val); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::fun_getInt32(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::getUint32Impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(is(args.thisv())); michael@0: michael@0: Rooted thisView(cx, &args.thisv().toObject().as()); michael@0: michael@0: uint32_t val; michael@0: if (!read(cx, thisView, args, &val, "getUint32")) michael@0: return false; michael@0: args.rval().setNumber(val); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::fun_getUint32(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::getFloat32Impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(is(args.thisv())); michael@0: michael@0: Rooted thisView(cx, &args.thisv().toObject().as()); michael@0: michael@0: float val; michael@0: if (!read(cx, thisView, args, &val, "getFloat32")) michael@0: return false; michael@0: michael@0: args.rval().setDouble(CanonicalizeNaN(val)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::fun_getFloat32(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::getFloat64Impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(is(args.thisv())); michael@0: michael@0: Rooted thisView(cx, &args.thisv().toObject().as()); michael@0: michael@0: double val; michael@0: if (!read(cx, thisView, args, &val, "getFloat64")) michael@0: return false; michael@0: michael@0: args.rval().setDouble(CanonicalizeNaN(val)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::fun_getFloat64(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::setInt8Impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(is(args.thisv())); michael@0: michael@0: Rooted thisView(cx, &args.thisv().toObject().as()); michael@0: michael@0: if (!write(cx, thisView, args, "setInt8")) michael@0: return false; michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::fun_setInt8(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::setUint8Impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(is(args.thisv())); michael@0: michael@0: Rooted thisView(cx, &args.thisv().toObject().as()); michael@0: michael@0: if (!write(cx, thisView, args, "setUint8")) michael@0: return false; michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::fun_setUint8(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::setInt16Impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(is(args.thisv())); michael@0: michael@0: Rooted thisView(cx, &args.thisv().toObject().as()); michael@0: michael@0: if (!write(cx, thisView, args, "setInt16")) michael@0: return false; michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::fun_setInt16(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::setUint16Impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(is(args.thisv())); michael@0: michael@0: Rooted thisView(cx, &args.thisv().toObject().as()); michael@0: michael@0: if (!write(cx, thisView, args, "setUint16")) michael@0: return false; michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::fun_setUint16(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::setInt32Impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(is(args.thisv())); michael@0: michael@0: Rooted thisView(cx, &args.thisv().toObject().as()); michael@0: michael@0: if (!write(cx, thisView, args, "setInt32")) michael@0: return false; michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::fun_setInt32(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::setUint32Impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(is(args.thisv())); michael@0: michael@0: Rooted thisView(cx, &args.thisv().toObject().as()); michael@0: michael@0: if (!write(cx, thisView, args, "setUint32")) michael@0: return false; michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::fun_setUint32(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::setFloat32Impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(is(args.thisv())); michael@0: michael@0: Rooted thisView(cx, &args.thisv().toObject().as()); michael@0: michael@0: if (!write(cx, thisView, args, "setFloat32")) michael@0: return false; michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::fun_setFloat32(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::setFloat64Impl(JSContext *cx, CallArgs args) michael@0: { michael@0: JS_ASSERT(is(args.thisv())); michael@0: michael@0: Rooted thisView(cx, &args.thisv().toObject().as()); michael@0: michael@0: if (!write(cx, thisView, args, "setFloat64")) michael@0: return false; michael@0: args.rval().setUndefined(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DataViewObject::fun_setFloat64(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod(cx, args); michael@0: } michael@0: michael@0: Value michael@0: TypedArrayObject::getElement(uint32_t index) michael@0: { michael@0: switch (type()) { michael@0: case ScalarTypeDescr::TYPE_INT8: michael@0: return TypedArrayObjectTemplate::getIndexValue(this, index); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_UINT8: michael@0: return TypedArrayObjectTemplate::getIndexValue(this, index); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_UINT8_CLAMPED: michael@0: return TypedArrayObjectTemplate::getIndexValue(this, index); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_INT16: michael@0: return TypedArrayObjectTemplate::getIndexValue(this, index); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_UINT16: michael@0: return TypedArrayObjectTemplate::getIndexValue(this, index); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_INT32: michael@0: return TypedArrayObjectTemplate::getIndexValue(this, index); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_UINT32: michael@0: return TypedArrayObjectTemplate::getIndexValue(this, index); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_FLOAT32: michael@0: return TypedArrayObjectTemplate::getIndexValue(this, index); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_FLOAT64: michael@0: return TypedArrayObjectTemplate::getIndexValue(this, index); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unknown TypedArray type"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: TypedArrayObject::setElement(TypedArrayObject &obj, uint32_t index, double d) michael@0: { michael@0: MOZ_ASSERT(index < obj.length()); michael@0: michael@0: switch (obj.type()) { michael@0: case ScalarTypeDescr::TYPE_INT8: michael@0: TypedArrayObjectTemplate::setIndexValue(obj, index, d); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_UINT8: michael@0: TypedArrayObjectTemplate::setIndexValue(obj, index, d); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_UINT8_CLAMPED: michael@0: TypedArrayObjectTemplate::setIndexValue(obj, index, d); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_INT16: michael@0: TypedArrayObjectTemplate::setIndexValue(obj, index, d); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_UINT16: michael@0: TypedArrayObjectTemplate::setIndexValue(obj, index, d); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_INT32: michael@0: TypedArrayObjectTemplate::setIndexValue(obj, index, d); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_UINT32: michael@0: TypedArrayObjectTemplate::setIndexValue(obj, index, d); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_FLOAT32: michael@0: TypedArrayObjectTemplate::setIndexValue(obj, index, d); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_FLOAT64: michael@0: TypedArrayObjectTemplate::setIndexValue(obj, index, d); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unknown TypedArray type"); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: /*** michael@0: *** JS impl michael@0: ***/ michael@0: michael@0: /* michael@0: * TypedArrayObject boilerplate michael@0: */ michael@0: michael@0: #ifndef RELEASE_BUILD michael@0: # define IMPL_TYPED_ARRAY_STATICS(_typedArray) \ michael@0: const JSFunctionSpec _typedArray##Object::jsfuncs[] = { \ michael@0: JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0, 0), \ michael@0: JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \ michael@0: JS_FN("set", _typedArray##Object::fun_set, 2, JSFUN_GENERIC_NATIVE), \ michael@0: JS_FN("move", _typedArray##Object::fun_move, 3, JSFUN_GENERIC_NATIVE), \ michael@0: JS_FS_END \ michael@0: } michael@0: #else michael@0: # define IMPL_TYPED_ARRAY_STATICS(_typedArray) \ michael@0: const JSFunctionSpec _typedArray##Object::jsfuncs[] = { \ michael@0: JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0, 0), \ michael@0: JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \ michael@0: JS_FN("set", _typedArray##Object::fun_set, 2, JSFUN_GENERIC_NATIVE), \ michael@0: JS_FS_END \ michael@0: } michael@0: #endif michael@0: michael@0: #define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Name,NativeType) \ michael@0: JS_FRIEND_API(JSObject *) JS_New ## Name ## Array(JSContext *cx, uint32_t nelements) \ michael@0: { \ michael@0: return TypedArrayObjectTemplate::fromLength(cx, nelements); \ michael@0: } \ michael@0: JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayFromArray(JSContext *cx, HandleObject other) \ michael@0: { \ michael@0: return TypedArrayObjectTemplate::fromArray(cx, other); \ michael@0: } \ michael@0: JS_FRIEND_API(JSObject *) JS_New ## Name ## ArrayWithBuffer(JSContext *cx, \ michael@0: HandleObject arrayBuffer, uint32_t byteOffset, int32_t length) \ michael@0: { \ michael@0: return TypedArrayObjectTemplate::fromBuffer(cx, arrayBuffer, byteOffset, \ michael@0: length, js::NullPtr()); \ michael@0: } \ michael@0: JS_FRIEND_API(bool) JS_Is ## Name ## Array(JSObject *obj) \ michael@0: { \ michael@0: if (!(obj = CheckedUnwrap(obj))) \ michael@0: return false; \ michael@0: const Class *clasp = obj->getClass(); \ michael@0: return clasp == &TypedArrayObject::classes[TypedArrayObjectTemplate::ArrayTypeID()]; \ michael@0: } \ michael@0: JS_FRIEND_API(JSObject *) js::Unwrap ## Name ## Array(JSObject *obj) \ michael@0: { \ michael@0: obj = CheckedUnwrap(obj); \ michael@0: if (!obj) \ michael@0: return nullptr; \ michael@0: const Class *clasp = obj->getClass(); \ michael@0: if (clasp == &TypedArrayObject::classes[TypedArrayObjectTemplate::ArrayTypeID()]) \ michael@0: return obj; \ michael@0: return nullptr; \ michael@0: } \ michael@0: const js::Class* const js::detail::Name ## ArrayClassPtr = \ michael@0: &js::TypedArrayObject::classes[TypedArrayObjectTemplate::ArrayTypeID()]; michael@0: michael@0: IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int8, int8_t) michael@0: IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8, uint8_t) michael@0: IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint8Clamped, uint8_clamped) michael@0: IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int16, int16_t) michael@0: IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint16, uint16_t) michael@0: IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Int32, int32_t) michael@0: IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Uint32, uint32_t) michael@0: IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float32, float) michael@0: IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Float64, double) michael@0: michael@0: #define IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Name, ExternalType, InternalType) \ michael@0: JS_FRIEND_API(JSObject *) JS_GetObjectAs ## Name ## Array(JSObject *obj, \ michael@0: uint32_t *length, \ michael@0: ExternalType **data) \ michael@0: { \ michael@0: if (!(obj = CheckedUnwrap(obj))) \ michael@0: return nullptr; \ michael@0: \ michael@0: const Class *clasp = obj->getClass(); \ michael@0: if (clasp != &TypedArrayObject::classes[TypedArrayObjectTemplate::ArrayTypeID()]) \ michael@0: return nullptr; \ michael@0: \ michael@0: TypedArrayObject *tarr = &obj->as(); \ michael@0: *length = tarr->length(); \ michael@0: *data = static_cast(tarr->viewData()); \ michael@0: \ michael@0: return obj; \ michael@0: } michael@0: michael@0: IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int8, int8_t, int8_t) michael@0: IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint8, uint8_t, uint8_t) michael@0: IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint8Clamped, uint8_t, uint8_clamped) michael@0: IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int16, int16_t, int16_t) michael@0: IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint16, uint16_t, uint16_t) michael@0: IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Int32, int32_t, int32_t) michael@0: IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint32, uint32_t, uint32_t) michael@0: IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float32, float, float) michael@0: IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double) michael@0: michael@0: #define IMPL_TYPED_ARRAY_PROTO_CLASS(_typedArray) \ michael@0: { \ michael@0: #_typedArray "Prototype", \ michael@0: JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) | \ michael@0: JSCLASS_HAS_PRIVATE | \ michael@0: JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray), \ michael@0: JS_PropertyStub, /* addProperty */ \ michael@0: JS_DeletePropertyStub, /* delProperty */ \ michael@0: JS_PropertyStub, /* getProperty */ \ michael@0: JS_StrictPropertyStub, /* setProperty */ \ michael@0: JS_EnumerateStub, \ michael@0: JS_ResolveStub, \ michael@0: JS_ConvertStub \ michael@0: } michael@0: michael@0: #define IMPL_TYPED_ARRAY_FAST_CLASS(_typedArray) \ michael@0: { \ michael@0: #_typedArray, \ michael@0: JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) | \ michael@0: JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | \ michael@0: JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray), \ michael@0: JS_PropertyStub, /* addProperty */ \ michael@0: JS_DeletePropertyStub, /* delProperty */ \ michael@0: JS_PropertyStub, /* getProperty */ \ michael@0: JS_StrictPropertyStub, /* setProperty */ \ michael@0: JS_EnumerateStub, \ michael@0: JS_ResolveStub, \ michael@0: JS_ConvertStub, \ michael@0: nullptr, /* finalize */ \ michael@0: nullptr, /* call */ \ michael@0: nullptr, /* hasInstance */ \ michael@0: nullptr, /* construct */ \ michael@0: ArrayBufferViewObject::trace, /* trace */ \ michael@0: } michael@0: michael@0: template michael@0: static inline bool michael@0: InitTypedArrayClass(JSContext *cx) michael@0: { michael@0: Rooted global(cx, cx->compartment()->maybeGlobal()); michael@0: if (global->isStandardClassResolved(ArrayType::key)) michael@0: return true; michael@0: michael@0: RootedObject proto(cx, global->createBlankPrototype(cx, ArrayType::protoClass())); michael@0: if (!proto) michael@0: return false; michael@0: michael@0: RootedFunction ctor(cx); michael@0: ctor = global->createConstructor(cx, ArrayType::class_constructor, michael@0: ClassName(ArrayType::key, cx), 3); michael@0: if (!ctor) michael@0: return false; michael@0: michael@0: if (!LinkConstructorAndPrototype(cx, ctor, proto)) michael@0: return false; michael@0: michael@0: RootedValue bytesValue(cx, Int32Value(ArrayType::BYTES_PER_ELEMENT)); michael@0: michael@0: if (!JSObject::defineProperty(cx, ctor, michael@0: cx->names().BYTES_PER_ELEMENT, bytesValue, michael@0: JS_PropertyStub, JS_StrictPropertyStub, michael@0: JSPROP_PERMANENT | JSPROP_READONLY) || michael@0: !JSObject::defineProperty(cx, proto, michael@0: cx->names().BYTES_PER_ELEMENT, bytesValue, michael@0: JS_PropertyStub, JS_StrictPropertyStub, michael@0: JSPROP_PERMANENT | JSPROP_READONLY)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: if (!ArrayType::defineGetters(cx, proto)) michael@0: return false; michael@0: michael@0: if (!JS_DefineFunctions(cx, proto, ArrayType::jsfuncs)) michael@0: return false; michael@0: michael@0: RootedFunction fun(cx); michael@0: fun = michael@0: NewFunction(cx, NullPtr(), michael@0: ArrayBufferObject::createTypedArrayFromBuffer, michael@0: 0, JSFunction::NATIVE_FUN, global, NullPtr()); michael@0: if (!fun) michael@0: return false; michael@0: michael@0: if (!GlobalObject::initBuiltinConstructor(cx, global, ArrayType::key, ctor, proto)) michael@0: return false; michael@0: michael@0: global->setCreateArrayFromBuffer(fun); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: IMPL_TYPED_ARRAY_STATICS(Int8Array); michael@0: IMPL_TYPED_ARRAY_STATICS(Uint8Array); michael@0: IMPL_TYPED_ARRAY_STATICS(Int16Array); michael@0: IMPL_TYPED_ARRAY_STATICS(Uint16Array); michael@0: IMPL_TYPED_ARRAY_STATICS(Int32Array); michael@0: IMPL_TYPED_ARRAY_STATICS(Uint32Array); michael@0: IMPL_TYPED_ARRAY_STATICS(Float32Array); michael@0: IMPL_TYPED_ARRAY_STATICS(Float64Array); michael@0: IMPL_TYPED_ARRAY_STATICS(Uint8ClampedArray); michael@0: michael@0: const Class TypedArrayObject::classes[ScalarTypeDescr::TYPE_MAX] = { michael@0: IMPL_TYPED_ARRAY_FAST_CLASS(Int8Array), michael@0: IMPL_TYPED_ARRAY_FAST_CLASS(Uint8Array), michael@0: IMPL_TYPED_ARRAY_FAST_CLASS(Int16Array), michael@0: IMPL_TYPED_ARRAY_FAST_CLASS(Uint16Array), michael@0: IMPL_TYPED_ARRAY_FAST_CLASS(Int32Array), michael@0: IMPL_TYPED_ARRAY_FAST_CLASS(Uint32Array), michael@0: IMPL_TYPED_ARRAY_FAST_CLASS(Float32Array), michael@0: IMPL_TYPED_ARRAY_FAST_CLASS(Float64Array), michael@0: IMPL_TYPED_ARRAY_FAST_CLASS(Uint8ClampedArray) michael@0: }; michael@0: michael@0: const Class TypedArrayObject::protoClasses[ScalarTypeDescr::TYPE_MAX] = { michael@0: IMPL_TYPED_ARRAY_PROTO_CLASS(Int8Array), michael@0: IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8Array), michael@0: IMPL_TYPED_ARRAY_PROTO_CLASS(Int16Array), michael@0: IMPL_TYPED_ARRAY_PROTO_CLASS(Uint16Array), michael@0: IMPL_TYPED_ARRAY_PROTO_CLASS(Int32Array), michael@0: IMPL_TYPED_ARRAY_PROTO_CLASS(Uint32Array), michael@0: IMPL_TYPED_ARRAY_PROTO_CLASS(Float32Array), michael@0: IMPL_TYPED_ARRAY_PROTO_CLASS(Float64Array), michael@0: IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8ClampedArray) michael@0: }; michael@0: michael@0: #define CHECK(t, a) { if (t == a::IsThisClass) return true; } michael@0: JS_FRIEND_API(bool) michael@0: js::IsTypedArrayThisCheck(JS::IsAcceptableThis test) michael@0: { michael@0: CHECK(test, Int8ArrayObject); michael@0: CHECK(test, Uint8ArrayObject); michael@0: CHECK(test, Int16ArrayObject); michael@0: CHECK(test, Uint16ArrayObject); michael@0: CHECK(test, Int32ArrayObject); michael@0: CHECK(test, Uint32ArrayObject); michael@0: CHECK(test, Float32ArrayObject); michael@0: CHECK(test, Float64ArrayObject); michael@0: CHECK(test, Uint8ClampedArrayObject); michael@0: return false; michael@0: } michael@0: #undef CHECK michael@0: michael@0: static JSObject * michael@0: InitArrayBufferClass(JSContext *cx) michael@0: { michael@0: Rooted global(cx, cx->compartment()->maybeGlobal()); michael@0: if (global->isStandardClassResolved(JSProto_ArrayBuffer)) michael@0: return &global->getPrototype(JSProto_ArrayBuffer).toObject(); michael@0: michael@0: RootedObject arrayBufferProto(cx, global->createBlankPrototype(cx, &ArrayBufferObject::protoClass)); michael@0: if (!arrayBufferProto) michael@0: return nullptr; michael@0: michael@0: RootedFunction ctor(cx, global->createConstructor(cx, ArrayBufferObject::class_constructor, michael@0: cx->names().ArrayBuffer, 1)); michael@0: if (!ctor) michael@0: return nullptr; michael@0: michael@0: if (!LinkConstructorAndPrototype(cx, ctor, arrayBufferProto)) michael@0: return nullptr; michael@0: michael@0: RootedId byteLengthId(cx, NameToId(cx->names().byteLength)); michael@0: unsigned attrs = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT; michael@0: JSObject *getter = NewFunction(cx, NullPtr(), ArrayBufferObject::byteLengthGetter, 0, michael@0: JSFunction::NATIVE_FUN, global, NullPtr()); michael@0: if (!getter) michael@0: return nullptr; michael@0: michael@0: if (!DefineNativeProperty(cx, arrayBufferProto, byteLengthId, UndefinedHandleValue, michael@0: JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr, attrs)) michael@0: return nullptr; michael@0: michael@0: if (!JS_DefineFunctions(cx, ctor, ArrayBufferObject::jsstaticfuncs)) michael@0: return nullptr; michael@0: michael@0: if (!JS_DefineFunctions(cx, arrayBufferProto, ArrayBufferObject::jsfuncs)) michael@0: return nullptr; michael@0: michael@0: if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_ArrayBuffer, michael@0: ctor, arrayBufferProto)) michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: return arrayBufferProto; michael@0: } michael@0: michael@0: const Class DataViewObject::protoClass = { michael@0: "DataViewPrototype", michael@0: JSCLASS_HAS_PRIVATE | michael@0: JSCLASS_HAS_RESERVED_SLOTS(DataViewObject::RESERVED_SLOTS) | michael@0: JSCLASS_HAS_CACHED_PROTO(JSProto_DataView), michael@0: JS_PropertyStub, /* addProperty */ michael@0: JS_DeletePropertyStub, /* delProperty */ michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub michael@0: }; michael@0: michael@0: const Class DataViewObject::class_ = { michael@0: "DataView", michael@0: JSCLASS_HAS_PRIVATE | michael@0: JSCLASS_IMPLEMENTS_BARRIERS | michael@0: JSCLASS_HAS_RESERVED_SLOTS(DataViewObject::RESERVED_SLOTS) | michael@0: JSCLASS_HAS_CACHED_PROTO(JSProto_DataView), michael@0: JS_PropertyStub, /* addProperty */ michael@0: JS_DeletePropertyStub, /* delProperty */ michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: nullptr, /* finalize */ michael@0: nullptr, /* call */ michael@0: nullptr, /* hasInstance */ michael@0: nullptr, /* construct */ michael@0: ArrayBufferViewObject::trace, /* trace */ michael@0: }; michael@0: michael@0: const JSFunctionSpec DataViewObject::jsfuncs[] = { michael@0: JS_FN("getInt8", DataViewObject::fun_getInt8, 1,0), michael@0: JS_FN("getUint8", DataViewObject::fun_getUint8, 1,0), michael@0: JS_FN("getInt16", DataViewObject::fun_getInt16, 2,0), michael@0: JS_FN("getUint16", DataViewObject::fun_getUint16, 2,0), michael@0: JS_FN("getInt32", DataViewObject::fun_getInt32, 2,0), michael@0: JS_FN("getUint32", DataViewObject::fun_getUint32, 2,0), michael@0: JS_FN("getFloat32", DataViewObject::fun_getFloat32, 2,0), michael@0: JS_FN("getFloat64", DataViewObject::fun_getFloat64, 2,0), michael@0: JS_FN("setInt8", DataViewObject::fun_setInt8, 2,0), michael@0: JS_FN("setUint8", DataViewObject::fun_setUint8, 2,0), michael@0: JS_FN("setInt16", DataViewObject::fun_setInt16, 3,0), michael@0: JS_FN("setUint16", DataViewObject::fun_setUint16, 3,0), michael@0: JS_FN("setInt32", DataViewObject::fun_setInt32, 3,0), michael@0: JS_FN("setUint32", DataViewObject::fun_setUint32, 3,0), michael@0: JS_FN("setFloat32", DataViewObject::fun_setFloat32, 3,0), michael@0: JS_FN("setFloat64", DataViewObject::fun_setFloat64, 3,0), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: template michael@0: bool michael@0: DataViewObject::getterImpl(JSContext *cx, CallArgs args) michael@0: { michael@0: args.rval().set(ValueGetter(&args.thisv().toObject().as())); michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: bool michael@0: DataViewObject::getter(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: return CallNonGenericMethod >(cx, args); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: DataViewObject::defineGetter(JSContext *cx, PropertyName *name, HandleObject proto) michael@0: { michael@0: RootedId id(cx, NameToId(name)); michael@0: unsigned attrs = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT; michael@0: michael@0: Rooted global(cx, cx->compartment()->maybeGlobal()); michael@0: JSObject *getter = NewFunction(cx, NullPtr(), DataViewObject::getter, 0, michael@0: JSFunction::NATIVE_FUN, global, NullPtr()); michael@0: if (!getter) michael@0: return false; michael@0: michael@0: return DefineNativeProperty(cx, proto, id, UndefinedHandleValue, michael@0: JS_DATA_TO_FUNC_PTR(PropertyOp, getter), nullptr, attrs); michael@0: } michael@0: michael@0: /* static */ bool michael@0: DataViewObject::initClass(JSContext *cx) michael@0: { michael@0: Rooted global(cx, cx->compartment()->maybeGlobal()); michael@0: if (global->isStandardClassResolved(JSProto_DataView)) michael@0: return true; michael@0: michael@0: RootedObject proto(cx, global->createBlankPrototype(cx, &DataViewObject::protoClass)); michael@0: if (!proto) michael@0: return false; michael@0: michael@0: RootedFunction ctor(cx, global->createConstructor(cx, DataViewObject::class_constructor, michael@0: cx->names().DataView, 3)); michael@0: if (!ctor) michael@0: return false; michael@0: michael@0: if (!LinkConstructorAndPrototype(cx, ctor, proto)) michael@0: return false; michael@0: michael@0: if (!defineGetter(cx, cx->names().buffer, proto)) michael@0: return false; michael@0: michael@0: if (!defineGetter(cx, cx->names().byteLength, proto)) michael@0: return false; michael@0: michael@0: if (!defineGetter(cx, cx->names().byteOffset, proto)) michael@0: return false; michael@0: michael@0: if (!JS_DefineFunctions(cx, proto, DataViewObject::jsfuncs)) michael@0: return false; michael@0: michael@0: /* michael@0: * Create a helper function to implement the craziness of michael@0: * |new DataView(new otherWindow.ArrayBuffer())|, and install it in the michael@0: * global for use by the DataViewObject constructor. michael@0: */ michael@0: RootedFunction fun(cx, NewFunction(cx, NullPtr(), ArrayBufferObject::createDataViewForThis, michael@0: 0, JSFunction::NATIVE_FUN, global, NullPtr())); michael@0: if (!fun) michael@0: return false; michael@0: michael@0: if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_DataView, ctor, proto)) michael@0: return false; michael@0: michael@0: global->setCreateDataViewForThis(fun); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: DataViewObject::neuter(void *newData) michael@0: { michael@0: setSlot(BYTELENGTH_SLOT, Int32Value(0)); michael@0: setSlot(BYTEOFFSET_SLOT, Int32Value(0)); michael@0: setPrivate(newData); michael@0: } michael@0: michael@0: JSObject * michael@0: js_InitTypedArrayClasses(JSContext *cx, HandleObject obj) michael@0: { michael@0: if (!InitTypedArrayClass(cx) || michael@0: !InitTypedArrayClass(cx) || michael@0: !InitTypedArrayClass(cx) || michael@0: !InitTypedArrayClass(cx) || michael@0: !InitTypedArrayClass(cx) || michael@0: !InitTypedArrayClass(cx) || michael@0: !InitTypedArrayClass(cx) || michael@0: !InitTypedArrayClass(cx) || michael@0: !InitTypedArrayClass(cx) || michael@0: !DataViewObject::initClass(cx)) michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: return InitArrayBufferClass(cx); michael@0: } michael@0: michael@0: bool michael@0: js::IsTypedArrayConstructor(HandleValue v, uint32_t type) michael@0: { michael@0: switch (type) { michael@0: case ScalarTypeDescr::TYPE_INT8: michael@0: return IsNativeFunction(v, Int8ArrayObject::class_constructor); michael@0: case ScalarTypeDescr::TYPE_UINT8: michael@0: return IsNativeFunction(v, Uint8ArrayObject::class_constructor); michael@0: case ScalarTypeDescr::TYPE_INT16: michael@0: return IsNativeFunction(v, Int16ArrayObject::class_constructor); michael@0: case ScalarTypeDescr::TYPE_UINT16: michael@0: return IsNativeFunction(v, Uint16ArrayObject::class_constructor); michael@0: case ScalarTypeDescr::TYPE_INT32: michael@0: return IsNativeFunction(v, Int32ArrayObject::class_constructor); michael@0: case ScalarTypeDescr::TYPE_UINT32: michael@0: return IsNativeFunction(v, Uint32ArrayObject::class_constructor); michael@0: case ScalarTypeDescr::TYPE_FLOAT32: michael@0: return IsNativeFunction(v, Float32ArrayObject::class_constructor); michael@0: case ScalarTypeDescr::TYPE_FLOAT64: michael@0: return IsNativeFunction(v, Float64ArrayObject::class_constructor); michael@0: case ScalarTypeDescr::TYPE_UINT8_CLAMPED: michael@0: return IsNativeFunction(v, Uint8ClampedArrayObject::class_constructor); michael@0: } michael@0: MOZ_ASSUME_UNREACHABLE("unexpected typed array type"); michael@0: } michael@0: michael@0: bool michael@0: js::IsTypedArrayBuffer(HandleValue v) michael@0: { michael@0: return v.isObject() && michael@0: (v.toObject().is() || michael@0: v.toObject().is()); michael@0: } michael@0: michael@0: ArrayBufferObject & michael@0: js::AsTypedArrayBuffer(HandleValue v) michael@0: { michael@0: JS_ASSERT(IsTypedArrayBuffer(v)); michael@0: if (v.toObject().is()) michael@0: return v.toObject().as(); michael@0: return v.toObject().as(); michael@0: } michael@0: michael@0: bool michael@0: js::StringIsTypedArrayIndex(JSLinearString *str, uint64_t *indexp) michael@0: { michael@0: const jschar *s = str->chars(); michael@0: const jschar *end = s + str->length(); michael@0: michael@0: if (s == end) michael@0: return false; michael@0: michael@0: bool negative = false; michael@0: if (*s == '-') { michael@0: negative = true; michael@0: if (++s == end) michael@0: return false; michael@0: } michael@0: michael@0: if (!JS7_ISDEC(*s)) michael@0: return false; michael@0: michael@0: uint64_t index = 0; michael@0: uint32_t digit = JS7_UNDEC(*s++); michael@0: michael@0: /* Don't allow leading zeros. */ michael@0: if (digit == 0 && s != end) michael@0: return false; michael@0: michael@0: index = digit; michael@0: michael@0: for (; s < end; s++) { michael@0: if (!JS7_ISDEC(*s)) michael@0: return false; michael@0: michael@0: digit = JS7_UNDEC(*s); michael@0: michael@0: /* Watch for overflows. */ michael@0: if ((UINT64_MAX - digit) / 10 < index) michael@0: index = UINT64_MAX; michael@0: else michael@0: index = 10 * index + digit; michael@0: } michael@0: michael@0: if (negative) michael@0: *indexp = UINT64_MAX; michael@0: else michael@0: *indexp = index; michael@0: return true; michael@0: } michael@0: michael@0: /* JS Friend API */ michael@0: michael@0: JS_FRIEND_API(bool) michael@0: JS_IsTypedArrayObject(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: return obj ? obj->is() : false; michael@0: } michael@0: michael@0: JS_FRIEND_API(uint32_t) michael@0: JS_GetTypedArrayLength(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return 0; michael@0: return obj->as().length(); michael@0: } michael@0: michael@0: JS_FRIEND_API(uint32_t) michael@0: JS_GetTypedArrayByteOffset(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return 0; michael@0: return obj->as().byteOffset(); michael@0: } michael@0: michael@0: JS_FRIEND_API(uint32_t) michael@0: JS_GetTypedArrayByteLength(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return 0; michael@0: return obj->as().byteLength(); michael@0: } michael@0: michael@0: JS_FRIEND_API(JSArrayBufferViewType) michael@0: JS_GetArrayBufferViewType(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return ArrayBufferView::TYPE_MAX; michael@0: michael@0: if (obj->is()) michael@0: return static_cast(obj->as().type()); michael@0: else if (obj->is()) michael@0: return ArrayBufferView::TYPE_DATAVIEW; michael@0: MOZ_ASSUME_UNREACHABLE("invalid ArrayBufferView type"); michael@0: } michael@0: michael@0: JS_FRIEND_API(int8_t *) michael@0: JS_GetInt8ArrayData(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return nullptr; michael@0: TypedArrayObject *tarr = &obj->as(); michael@0: JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_INT8); michael@0: return static_cast(tarr->viewData()); michael@0: } michael@0: michael@0: JS_FRIEND_API(uint8_t *) michael@0: JS_GetUint8ArrayData(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return nullptr; michael@0: TypedArrayObject *tarr = &obj->as(); michael@0: JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_UINT8); michael@0: return static_cast(tarr->viewData()); michael@0: } michael@0: michael@0: JS_FRIEND_API(uint8_t *) michael@0: JS_GetUint8ClampedArrayData(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return nullptr; michael@0: TypedArrayObject *tarr = &obj->as(); michael@0: JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_UINT8_CLAMPED); michael@0: return static_cast(tarr->viewData()); michael@0: } michael@0: michael@0: JS_FRIEND_API(int16_t *) michael@0: JS_GetInt16ArrayData(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return nullptr; michael@0: TypedArrayObject *tarr = &obj->as(); michael@0: JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_INT16); michael@0: return static_cast(tarr->viewData()); michael@0: } michael@0: michael@0: JS_FRIEND_API(uint16_t *) michael@0: JS_GetUint16ArrayData(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return nullptr; michael@0: TypedArrayObject *tarr = &obj->as(); michael@0: JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_UINT16); michael@0: return static_cast(tarr->viewData()); michael@0: } michael@0: michael@0: JS_FRIEND_API(int32_t *) michael@0: JS_GetInt32ArrayData(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return nullptr; michael@0: TypedArrayObject *tarr = &obj->as(); michael@0: JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_INT32); michael@0: return static_cast(tarr->viewData()); michael@0: } michael@0: michael@0: JS_FRIEND_API(uint32_t *) michael@0: JS_GetUint32ArrayData(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return nullptr; michael@0: TypedArrayObject *tarr = &obj->as(); michael@0: JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_UINT32); michael@0: return static_cast(tarr->viewData()); michael@0: } michael@0: michael@0: JS_FRIEND_API(float *) michael@0: JS_GetFloat32ArrayData(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return nullptr; michael@0: TypedArrayObject *tarr = &obj->as(); michael@0: JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_FLOAT32); michael@0: return static_cast(tarr->viewData()); michael@0: } michael@0: michael@0: JS_FRIEND_API(double *) michael@0: JS_GetFloat64ArrayData(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return nullptr; michael@0: TypedArrayObject *tarr = &obj->as(); michael@0: JS_ASSERT(tarr->type() == ArrayBufferView::TYPE_FLOAT64); michael@0: return static_cast(tarr->viewData()); michael@0: } michael@0: michael@0: JS_FRIEND_API(bool) michael@0: JS_IsDataViewObject(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: return obj ? obj->is() : false; michael@0: } michael@0: michael@0: JS_FRIEND_API(uint32_t) michael@0: JS_GetDataViewByteOffset(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return 0; michael@0: return obj->as().byteOffset(); michael@0: } michael@0: michael@0: JS_FRIEND_API(void *) michael@0: JS_GetDataViewData(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return nullptr; michael@0: return obj->as().dataPointer(); michael@0: } michael@0: michael@0: JS_FRIEND_API(uint32_t) michael@0: JS_GetDataViewByteLength(JSObject *obj) michael@0: { michael@0: obj = CheckedUnwrap(obj); michael@0: if (!obj) michael@0: return 0; michael@0: return obj->as().byteLength(); michael@0: }