# HG changeset patch # Parent 6e5675acb34cb866b5d202fd571d85da05cb35be # User Nikhil Marathe Bug 578700 - Numeric Type implementation. diff --git a/js/src/jsbinarydata.cpp b/js/src/jsbinarydata.cpp --- a/js/src/jsbinarydata.cpp +++ b/js/src/jsbinarydata.cpp @@ -1,16 +1,18 @@ /* -*- 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 "jsbinarydata.h" +#include "mozilla/FloatingPoint.h" + #include "jscompartment.h" #include "jsobj.h" #include "jsinterp.h" #include "vm/GlobalObject.h" using namespace js; @@ -19,39 +21,153 @@ JSBool TypeThrowError(JSContext *cx, uns 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) +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 +bool NumericType::convert(JSContext *cx, Value val, T* converted) +{ + if (val.isBoolean()) { + *converted = val.toBoolean() ? 1 : 0; + return true; + } + + if (val.isNumber()) { + // NOTE is this the right way to do it? + if (val.isInt32()) { + int num = val.toInt32(); + if (InRange(num)) { + *converted = T(num); + return true; + } + } else { + double num = val.toDouble(); + if (InRange(num)) { + *converted = T(num); + return true; + } + } + } + + // TODO conditional processing for (U)Int64 + + return false; +} + +template +bool NumericType::cast(JSContext *cx, Value val, T *casted) +{ + if (convert(cx, val, casted)) + return true; + + if (val.isDouble()) { + double d = val.toDouble(); + if (mozilla::IsInfinite(d) || mozilla::IsNaN(d)) { + *casted = 0; + return true; + } + } + + if (val.isNumber()) { + // [[CCast]] + *casted = (T) val.toNumber(); + return true; + } + + // TODO val is a js-ctypes (U)Int64 + + if (val.isString()) { + // always returns true in this case, so no check + double d; + JS_ValueToNumber(cx, val, &d); + if (mozilla::IsNaN(d)) // non-numeric string + return false; + + // [[CCast]] + *casted = (T) (d); + return true; + } + + return false; +} + +template +JSBool NumericType::call(JSContext *cx, unsigned argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + if (args.length() < 1) // TODO should we raise error? + return false; + + T answer; + if (!cast(cx, args[0], &answer)) + { + char *valueStr = JS_EncodeString(cx, ToString(cx, args[0])); + char *fnName = JS_EncodeString(cx, JS_GetFunctionId(args.callee().toFunction())); + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO, valueStr, fnName); + JS_free(cx, (void *) valueStr); + JS_free(cx, (void *) fnName); + return false; + } + + // TODO reify + args.rval().set(NumberValue(answer)); + return true; +} + +JSBool createArrayType(JSContext *cx, unsigned argc, Value *vp) { return false; } -JSBool createArrayType(JSContext *cx, unsigned argc, jsval *vp) +JSBool createStructType(JSContext *cx, unsigned argc, Value *vp) { return false; } -JSBool createStructType(JSContext *cx, unsigned argc, jsval *vp) -{ - return false; -} - -JSBool DataInstanceUpdate(JSContext *cx, unsigned argc, jsval *vp) +JSBool DataInstanceUpdate(JSContext *cx, unsigned argc, Value *vp) { return false; } JSBool -ArrayTypeObject::repeat(JSContext *cx, unsigned int argc, jsval *vp) +ArrayTypeObject::repeat(JSContext *cx, unsigned int argc, Value *vp) { return false; } static JSObject * InitBaseClasses(JSContext *cx, HandleObject obj) { JSFunction *TypeFun = JS_DefineFunction(cx, obj, "Type", TypeThrowError, 0, 0); @@ -89,17 +205,17 @@ SetupComplexHeirarchy(JSContext *cx, Han else return NULL; // Set complexObject.__proto__ = Type if (!JS_SetPrototype(cx, complexObject, TypeFunObj)) return NULL; // get the 'Data' function - jsval DataVal; + Value DataVal; if (!JS_GetProperty(cx, global, "Data", &DataVal)) return NULL; RootedObject DataFunObj(cx); if (!DataVal.isPrimitive()) DataFunObj = DataVal.toObjectOrNull(); else return NULL; @@ -159,25 +275,25 @@ InitComplexClasses(JSContext *cx, Handle } JSObject * js_InitBinaryDataClasses(JSContext *cx, JSHandleObject obj) { if (!InitBaseClasses(cx, obj)) return NULL; -typedef float_t float32_t; -typedef double_t float64_t; +typedef float float32_t; +typedef double float64_t; #define BINARYDATA_NUMERIC_DEFINE(type_)\ do {\ - JSFunction *numFun = JS_DefineFunction(cx, obj, #type_, createNumericBlock, 1, 0);\ + JSFunction *numFun = JS_DefineFunction(cx, obj, #type_, NumericType::call, 1, 0);\ if (!numFun)\ return NULL;\ \ - if (!JS_DefineProperty(cx, numFun, "bytes", INT_TO_JSVAL(sizeof(type_##_t)), JS_PropertyStub, JS_StrictPropertyStub, 0))\ + if (!JS_DefineProperty(cx, numFun, "bytes", NumberValue(sizeof(type_##_t)), JS_PropertyStub, JS_StrictPropertyStub, 0))\ return NULL;\ } while(0); BINARYDATA_FOR_EACH_NUMERIC_TYPES(BINARYDATA_NUMERIC_DEFINE) #undef BINARYDATA_NUMERIC_DEFINE if (!InitComplexClasses(cx, obj)) return NULL; return obj; diff --git a/js/src/jsbinarydata.h b/js/src/jsbinarydata.h --- a/js/src/jsbinarydata.h +++ b/js/src/jsbinarydata.h @@ -11,16 +11,27 @@ #include "jsobj.h" #include "jsfriendapi.h" #include "gc/Heap.h" namespace js { class Block : public gc::Cell { }; + +template +class NumericType +{ + private: + static bool convert(JSContext *cx, jsval val, T *converted); + static bool cast(JSContext *cx, jsval val, T *casted); + public: + static JSBool call(JSContext *cx, unsigned argc, jsval *vp); +}; + static Class DataClass; static Class TypeClass; #define BINARYDATA_FOR_EACH_NUMERIC_TYPES(macro_)\ macro_(uint8)\ macro_(uint16)\ macro_(uint32)\ macro_(uint64)\ diff --git a/js/src/tests/ecma_6/BinaryData/architecture.js b/js/src/tests/ecma_6/BinaryData/architecture.js --- a/js/src/tests/ecma_6/BinaryData/architecture.js +++ b/js/src/tests/ecma_6/BinaryData/architecture.js @@ -1,25 +1,37 @@ var BUGNUMBER = 578700; var summary = 'Test class diagram'; print(BUGNUMBER + ": " + summary); +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"); +} + assertEq(Type.__proto__, Function.prototype); assertEq(Type.prototype, Data); assertEq(Data.__proto__, Function.prototype); assertEq(Data.prototype.__proto__, Object.prototype); assertEq(Data.prototype.constructor, Data); assertEq(typeof Data.prototype.update === "function", true); 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); diff --git a/js/src/tests/ecma_6/BinaryData/numerictypes.js b/js/src/tests/ecma_6/BinaryData/numerictypes.js new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_6/BinaryData/numerictypes.js @@ -0,0 +1,179 @@ +/* -*- Mode: js2; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Nikhil Marathe + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 578700; +var summary = 'js BinaryData numeric types'; +var actual = ''; +var expect = ''; + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + enterFunc ('test'); + 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, float32, float64]; + 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); + + checkThrows(function() new type()); + checkThrows(function() type(null)); + checkThrows(function() type(undefined)); + checkThrows(function() type([])); + checkThrows(function() type({})); + checkThrows(function() type(/abcd/)); + } + + ///// 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); + checkThrows(function() uint8("-0xff") == 1); + + /// 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); + checkThrows(function() uint16("-0xffff") == 1); // FIXME + check(function() uint16("0xffffff") == 0xffff); + + // wrong types + check(function() uint16(3.14) == 3); // c-like casts in explicit conversion + + checkThrows(function() uint16([1, 2, 3])); + checkThrows(function() uint16({})); + checkThrows(function() uint16("not a number")); + + print ("done"); + + reportCompare(0, TestFailCount, "BinaryData numeric type tests"); + + exitFunc ('test'); +}