author | Nikhil Marathe <nsm.nikhil@gmail.com> |
Thu, 25 Jul 2013 17:58:50 -0700 | |
changeset 140051 | 2aa4435cd79882c98217991bad72c65633269430 |
parent 140050 | 5dd7854c9474f0a997233430072f7a6b239677a7 |
child 140052 | 8253f5b39cbd23c09ee7485489d5daee520555a9 |
push id | 31583 |
push user | nsm.nikhil@gmail.com |
push date | Fri, 26 Jul 2013 01:00:52 +0000 |
treeherder | mozilla-inbound@13b28328f010 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | nmatsakis |
bugs | 578700 |
milestone | 25.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/builtin/BinaryData.cpp +++ b/js/src/builtin/BinaryData.cpp @@ -1,201 +1,382 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "builtin/BinaryData.h" +#include "mozilla/FloatingPoint.h" + #include "jscompartment.h" #include "jsobj.h" #include "jsatominlines.h" #include "jsobjinlines.h" #include "vm/GlobalObject.h" +#include "vm/TypedArrayObject.h" using namespace js; JSBool TypeThrowError(JSContext *cx, unsigned argc, Value *vp) { return ReportIsNotFunction(cx, *vp); } JSBool DataThrowError(JSContext *cx, unsigned argc, Value *vp) { return ReportIsNotFunction(cx, *vp); } -// FIXME will actually require knowing function name -JSBool createNumericBlock(JSContext *cx, unsigned argc, jsval *vp) +static void +ReportTypeError(JSContext *cx, Value fromValue, const char *toType) +{ + char *valueStr = JS_EncodeString(cx, JS_ValueToString(cx, fromValue)); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO, + valueStr, toType); + JS_free(cx, (void *) valueStr); +} + +static void +ReportTypeError(JSContext *cx, Value fromValue, JSString *toType) +{ + const char *fnName = JS_EncodeString(cx, toType); + ReportTypeError(cx, fromValue, fnName); + JS_free(cx, (void *) fnName); +} + +static inline bool +IsNumericType(JSObject *type) +{ + return type && &NumericTypeClasses[NUMERICTYPE_UINT8] <= type->getClass() && + type->getClass() <= &NumericTypeClasses[NUMERICTYPE_FLOAT64]; +} + +template <typename Domain, typename Input> +bool +InRange(Input x) { - return false; + return std::numeric_limits<Domain>::min() <= x && + x <= std::numeric_limits<Domain>::max(); +} + +template <> +bool +InRange<float, int>(int x) +{ + return -std::numeric_limits<float>::max() <= x && + x <= std::numeric_limits<float>::max(); +} + +template <> +bool +InRange<double, int>(int x) +{ + return -std::numeric_limits<double>::max() <= x && + x <= std::numeric_limits<double>::max(); +} + +template <> +bool +InRange<float, double>(double x) +{ + return -std::numeric_limits<float>::max() <= x && + x <= std::numeric_limits<float>::max(); +} + +template <> +bool +InRange<double, double>(double x) +{ + return -std::numeric_limits<double>::max() <= x && + x <= std::numeric_limits<double>::max(); +} + +template <typename T> +Class * +js::NumericType<T>::typeToClass() +{ + JS_ASSERT(0); + return NULL; } -JSBool createArrayType(JSContext *cx, unsigned argc, jsval *vp) -{ - return false; +#define BINARYDATA_TYPE_TO_CLASS(constant_, type_)\ + template <>\ + Class *\ + NumericType<type_##_t>::typeToClass()\ + {\ + return &NumericTypeClasses[constant_];\ + } + +/** + * This namespace declaration is required because of a weird 'specialization in + * different namespace' error that happens in gcc, only on type specialized + * template functions. + */ +namespace js { + BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_TYPE_TO_CLASS); } -JSBool createStructType(JSContext *cx, unsigned argc, jsval *vp) +template <typename T> +bool +NumericType<T>::convert(JSContext *cx, HandleValue val, T* converted) { - return false; + if (val.isInt32()) { + *converted = T(val.toInt32()); + return true; + } + + double d; + if (!ToDoubleForTypedArray(cx, val, &d)) { + Class *typeClass = typeToClass(); + ReportTypeError(cx, val, typeClass->name); + return false; + } + + if (TypeIsFloatingPoint<T>()) { + *converted = T(d); + } else if (TypeIsUnsigned<T>()) { + uint32_t n = ToUint32(d); + *converted = T(n); + } else { + int32_t n = ToInt32(d); + *converted = T(n); + } + + return true; } -JSBool DataInstanceUpdate(JSContext *cx, unsigned argc, jsval *vp) +template <typename T> +JSBool +NumericType<T>::call(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() < 1) { + char *fnName = JS_EncodeString(cx, args.callee().as<JSFunction>().atom()); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED, + fnName, "0", "s"); + JS_free(cx, (void *) fnName); + return false; + } + + RootedValue arg(cx, args[0]); + T answer; + if (!convert(cx, arg, &answer)) + return false; // convert() raises TypeError. + + // TODO Once reify is implemented (in a later patch) this will be replaced + // by a call to reify. + args.rval().set(NumberValue(answer)); + return true; +} + +template<unsigned int N> +JSBool +NumericTypeToString(JSContext *cx, unsigned int argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + JS_ASSERT(NUMERICTYPE_UINT8 <= N && N <= NUMERICTYPE_FLOAT64); + JSString *s = JS_NewStringCopyZ(cx, NumericTypeClasses[N].name); + args.rval().set(StringValue(s)); + return true; +} + +JSBool +createArrayType(JSContext *cx, unsigned argc, Value *vp) { return false; } JSBool -ArrayTypeObject::repeat(JSContext *cx, unsigned int argc, jsval *vp) +createStructType(JSContext *cx, unsigned argc, Value *vp) +{ + return false; +} + +JSBool +DataInstanceUpdate(JSContext *cx, unsigned argc, Value *vp) +{ + return false; +} + +JSBool +ArrayTypeObject::repeat(JSContext *cx, unsigned int argc, Value *vp) { return false; } bool GlobalObject::initDataObject(JSContext *cx, Handle<GlobalObject *> global) { RootedObject DataProto(cx); - DataProto = NewObjectWithGivenProto(cx, &DataClass, global->getOrCreateObjectPrototype(cx), global, SingletonObject); + DataProto = NewObjectWithGivenProto(cx, &DataClass, + global->getOrCreateObjectPrototype(cx), + global, SingletonObject); if (!DataProto) return false; RootedAtom DataName(cx, ClassName(JSProto_Data, cx)); - RootedFunction DataCtor(cx, global->createConstructor(cx, DataThrowError, DataName, 1, JSFunction::ExtendedFinalizeKind)); + RootedFunction DataCtor(cx, + global->createConstructor(cx, DataThrowError, DataName, + 1, JSFunction::ExtendedFinalizeKind)); + if (!DataCtor) return false; if (!JS_DefineFunction(cx, DataProto, "update", DataInstanceUpdate, 1, 0)) return false; if (!LinkConstructorAndPrototype(cx, DataCtor, DataProto)) return false; - if (!DefineConstructorAndPrototype(cx, global, JSProto_Data, DataCtor, DataProto)) + if (!DefineConstructorAndPrototype(cx, global, JSProto_Data, + DataCtor, DataProto)) return false; global->setReservedSlot(JSProto_Data, ObjectValue(*DataCtor)); return true; } bool GlobalObject::initTypeObject(JSContext *cx, Handle<GlobalObject *> global) { RootedObject TypeProto(cx, global->getOrCreateDataObject(cx)); if (!TypeProto) return false; RootedAtom TypeName(cx, ClassName(JSProto_Type, cx)); - RootedFunction TypeCtor(cx, global->createConstructor(cx, TypeThrowError, TypeName, 1, JSFunction::ExtendedFinalizeKind)); + RootedFunction TypeCtor(cx, + global->createConstructor(cx, TypeThrowError, TypeName, + 1, JSFunction::ExtendedFinalizeKind)); if (!TypeCtor) return false; if (!LinkConstructorAndPrototype(cx, TypeCtor, TypeProto)) return false; - if (!DefineConstructorAndPrototype(cx, global, JSProto_Type, TypeCtor, TypeProto)) + if (!DefineConstructorAndPrototype(cx, global, JSProto_Type, + TypeCtor, TypeProto)) return false; global->setReservedSlot(JSProto_Type, ObjectValue(*TypeCtor)); return true; } static JSObject * -SetupComplexHeirarchy(JSContext *cx, Handle<GlobalObject *> global, HandleObject complexObject) +SetupComplexHeirarchy(JSContext *cx, Handle<GlobalObject *> global, + HandleObject complexObject) { // get the 'Type' constructor RootedObject TypeObject(cx, global->getOrCreateTypeObject(cx)); if (!TypeObject) return NULL; // Set complexObject.__proto__ = Type if (!JS_SetPrototype(cx, complexObject, TypeObject)) return NULL; RootedObject DataObject(cx, global->getOrCreateDataObject(cx)); if (!DataObject) return NULL; RootedValue DataProtoVal(cx); - if (!JSObject::getProperty(cx, DataObject, DataObject, cx->names().classPrototype, &DataProtoVal)) + if (!JSObject::getProperty(cx, DataObject, DataObject, + cx->names().classPrototype, &DataProtoVal)) return NULL; RootedObject DataProto(cx, DataProtoVal.toObjectOrNull()); if (!DataProto) return NULL; // Set complexObject.prototype.__proto__ = Data RootedObject prototypeObj(cx, JS_NewObject(cx, NULL, NULL, global)); if (!LinkConstructorAndPrototype(cx, complexObject, prototypeObj)) return NULL; if (!JS_SetPrototype(cx, prototypeObj, DataObject)) return NULL; // Set complexObject.prototype.prototype.__proto__ = Data.prototype - // TODO does this have to actually be a Class so we can set accessor properties etc? - RootedObject prototypePrototypeObj(cx, JS_NewObject(cx, NULL, NULL, global)); - if (!LinkConstructorAndPrototype(cx, prototypeObj, prototypePrototypeObj)) + RootedObject prototypePrototypeObj(cx, JS_NewObject(cx, NULL, NULL, + global)); + + if (!LinkConstructorAndPrototype(cx, prototypeObj, + prototypePrototypeObj)) return NULL; if (!JS_SetPrototype(cx, prototypePrototypeObj, DataProto)) return NULL; return complexObject; } static JSObject * InitComplexClasses(JSContext *cx, Handle<GlobalObject *> global) { - // TODO FIXME use DefineConstructorAndPrototype and other - // utilities - RootedFunction ArrayTypeFun(cx, JS_DefineFunction(cx, global, "ArrayType", createArrayType, 1, 0)); + RootedFunction ArrayTypeFun(cx, + JS_DefineFunction(cx, global, "ArrayType", createArrayType, 1, 0)); + if (!ArrayTypeFun) return NULL; if (!SetupComplexHeirarchy(cx, global, ArrayTypeFun)) return NULL; // ArrayType.prototype.repeat RootedValue ArrayTypePrototypeVal(cx); - if (!JSObject::getProperty(cx, ArrayTypeFun, ArrayTypeFun, cx->names().classPrototype, &ArrayTypePrototypeVal)) + if (!JSObject::getProperty(cx, ArrayTypeFun, ArrayTypeFun, + cx->names().classPrototype, &ArrayTypePrototypeVal)) return NULL; - if (!JS_DefineFunction(cx, ArrayTypePrototypeVal.toObjectOrNull(), "repeat", ArrayTypeObject::repeat, 1, 0)) + if (!JS_DefineFunction(cx, ArrayTypePrototypeVal.toObjectOrNull(), "repeat", + ArrayTypeObject::repeat, 1, 0)) return NULL; - RootedFunction StructTypeFun(cx, JS_DefineFunction(cx, global, "StructType", createStructType, 1, 0)); + RootedFunction StructTypeFun(cx, + JS_DefineFunction(cx, global, "StructType", createStructType, 1, 0)); + if (!StructTypeFun) return NULL; if (!SetupComplexHeirarchy(cx, global, StructTypeFun)) return NULL; return global; } JSObject * js_InitBinaryDataClasses(JSContext *cx, HandleObject obj) { JS_ASSERT(obj->is<GlobalObject>()); Rooted<GlobalObject *> global(cx, &obj->as<GlobalObject>()); -typedef float_t float32_t; -typedef double_t float64_t; -#define BINARYDATA_NUMERIC_DEFINE(type_)\ + JSObject *funProto = JS_GetFunctionPrototype(cx, global); +#define BINARYDATA_NUMERIC_DEFINE(constant_, type_)\ do {\ - JSFunction *numFun = JS_DefineFunction(cx, obj, #type_, createNumericBlock, 1, 0);\ + RootedObject numFun(cx, JS_DefineObject(cx, global, #type_,\ + (JSClass *) &NumericTypeClasses[constant_], funProto, 0));\ +\ if (!numFun)\ return NULL;\ \ - if (!JS_DefineProperty(cx, numFun, "bytes", INT_TO_JSVAL(sizeof(type_##_t)), JS_PropertyStub, JS_StrictPropertyStub, 0))\ - return NULL;\ + numFun->setFixedSlot(SLOT_DATATYPE, Int32Value(constant_));\ +\ + RootedValue sizeVal(cx, NumberValue(sizeof(type_##_t)));\ + if (!JSObject::defineProperty(cx, numFun, cx->names().bytes,\ + sizeVal,\ + NULL, NULL,\ + JSPROP_READONLY | JSPROP_PERMANENT))\ + return NULL;\ +\ + if (!JS_DefineFunction(cx, numFun, "toString",\ + NumericTypeToString<constant_>, 0, 0))\ + return NULL;\ } while(0); BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_NUMERIC_DEFINE) #undef BINARYDATA_NUMERIC_DEFINE if (!InitComplexClasses(cx, global)) return NULL; return global; }
--- a/js/src/builtin/BinaryData.h +++ b/js/src/builtin/BinaryData.h @@ -12,16 +12,44 @@ #include "jsfriendapi.h" #include "gc/Heap.h" namespace js { class Block : public gc::Cell { }; +typedef float float32_t; +typedef double float64_t; + +enum { + NUMERICTYPE_UINT8 = 0, + NUMERICTYPE_UINT16, + NUMERICTYPE_UINT32, + NUMERICTYPE_UINT64, + NUMERICTYPE_INT8, + NUMERICTYPE_INT16, + NUMERICTYPE_INT32, + NUMERICTYPE_INT64, + NUMERICTYPE_FLOAT32, + NUMERICTYPE_FLOAT64, + NUMERICTYPES +}; + +enum TypeCommonSlots { + SLOT_MEMSIZE = 0, + SLOT_ALIGN, + TYPE_RESERVED_SLOTS +}; + +enum BlockCommonSlots { + SLOT_DATATYPE = 0, + BLOCK_RESERVED_SLOTS +}; + static Class DataClass = { "Data", JSCLASS_HAS_CACHED_PROTO(JSProto_Data), JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, @@ -36,44 +64,87 @@ static Class TypeClass = { JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub }; +template <typename T> +class NumericType +{ + private: + static Class * typeToClass(); + public: + static bool convert(JSContext *cx, HandleValue val, T *converted); + static bool reify(JSContext *cx, void *mem, MutableHandleValue vp); + static JSBool call(JSContext *cx, unsigned argc, Value *vp); +}; + +template <typename T> +JS_ALWAYS_INLINE +bool NumericType<T>::reify(JSContext *cx, void *mem, MutableHandleValue vp) +{ + vp.setInt32(* ((T*)mem) ); + return true; +} + +template <> +JS_ALWAYS_INLINE +bool NumericType<float32_t>::reify(JSContext *cx, void *mem, MutableHandleValue vp) +{ + vp.setNumber(* ((float32_t*)mem) ); + return true; +} + +template <> +JS_ALWAYS_INLINE +bool NumericType<float64_t>::reify(JSContext *cx, void *mem, MutableHandleValue vp) +{ + vp.setNumber(* ((float64_t*)mem) ); + return true; +} #define BINARYDATA_FOR_EACH_NUMERIC_TYPES(macro_)\ - macro_(uint8)\ - macro_(uint16)\ - macro_(uint32)\ - macro_(uint64)\ - macro_(int8)\ - macro_(int16)\ - macro_(int32)\ - macro_(int64)\ - macro_(float32)\ - macro_(float64) + macro_(NUMERICTYPE_UINT8, uint8)\ + macro_(NUMERICTYPE_UINT16, uint16)\ + macro_(NUMERICTYPE_UINT32, uint32)\ + macro_(NUMERICTYPE_UINT64, uint64)\ + macro_(NUMERICTYPE_INT8, int8)\ + macro_(NUMERICTYPE_INT16, int16)\ + macro_(NUMERICTYPE_INT32, int32)\ + macro_(NUMERICTYPE_INT64, int64)\ + macro_(NUMERICTYPE_FLOAT32, float32)\ + macro_(NUMERICTYPE_FLOAT64, float64) -#define BINARYDATA_NUMERIC_CLASSES(type_)\ -static Class type_##BlockClass = {\ +#define BINARYDATA_NUMERIC_CLASSES(constant_, type_)\ +{\ #type_,\ + JSCLASS_HAS_RESERVED_SLOTS(1) |\ JSCLASS_HAS_CACHED_PROTO(JSProto_##type_),\ JS_PropertyStub, /* addProperty */\ JS_DeletePropertyStub, /* delProperty */\ JS_PropertyStub, /* getProperty */\ JS_StrictPropertyStub, /* setProperty */\ JS_EnumerateStub,\ JS_ResolveStub,\ - JS_ConvertStub\ + JS_ConvertStub,\ + NULL,\ + NULL,\ + NumericType<type_##_t>::call,\ + NULL,\ + NULL,\ + NULL\ +}, + +static Class NumericTypeClasses[NUMERICTYPES] = { + BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_NUMERIC_CLASSES) }; -BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_NUMERIC_CLASSES) - static Class ArrayTypeClass = { "ArrayType", JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayType), JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub,
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1909,18 +1909,18 @@ static const JSStdName standard_class_na {js_InitTypedArrayClasses, EAGER_CLASS_ATOM(Uint8ClampedArray), TYPED_ARRAY_CLASP(TYPE_UINT8_CLAMPED)}, {js_InitTypedArrayClasses, EAGER_CLASS_ATOM(DataView), &DataViewObject::class_}, /* Binary Data */ #ifdef ENABLE_BINARYDATA {js_InitBinaryDataClasses, EAGER_ATOM_AND_CLASP(Type)}, {js_InitBinaryDataClasses, EAGER_ATOM_AND_CLASP(Data)}, -#define BINARYDATA_NUMERIC_NAMES(type_)\ - {js_InitBinaryDataClasses, EAGER_CLASS_ATOM(type_), CLASP(type_##Block)}, +#define BINARYDATA_NUMERIC_NAMES(constant_, type_)\ + {js_InitBinaryDataClasses, EAGER_CLASS_ATOM(type_), &NumericTypeClasses[constant_]}, BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_NUMERIC_NAMES) #undef BINARYDATA_NUMERIC_NAMES {js_InitBinaryDataClasses, EAGER_ATOM_AND_CLASP(ArrayType)}, {js_InitBinaryDataClasses, EAGER_ATOM_AND_CLASP(StructType)}, #endif {NULL, 0, NULL} };
--- a/js/src/tests/ecma_6/BinaryData/architecture.js +++ b/js/src/tests/ecma_6/BinaryData/architecture.js @@ -9,16 +9,27 @@ function assertNotEq(a, b) { } catch(exc) { ok = true; } if (!ok) throw new TypeError("Assertion failed: assertNotEq(" + a + " " + b + ")"); } +function assertThrows(f) { + var ok = false; + try { + f(); + } catch (exc) { + ok = true; + } + if (!ok) + throw new TypeError("Assertion failed: " + f + " did not throw as expected"); +} + function runTests() { print(BUGNUMBER + ": " + summary); assertEq(Data.__proto__, Function.prototype); assertEq(Data.prototype.__proto__, Object.prototype); assertEq(Data.prototype.constructor, Data); assertEq(typeof Data.prototype.update === "function", true); @@ -26,16 +37,17 @@ function runTests() { assertEq(Type.prototype, Data); var sizes = [1, 2, 4, 8, 1, 2, 4, 8, 4, 8]; [ uint8, uint16, uint32, uint64, int8, int16, int32, int64, float32, float64 ].forEach(function(numType, i) { assertEq(numType.__proto__, Function.prototype); assertEq(numType.bytes, sizes[i]); + assertThrows(function() new numType()); }); assertEq(ArrayType.__proto__, Type); assertEq(ArrayType.prototype.__proto__, Type.prototype); assertEq(typeof ArrayType.prototype.repeat === "function", true); assertEq(ArrayType.prototype.prototype.__proto__, Data.prototype);
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/BinaryData/numerictypes.js @@ -0,0 +1,184 @@ +// |reftest| skip-if(!this.hasOwnProperty("Type")) +var BUGNUMBER = 578700; +var summary = 'BinaryData numeric types'; +var actual = ''; +var expect = ''; + +function runTests() +{ + printBugNumber(BUGNUMBER); + printStatus(summary); + + var TestPassCount = 0; + var TestFailCount = 0; + var TestTodoCount = 0; + + var TODO = 1; + + function check(fun, todo) { + var thrown = null; + var success = false; + try { + success = fun(); + } catch (x) { + thrown = x; + } + + if (thrown) + success = false; + + if (todo) { + TestTodoCount++; + + if (success) { + var ex = new Error; + print ("=== TODO but PASSED? ==="); + print (ex.stack); + print ("========================"); + } + + return; + } + + if (success) { + TestPassCount++; + } else { + TestFailCount++; + + var ex = new Error; + print ("=== FAILED ==="); + print (ex.stack); + if (thrown) { + print (" threw exception:"); + print (thrown); + } + print ("=============="); + } + } + + function checkThrows(fun, todo) { + var thrown = false; + try { + fun(); + } catch (x) { + thrown = true; + } + + check(function() thrown, todo); + } + + var types = [uint8, uint16, uint32, uint64, int8, int16, int32, int64]; + var strings = ["uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64"]; + for (var i = 0; i < types.length; i++) { + var type = types[i]; + + check(function() type(true) === 1); + check(function() type(false) === 0); + check(function() type(+Infinity) === 0); + check(function() type(-Infinity) === 0); + check(function() type(NaN) === 0); + check(function() type.toString() === strings[i]); + check(function() type(null) == 0); + check(function() type(undefined) == 0); + check(function() type([]) == 0); + check(function() type({}) == 0); + check(function() type(/abcd/) == 0); + + checkThrows(function() new type()); + } + + var floatTypes = [float32, float64]; + var floatStrings = ["float32", "float64"]; + for (var i = 0; i < floatTypes.length; i++) { + var type = floatTypes[i]; + + check(function() type(true) === 1); + check(function() type(false) === 0); + check(function() type(+Infinity) === Infinity); + check(function() type(-Infinity) === -Infinity); + check(function() Number.isNaN(type(NaN))); + check(function() type.toString() === floatStrings[i]); + check(function() type(null) == 0); + check(function() Number.isNaN(type(undefined))); + check(function() Number.isNaN(type([]))); + check(function() Number.isNaN(type({}))); + check(function() Number.isNaN(type(/abcd/))); + + checkThrows(function() new type()); + } + + ///// test ranges and creation + /// uint8 + // valid + check(function() uint8(0) == 0); + check(function() uint8(-0) == 0); + check(function() uint8(129) == 129); + check(function() uint8(255) == 255); + + if (typeof ctypes != "undefined") { + check(function() uint8(ctypes.Uint64(99)) == 99); + check(function() uint8(ctypes.Int64(99)) == 99); + } + + // overflow is allowed for explicit conversions + check(function() uint8(-1) == 255); + check(function() uint8(-255) == 1); + check(function() uint8(256) == 0); + check(function() uint8(2345678) == 206); + check(function() uint8(3.14) == 3); + check(function() uint8(342.56) == 86); + check(function() uint8(-342.56) == 170); + + if (typeof ctypes != "undefined") { + checkThrows(function() uint8(ctypes.Uint64("18446744073709551615")) == 255); + checkThrows(function() uint8(ctypes.Int64("0xcafebabe")) == 190); + } + + // strings + check(function() uint8("0") == 0); + check(function() uint8("255") == 255); + check(function() uint8("256") == 0); + check(function() uint8("0x0f") == 15); + check(function() uint8("0x00") == 0); + check(function() uint8("0xff") == 255); + check(function() uint8("0x1ff") == 255); + // in JS, string literals with leading zeroes are interpreted as decimal + check(function() uint8("-0777") == 247); + check(function() uint8("-0xff") == 0); + + /// uint16 + // valid + check(function() uint16(65535) == 65535); + + if (typeof ctypes != "undefined") { + check(function() uint16(ctypes.Uint64("0xb00")) == 2816); + check(function() uint16(ctypes.Int64("0xb00")) == 2816); + } + + // overflow is allowed for explicit conversions + check(function() uint16(-1) == 65535); + check(function() uint16(-65535) == 1); + check(function() uint16(-65536) == 0); + check(function() uint16(65536) == 0); + + if (typeof ctypes != "undefined") { + check(function() uint16(ctypes.Uint64("18446744073709551615")) == 65535); + check(function() uint16(ctypes.Int64("0xcafebabe")) == 47806); + } + + // strings + check(function() uint16("0x1234") == 0x1234); + check(function() uint16("0x00") == 0); + check(function() uint16("0xffff") == 65535); + check(function() uint16("-0xffff") == 0); + check(function() uint16("0xffffff") == 0xffff); + + // wrong types + check(function() uint16(3.14) == 3); // c-like casts in explicit conversion + + print("done"); + + reportCompare(0, TestFailCount, "BinaryData numeric type tests"); +} + +runTests();
--- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -15,16 +15,17 @@ #define FOR_EACH_COMMON_PROPERTYNAME(macro) \ macro(anonymous, anonymous, "anonymous") \ macro(apply, apply, "apply") \ macro(arguments, arguments, "arguments") \ macro(buffer, buffer, "buffer") \ macro(builder, builder, "builder") \ macro(byteLength, byteLength, "byteLength") \ macro(byteOffset, byteOffset, "byteOffset") \ + macro(bytes, bytes, "bytes") \ macro(BYTES_PER_ELEMENT, BYTES_PER_ELEMENT, "BYTES_PER_ELEMENT") \ macro(call, call, "call") \ macro(callee, callee, "callee") \ macro(caller, caller, "caller") \ macro(callFunction, callFunction, "callFunction") \ macro(caseFirst, caseFirst, "caseFirst") \ macro(classPrototype, classPrototype, "prototype") \ macro(Collator, Collator, "Collator") \
--- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -1326,16 +1326,41 @@ js::ClampDoubleToUint8(const double x) * want. */ return (y & ~1); } return y; } +bool +js::ToDoubleForTypedArray(JSContext *cx, JS::HandleValue vp, double *d) +{ + if (vp.isDouble()) { + *d = vp.toDouble(); + } else if (vp.isNull()) { + *d = 0.0; + } else if (vp.isPrimitive()) { + JS_ASSERT(vp.isString() || vp.isUndefined() || vp.isBoolean()); + if (vp.isString()) { + if (!ToNumber(cx, vp, d)) + return false; + } else if (vp.isUndefined()) { + *d = js_NaN; + } else { + *d = double(vp.toBoolean()); + } + } else { + // non-primitive assignments become NaN or 0 (for float/int arrays) + *d = js_NaN; + } + + return true; +} + /* * This method is used to trace TypedArrayObjects and DataViewObjects. We need * a custom tracer because some of an ArrayBufferViewObject's reserved slots * are weak references, and some need to be updated specially during moving * GCs. */ /* static */ void ArrayBufferViewObject::trace(JSTracer *trc, JSObject *obj) @@ -1497,54 +1522,29 @@ class TypedArrayObjectTemplate : public vp.setUndefined(); return true; } return JSObject::getElementIfPresent(cx, proto, receiver, index, vp, present); } static bool - toDoubleForTypedArray(JSContext *cx, HandleValue vp, double *d) - { - if (vp.isDouble()) { - *d = vp.toDouble(); - } else if (vp.isNull()) { - *d = 0.0; - } else if (vp.isPrimitive()) { - JS_ASSERT(vp.isString() || vp.isUndefined() || vp.isBoolean()); - if (vp.isString()) { - if (!ToNumber(cx, vp, d)) - return false; - } else if (vp.isUndefined()) { - *d = js_NaN; - } else { - *d = double(vp.toBoolean()); - } - } else { - // non-primitive assignments become NaN or 0 (for float/int arrays) - *d = js_NaN; - } - - return true; - } - - static bool setElementTail(JSContext *cx, HandleObject tarray, uint32_t index, MutableHandleValue vp, JSBool strict) { JS_ASSERT(tarray); JS_ASSERT(index < tarray->as<TypedArrayObject>().length()); if (vp.isInt32()) { setIndex(tarray, index, NativeType(vp.toInt32())); return true; } double d; - if (!toDoubleForTypedArray(cx, vp, &d)) + if (!ToDoubleForTypedArray(cx, vp, &d)) return false; // If the array is an integer array, we only handle up to // 32-bit ints from this point on. if we want to handle // 64-bit ints, we'll need some changes. // Assign based on characteristics of the destination type if (ArrayTypeIsFloatingPoint()) {
--- a/js/src/vm/TypedArrayObject.h +++ b/js/src/vm/TypedArrayObject.h @@ -585,16 +585,18 @@ ClampIntForUint8Array(int32_t x) { if (x < 0) return 0; if (x > 255) return 255; return x; } +bool ToDoubleForTypedArray(JSContext *cx, JS::HandleValue vp, double *d); + } // namespace js template <> inline bool JSObject::is<js::TypedArrayObject>() const { return js::IsTypedArrayClass(getClass()); }