Bug 578700 - BinaryData ArrayType method implementations. r=nmatsakis

--HG--
extra : amend_source : 8ae08d1ce9a57328dd6a3f8028abf21d0b806222
This commit is contained in:
Nikhil Marathe 2013-07-25 17:59:59 -07:00
parent add14d406c
commit 0ae601ff06
7 changed files with 347 additions and 28 deletions

View File

@ -81,6 +81,17 @@ ReportTypeError(JSContext *cx, Value fromValue, HandleObject exemplar)
return false;
}
static int32_t
Clamp(int32_t value, int32_t min, int32_t max)
{
JS_ASSERT(min < max);
if (value < min)
return min;
if (value > max)
return max;
return value;
}
static inline bool
IsNumericType(HandleObject type)
{
@ -641,9 +652,15 @@ ArrayType::create(JSContext *cx, HandleObject arrayTypeGlobal,
if (!LinkConstructorAndPrototype(cx, obj, prototypeObj))
return NULL;
if (!JS_DefineFunction(cx, prototypeObj, "fill", BinaryArray::fill, 1, 0))
JSFunction *fillFun = DefineFunctionWithReserved(cx, prototypeObj, "fill", BinaryArray::fill, 1, 0);
if (!fillFun)
return NULL;
// This is important
// so that A.prototype.fill.call(b, val)
// where b.type != A raises an error
SetFunctionNativeReserved(fillFun, 0, ObjectValue(*obj));
RootedId id(cx, NON_INTEGER_ATOM_TO_JSID(cx->names().length));
unsigned flags = JSPROP_SHARED | JSPROP_GETTER | JSPROP_PERMANENT;
@ -702,13 +719,93 @@ ArrayType::construct(JSContext *cx, unsigned argc, Value *vp)
JSBool
DataInstanceUpdate(JSContext *cx, unsigned argc, Value *vp)
{
return false;
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() < 1) {
JS_ReportErrorNumber(cx, js_GetErrorMessage,
NULL, JSMSG_MORE_ARGS_NEEDED,
"update()", "0", "s");
return false;
}
RootedObject thisObj(cx, args.thisv().toObjectOrNull());
if (!IsBlock(thisObj)) {
ReportTypeError(cx, ObjectValue(*thisObj), "BinaryData block");
return false;
}
RootedValue val(cx, args[0]);
uint8_t *memory = (uint8_t*) thisObj->getPrivate();
RootedObject type(cx, GetType(thisObj));
if (!ConvertAndCopyTo(cx, type, val, memory)) {
ReportTypeError(cx, val, type);
return false;
}
args.rval().setUndefined();
return true;
}
static bool
FillBinaryArrayWithValue(JSContext *cx, HandleObject array, HandleValue val)
{
JS_ASSERT(IsBinaryArray(array));
RootedObject type(cx, GetType(array));
RootedObject elementType(cx, ArrayType::elementType(cx, type));
uint8_t *base = (uint8_t *) array->getPrivate();
// set array[0] = [[Convert]](val)
if (!ConvertAndCopyTo(cx, elementType, val, base)) {
ReportTypeError(cx, val, elementType);
return false;
}
size_t elementSize = GetMemSize(cx, elementType);
// Copy a[0] into remaining indices.
for (uint32_t i = 1; i < ArrayType::length(cx, type); i++) {
uint8_t *dest = base + elementSize * i;
memcpy(dest, base, elementSize);
}
return true;
}
JSBool
ArrayType::repeat(JSContext *cx, unsigned int argc, Value *vp)
{
return false;
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() < 1) {
JS_ReportErrorNumber(cx, js_GetErrorMessage,
NULL, JSMSG_MORE_ARGS_NEEDED,
"repeat()", "0", "s");
return false;
}
RootedObject thisObj(cx, args.thisv().toObjectOrNull());
if (!IsArrayType(thisObj)) {
JSString *valueStr = JS_ValueToString(cx, args.thisv());
char *valueChars = "(unknown type)";
if (valueStr)
valueChars = JS_EncodeString(cx, valueStr);
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, "ArrayType", "repeat", valueChars);
if (valueStr)
JS_free(cx, valueChars);
return false;
}
RootedObject binaryArray(cx, BinaryArray::create(cx, thisObj));
if (!binaryArray)
return false;
RootedValue val(cx, args[0]);
if (!FillBinaryArrayWithValue(cx, binaryArray, val))
return false;
args.rval().setObject(*binaryArray);
return true;
}
JSBool
@ -859,25 +956,132 @@ BinaryArray::lengthGetter(JSContext *cx, unsigned int argc, Value *vp)
return true;
}
JSBool
BinaryArray::forEach(JSContext *cx, unsigned int argc, Value *vp)
/**
* The subarray function first creates an ArrayType instance
* which will act as the elementType for the subarray.
*
* var MA = new ArrayType(elementType, 10);
* var mb = MA.repeat(val);
*
* mb.subarray(begin, end=mb.length) => (Only for +ve)
* var internalSA = new ArrayType(elementType, end-begin);
* var ret = new internalSA()
* for (var i = begin; i < end; i++)
* ret[i-begin] = ret[i]
* return ret
*
* The range specified by the begin and end values is clamped to the valid
* index range for the current array. If the computed length of the new
* TypedArray would be negative, it is clamped to zero.
* see: http://www.khronos.org/registry/typedarray/specs/latest/#7
*
*/
JSBool BinaryArray::subarray(JSContext *cx, unsigned int argc, Value *vp)
{
JS_ASSERT(0);
return false;
}
CallArgs args = CallArgsFromVp(argc, vp);
JSBool
BinaryArray::subarray(JSContext *cx, unsigned int argc, Value *vp)
{
JS_ASSERT(0);
return false;
if (args.length() < 1) {
JS_ReportErrorNumber(cx, js_GetErrorMessage,
NULL, JSMSG_MORE_ARGS_NEEDED,
"subarray()", "0", "s");
return false;
}
if (!args[0].isInt32()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BINARYDATA_SUBARRAY_INTEGER_ARG, "1");
return false;
}
RootedObject thisObj(cx, &args.thisv().toObject());
if (!IsBinaryArray(thisObj)) {
ReportTypeError(cx, ObjectValue(*thisObj), "binary array");
return false;
}
RootedObject type(cx, GetType(thisObj));
RootedObject elementType(cx, ArrayType::elementType(cx, type));
uint32_t length = ArrayType::length(cx, type);
int32_t begin = args[0].toInt32();
int32_t end = length;
if (args.length() >= 2) {
if (!args[1].isInt32()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BINARYDATA_SUBARRAY_INTEGER_ARG, "2");
return false;
}
end = args[1].toInt32();
}
if (begin < 0)
begin = length + begin;
if (end < 0)
end = length + end;
begin = Clamp(begin, 0, length);
end = Clamp(end, 0, length);
int32_t sublength = end - begin; // end exclusive
sublength = Clamp(sublength, 0, length);
RootedObject globalObj(cx, cx->compartment()->maybeGlobal());
JS_ASSERT(globalObj);
Rooted<GlobalObject*> global(cx, &globalObj->as<GlobalObject>());
RootedObject arrayTypeGlobal(cx, global->getOrCreateArrayTypeObject(cx));
RootedObject subArrayType(cx, ArrayType::create(cx, arrayTypeGlobal,
elementType, sublength));
if (!subArrayType)
return false;
int32_t elementSize = GetMemSize(cx, elementType);
size_t offset = elementSize * begin;
RootedObject subarray(cx, BinaryArray::create(cx, subArrayType, thisObj, offset));
if (!subarray)
return false;
args.rval().setObject(*subarray);
return true;
}
JSBool
BinaryArray::fill(JSContext *cx, unsigned int argc, Value *vp)
{
JS_ASSERT(0);
return false;
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() < 1) {
JS_ReportErrorNumber(cx, js_GetErrorMessage,
NULL, JSMSG_MORE_ARGS_NEEDED,
"fill()", "0", "s");
return false;
}
if (!args.thisv().isObject())
return false;
RootedObject thisObj(cx, args.thisv().toObjectOrNull());
if (!IsBinaryArray(thisObj)) {
ReportTypeError(cx, ObjectValue(*thisObj), "binary array");
return false;
}
Value funArrayTypeVal = GetFunctionNativeReserved(&args.callee(), 0);
JS_ASSERT(funArrayTypeVal.isObject());
RootedObject type(cx, GetType(thisObj));
RootedObject funArrayType(cx, funArrayTypeVal.toObjectOrNull());
if (!IsSameBinaryDataType(cx, funArrayType, type)) {
ReportTypeError(cx, ObjectValue(*thisObj), funArrayType);
return false;
}
args.rval().setUndefined();
RootedValue val(cx, args[0]);
return FillBinaryArrayWithValue(cx, thisObj, val);
}
JSBool
@ -1660,7 +1864,7 @@ BinaryStruct::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
}
uint8_t *loc = ((uint8_t *) obj->getPrivate()) + fieldInfo.offset;
RootedObject fieldType(cx, fieldInfo.type);
if (!ConvertAndCopyTo(cx, fieldType, vp, loc))
return false;
@ -1800,6 +2004,17 @@ GlobalObject::initTypeObject(JSContext *cx, Handle<GlobalObject *> global)
return true;
}
bool
GlobalObject::initArrayTypeObject(JSContext *cx, Handle<GlobalObject *> global)
{
RootedFunction ctor(cx,
global->createConstructor(cx, ArrayType::construct,
cx->names().ArrayType, 2));
global->setReservedSlot(JSProto_ArrayTypeObject, ObjectValue(*ctor));
return true;
}
static JSObject *
SetupComplexHeirarchy(JSContext *cx, HandleObject obj, JSProtoKey protoKey,
HandleObject complexObject, MutableHandleObject proto,
@ -1865,10 +2080,7 @@ InitArrayType(JSContext *cx, HandleObject obj)
{
JS_ASSERT(obj->isNative());
Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
RootedFunction ctor(cx,
global->createConstructor(cx, ArrayType::construct,
cx->names().ArrayType, 2));
RootedObject ctor(cx, global->getOrCreateArrayTypeObject(cx));
if (!ctor)
return NULL;
@ -1884,7 +2096,17 @@ InitArrayType(JSContext *cx, HandleObject obj)
if (!JS_DefineFunction(cx, proto, "toString", ArrayType::toString, 0, 0))
return NULL;
if (!JS_DefineFunction(cx, protoProto, "forEach", BinaryArray::forEach, 1, 0))
RootedObject arrayProto(cx);
if (!FindProto(cx, &ArrayObject::class_, &arrayProto))
return NULL;
RootedValue forEachFunVal(cx);
RootedAtom forEachAtom(cx, Atomize(cx, "forEach", 7));
RootedId forEachId(cx, AtomToId(forEachAtom));
if (!JSObject::getProperty(cx, arrayProto, arrayProto, forEachAtom->asPropertyName(), &forEachFunVal))
return NULL;
if (!JSObject::defineGeneric(cx, protoProto, forEachId, forEachFunVal, NULL, NULL, 0))
return NULL;
if (!JS_DefineFunction(cx, protoProto, "subarray",

View File

@ -145,11 +145,11 @@ static Class NumericTypeClasses[NUMERICTYPES] = {
class ArrayType : public JSObject
{
private:
static JSObject *create(JSContext *cx, HandleObject arrayTypeGlobal,
HandleObject elementType, uint32_t length);
public:
static Class class_;
static JSObject *create(JSContext *cx, HandleObject arrayTypeGlobal,
HandleObject elementType, uint32_t length);
static JSBool construct(JSContext *cx, unsigned int argc, jsval *vp);
static JSBool repeat(JSContext *cx, unsigned int argc, jsval *vp);
@ -169,8 +169,6 @@ class BinaryArray
private:
static JSObject *createEmpty(JSContext *cx, HandleObject type);
// creates initialized memory of size of type
static JSObject *create(JSContext *cx, HandleObject type);
// attempts to [[Convert]]
static JSObject *create(JSContext *cx, HandleObject type,
HandleValue initial);
@ -178,6 +176,8 @@ class BinaryArray
public:
static Class class_;
// creates initialized memory of size of type
static JSObject *create(JSContext *cx, HandleObject type);
// uses passed block as memory
static JSObject *create(JSContext *cx, HandleObject type,
HandleObject owner, size_t offset);
@ -186,7 +186,6 @@ class BinaryArray
static void finalize(FreeOp *op, JSObject *obj);
static void obj_trace(JSTracer *tracer, JSObject *obj);
static JSBool forEach(JSContext *cx, unsigned int argc, jsval *vp);
static JSBool subarray(JSContext *cx, unsigned int argc, jsval *vp);
static JSBool fill(JSContext *cx, unsigned int argc, jsval *vp);

View File

@ -406,3 +406,4 @@ MSG_DEF(JSMSG_BINARYDATA_ARRAYTYPE_BAD_ARGS, 352, 0, JSEXN_ERR, "Invalid argumen
MSG_DEF(JSMSG_BINARYDATA_BINARYARRAY_BAD_INDEX, 353, 0, JSEXN_RANGEERR, "invalid or out-of-range index")
MSG_DEF(JSMSG_BINARYDATA_STRUCTTYPE_BAD_ARGS, 354, 0, JSEXN_RANGEERR, "invalid field descriptor")
MSG_DEF(JSMSG_BINARYDATA_NOT_BINARYSTRUCT, 355, 1, JSEXN_TYPEERR, "{0} is not a BinaryStruct")
MSG_DEF(JSMSG_BINARYDATA_SUBARRAY_INTEGER_ARG, 356, 1, JSEXN_ERR, "argument {0} must be an integer")

View File

@ -70,5 +70,6 @@
macro(float64, 49, js_InitBinaryDataClasses) \
macro(ArrayType, 50, js_InitBinaryDataClasses) \
macro(StructType, 51, js_InitBinaryDataClasses) \
macro(ArrayTypeObject, 52, js_InitBinaryDataClasses) \
#endif /* jsprototypes_h */

View File

@ -34,6 +34,9 @@ function runTests() {
assertEq(A.prototype.__proto__, ArrayType.prototype.prototype);
assertEq(typeof A.prototype.fill, "function");
var X = { __proto__: A };
assertThrows(function() X.repeat(42));
var a = new A();
assertEq(a.__proto__, A.prototype);
assertEq(a.length, 10);
@ -46,6 +49,10 @@ function runTests() {
for (var i = 0; i < a.length; i++)
assertEq(a[i], i*2);
a.forEach(function(val, i) {
assertEq(val, i*2);
assertEq(arguments[2], a);
});
// Range.
assertThrows(function() a[i] = 5);
@ -55,7 +62,7 @@ function runTests() {
// constructor takes initial value
var b = new A(a);
for (var i = 0; i < a.length; i++)
assertEq(a[i], i*2);
assertEq(b[i], i*2);
var b = new A([0, 1, 0, 1, 0, 1, 0, 1, 0, 1]);
for (var i = 0; i < b.length; i++)
@ -94,8 +101,80 @@ function runTests() {
var as = new AllSprites();
assertEq(as.length, 65536);
// test methods
var c = new A();
c.fill(3);
for (var i = 0; i < c.length; i++)
assertEq(c[i], 3);
as.foo = "bar";
assertThrows(function() c.update([3.14, 4.52, 5]));
//assertThrows(function() c.update([3000, 0, 1, 1, 1, 1, 1, 1, 1, 1]));
assertThrows(function() Vec3.prototype.fill.call(c, 2));
var updatingPos = new Vec3();
updatingPos.update([5, 3, 1]);
assertEq(updatingPos[0], 5);
assertEq(updatingPos[1], 3);
assertEq(updatingPos[2], 1);
var d = A.repeat(10);
for (var i = 0; i < d.length; i++)
assertEq(d[i], 10);
assertThrows(function() ArrayType.prototype.repeat.call(d, 2));
var MA = new ArrayType(uint32, 5);
var ma = new MA([1, 2, 3, 4, 5]);
var mb = ma.subarray(2);
assertEq(mb.length, 3);
assertEq(mb[0], 3);
assertEq(mb[1], 4);
assertEq(mb[2], 5);
assertThrows(function() ma.subarray());
assertThrows(function() ma.subarray(2.14));
assertThrows(function() ma.subarray({}));
assertThrows(function() ma.subarray(2, []));
// check similarity even though mb's ArrayType
// is not script accessible
var Similar = new ArrayType(uint32, 3);
var sim = new Similar();
sim.update(mb);
assertEq(sim[0], 3);
assertEq(sim[1], 4);
assertEq(sim[2], 5);
var range = ma.subarray(0, 3);
assertEq(range.length, 3);
assertEq(range[0], 1);
assertEq(range[1], 2);
assertEq(range[2], 3);
assertEq(ma.subarray(ma.length).length, 0);
assertEq(ma.subarray(ma.length, ma.length-1).length, 0);
var rangeNeg = ma.subarray(-2);
assertEq(rangeNeg.length, 2);
assertEq(rangeNeg[0], 4);
assertEq(rangeNeg[1], 5);
var rangeNeg = ma.subarray(-5, -3);
assertEq(rangeNeg.length, 2);
assertEq(rangeNeg[0], 1);
assertEq(rangeNeg[1], 2);
assertEq(ma.subarray(-2, -3).length, 0);
assertEq(ma.subarray(-6).length, ma.length);
var modifyOriginal = ma.subarray(2);
modifyOriginal[0] = 42;
assertEq(ma[2], 42);
ma[4] = 97;
assertEq(modifyOriginal[2], 97);
var indexPropDesc = Object.getOwnPropertyDescriptor(as, '0');
assertEq(typeof indexPropDesc == "undefined", false);

View File

@ -41,6 +41,18 @@ function runTests() {
var Color = new StructType({'r': uint8, 'g': uint8, 'b': uint8});
var Rainbow = new ArrayType(Color, 7);
var theOneISawWasJustBlack = Rainbow.repeat({'r': 0, 'g': 0, 'b': 0});
var middleBand = theOneISawWasJustBlack[3];
theOneISawWasJustBlack = null;
gc();
spin();
assertEq(middleBand['r'] == 0 && middleBand['g'] == 0 && middleBand['b'] == 0, true);
middleBand.update({'r': 255, 'g': 207, 'b': 142});
assertEq(middleBand['r'] == 255 && middleBand['g'] == 207 && middleBand['b'] == 142, true);
var scopedType = function() {
var Point = new StructType({'x': int32, 'y': int32});
var aPoint = new Point();

View File

@ -338,6 +338,10 @@ class GlobalObject : public JSObject
return getOrCreateObject(cx, JSProto_Type, initTypeObject);
}
JSObject *getOrCreateArrayTypeObject(JSContext *cx) {
return getOrCreateObject(cx, JSProto_ArrayTypeObject, initArrayTypeObject);
}
private:
typedef bool (*ObjectInitOp)(JSContext *cx, Handle<GlobalObject*> global);
@ -443,6 +447,7 @@ class GlobalObject : public JSObject
// Implemented in builtin/BinaryData.cpp
static bool initTypeObject(JSContext *cx, Handle<GlobalObject*> global);
static bool initDataObject(JSContext *cx, Handle<GlobalObject*> global);
static bool initArrayTypeObject(JSContext *cx, Handle<GlobalObject*> global);
static bool initStandardClasses(JSContext *cx, Handle<GlobalObject*> global);