From 9fe0a853923678c8b599f96bdd23e064418d9403 Mon Sep 17 00:00:00 2001 From: Nikhil Marathe Date: Thu, 25 Jul 2013 17:58:50 -0700 Subject: [PATCH] Bug 578700 - Numeric types implementation. r=nmatsakis --HG-- extra : amend_source : 16bd14404c2e30589101f5b965c016b7304b7773 --- js/src/builtin/BinaryData.cpp | 243 +++++++++++++++--- js/src/builtin/BinaryData.h | 101 ++++++-- js/src/jsapi.cpp | 4 +- .../tests/ecma_6/BinaryData/architecture.js | 12 + .../tests/ecma_6/BinaryData/numerictypes.js | 184 +++++++++++++ js/src/vm/CommonPropertyNames.h | 1 + js/src/vm/TypedArrayObject.cpp | 52 ++-- js/src/vm/TypedArrayObject.h | 2 + 8 files changed, 525 insertions(+), 74 deletions(-) create mode 100644 js/src/tests/ecma_6/BinaryData/numerictypes.js diff --git a/js/src/builtin/BinaryData.cpp b/js/src/builtin/BinaryData.cpp index 000e6a2e7948..14f5640faf74 100644 --- a/js/src/builtin/BinaryData.cpp +++ b/js/src/builtin/BinaryData.cpp @@ -6,6 +6,8 @@ #include "builtin/BinaryData.h" +#include "mozilla/FloatingPoint.h" + #include "jscompartment.h" #include "jsobj.h" @@ -13,6 +15,7 @@ #include "jsobjinlines.h" #include "vm/GlobalObject.h" +#include "vm/TypedArrayObject.h" using namespace js; @@ -26,29 +29,179 @@ 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) { - return false; + 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); } -JSBool createArrayType(JSContext *cx, unsigned argc, jsval *vp) +static void +ReportTypeError(JSContext *cx, Value fromValue, JSString *toType) { - return false; + const char *fnName = JS_EncodeString(cx, toType); + ReportTypeError(cx, fromValue, fnName); + JS_free(cx, (void *) fnName); } -JSBool createStructType(JSContext *cx, unsigned argc, jsval *vp) +static inline bool +IsNumericType(JSObject *type) { - return false; + return type && &NumericTypeClasses[NUMERICTYPE_UINT8] <= type->getClass() && + type->getClass() <= &NumericTypeClasses[NUMERICTYPE_FLOAT64]; } -JSBool DataInstanceUpdate(JSContext *cx, unsigned argc, jsval *vp) +template +bool +InRange(Input x) +{ + return std::numeric_limits::min() <= x && + x <= std::numeric_limits::max(); +} + +template <> +bool +InRange(int x) +{ + return -std::numeric_limits::max() <= x && + x <= std::numeric_limits::max(); +} + +template <> +bool +InRange(int x) +{ + return -std::numeric_limits::max() <= x && + x <= std::numeric_limits::max(); +} + +template <> +bool +InRange(double x) +{ + return -std::numeric_limits::max() <= x && + x <= std::numeric_limits::max(); +} + +template <> +bool +InRange(double x) +{ + return -std::numeric_limits::max() <= x && + x <= std::numeric_limits::max(); +} + +template +Class * +js::NumericType::typeToClass() +{ + JS_ASSERT(0); + return NULL; +} + +#define BINARYDATA_TYPE_TO_CLASS(constant_, type_)\ + template <>\ + Class *\ + NumericType::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); +} + +template +bool +NumericType::convert(JSContext *cx, HandleValue val, T* converted) +{ + 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()) { + *converted = T(d); + } else if (TypeIsUnsigned()) { + uint32_t n = ToUint32(d); + *converted = T(n); + } else { + int32_t n = ToInt32(d); + *converted = T(n); + } + + return true; +} + +template +JSBool +NumericType::call(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() < 1) { + char *fnName = JS_EncodeString(cx, args.callee().as().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 +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; } @@ -57,12 +210,17 @@ bool GlobalObject::initDataObject(JSContext *cx, Handle 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; @@ -72,7 +230,8 @@ GlobalObject::initDataObject(JSContext *cx, Handle global) 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)); @@ -87,14 +246,17 @@ GlobalObject::initTypeObject(JSContext *cx, Handle global) 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)); @@ -102,7 +264,8 @@ GlobalObject::initTypeObject(JSContext *cx, Handle global) } static JSObject * -SetupComplexHeirarchy(JSContext *cx, Handle global, HandleObject complexObject) +SetupComplexHeirarchy(JSContext *cx, Handle global, + HandleObject complexObject) { // get the 'Type' constructor RootedObject TypeObject(cx, global->getOrCreateTypeObject(cx)); @@ -118,7 +281,8 @@ SetupComplexHeirarchy(JSContext *cx, Handle global, HandleObject 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()); @@ -134,9 +298,11 @@ SetupComplexHeirarchy(JSContext *cx, Handle global, HandleObject 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)) @@ -148,9 +314,9 @@ SetupComplexHeirarchy(JSContext *cx, Handle global, HandleObject static JSObject * InitComplexClasses(JSContext *cx, Handle 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; @@ -159,13 +325,17 @@ InitComplexClasses(JSContext *cx, Handle global) // 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; @@ -181,16 +351,27 @@ js_InitBinaryDataClasses(JSContext *cx, HandleObject obj) JS_ASSERT(obj->is()); Rooted global(cx, &obj->as()); -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, 0, 0))\ + return NULL;\ } while(0); BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_NUMERIC_DEFINE) #undef BINARYDATA_NUMERIC_DEFINE diff --git a/js/src/builtin/BinaryData.h b/js/src/builtin/BinaryData.h index 45c6e7f1ca92..5f698ab39230 100644 --- a/js/src/builtin/BinaryData.h +++ b/js/src/builtin/BinaryData.h @@ -17,6 +17,34 @@ 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), @@ -41,22 +69,57 @@ static Class TypeClass = { JS_ConvertStub }; +template +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 +JS_ALWAYS_INLINE +bool NumericType::reify(JSContext *cx, void *mem, MutableHandleValue vp) +{ + vp.setInt32(* ((T*)mem) ); + return true; +} + +template <> +JS_ALWAYS_INLINE +bool NumericType::reify(JSContext *cx, void *mem, MutableHandleValue vp) +{ + vp.setNumber(* ((float32_t*)mem) ); + return true; +} + +template <> +JS_ALWAYS_INLINE +bool NumericType::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 */\ @@ -64,10 +127,18 @@ static Class type_##BlockClass = {\ JS_StrictPropertyStub, /* setProperty */\ JS_EnumerateStub,\ JS_ResolveStub,\ - JS_ConvertStub\ -}; + JS_ConvertStub,\ + NULL,\ + NULL,\ + NumericType::call,\ + NULL,\ + NULL,\ + NULL\ +}, -BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_NUMERIC_CLASSES) +static Class NumericTypeClasses[NUMERICTYPES] = { + BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_NUMERIC_CLASSES) +}; static Class ArrayTypeClass = { "ArrayType", diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 867eb8177c11..a93e3c5f8f96 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1914,8 +1914,8 @@ static const JSStdName standard_class_names[] = { #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)}, diff --git a/js/src/tests/ecma_6/BinaryData/architecture.js b/js/src/tests/ecma_6/BinaryData/architecture.js index 456aaea94d75..1827c3ced1cc 100644 --- a/js/src/tests/ecma_6/BinaryData/architecture.js +++ b/js/src/tests/ecma_6/BinaryData/architecture.js @@ -14,6 +14,17 @@ function assertNotEq(a, b) { 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); @@ -31,6 +42,7 @@ function runTests() { 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); diff --git a/js/src/tests/ecma_6/BinaryData/numerictypes.js b/js/src/tests/ecma_6/BinaryData/numerictypes.js new file mode 100644 index 000000000000..566f19e31f67 --- /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(); diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 9fc3b1d06ec3..cb3fb502cfdc 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -20,6 +20,7 @@ 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") \ diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index e27fca509476..7bd7c4a74f39 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -1331,6 +1331,31 @@ js::ClampDoubleToUint8(const double x) 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 @@ -1501,31 +1526,6 @@ class TypedArrayObjectTemplate : public TypedArrayObject 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) @@ -1539,7 +1539,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject } 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 diff --git a/js/src/vm/TypedArrayObject.h b/js/src/vm/TypedArrayObject.h index c7f9962b748e..c825faf7d235 100644 --- a/js/src/vm/TypedArrayObject.h +++ b/js/src/vm/TypedArrayObject.h @@ -590,6 +590,8 @@ ClampIntForUint8Array(int32_t x) return x; } +bool ToDoubleForTypedArray(JSContext *cx, JS::HandleValue vp, double *d); + } // namespace js template <>