Bug 898356 Part 3 -- Adjust constructor for typed objects to more closely match upcoming spec r=sfink
authorNicholas D. Matsakis <nmatsakis@mozilla.com>
Wed, 12 Feb 2014 14:20:48 -0500
changeset 170071 4285c0b53c33b2893512ddd3700e07de9acc06bb
parent 170070 f163330fdc38e1d1075eb4052903c5f453afa148
child 170072 b2ed6214694fb63f82a5038e9bf1e17b9e762f9e
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewerssfink
bugs898356
milestone30.0a1
Bug 898356 Part 3 -- Adjust constructor for typed objects to more closely match upcoming spec r=sfink
js/src/builtin/TypedObject.cpp
js/src/builtin/TypedObject.h
js/src/tests/ecma_6/TypedObject/referencetypetrace.js
js/src/tests/ecma_6/TypedObject/unsizedarrays.js
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -233,34 +233,34 @@ const Class UnsizedArrayTypeDescr::class
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     nullptr,
     nullptr,
     nullptr,
-    TypedObject::construct,
+    TypedObject::constructUnsized,
     nullptr
 };
 
 const Class SizedArrayTypeDescr::class_ = {
     "ArrayType",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS),
     JS_PropertyStub,
     JS_DeletePropertyStub,
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     nullptr,
     nullptr,
     nullptr,
-    TypedObject::construct,
+    TypedObject::constructSized,
     nullptr
 };
 
 const JSPropertySpec ArrayMetaTypeDescr::typeObjectProperties[] = {
     JS_PS_END
 };
 
 const JSFunctionSpec ArrayMetaTypeDescr::typeObjectMethods[] = {
@@ -538,17 +538,17 @@ const Class StructTypeDescr::class_ = {
     JS_PropertyStub,
     JS_StrictPropertyStub,
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     nullptr, /* finalize */
     nullptr, /* call */
     nullptr, /* hasInstance */
-    TypedObject::construct,
+    TypedObject::constructSized,
     nullptr  /* trace */
 };
 
 const JSPropertySpec StructMetaTypeDescr::typeObjectProperties[] = {
     JS_PS_END
 };
 
 const JSFunctionSpec StructMetaTypeDescr::typeObjectMethods[] = {
@@ -2003,16 +2003,36 @@ const Class TypedObject::class_ = {
         TypedDatum::obj_deleteSpecial,
         nullptr, nullptr, // watch/unwatch
         nullptr,   /* slice */
         TypedDatum::obj_enumerate,
         nullptr, /* thisObject */
     }
 };
 
+static int32_t
+LengthForType(TypeDescr &descr)
+{
+    switch (descr.kind()) {
+      case TypeDescr::Scalar:
+      case TypeDescr::Reference:
+      case TypeDescr::Struct:
+      case TypeDescr::X4:
+        return 0;
+
+      case TypeDescr::SizedArray:
+        return descr.as<SizedArrayTypeDescr>().length();
+
+      case TypeDescr::UnsizedArray:
+        return 0;
+    }
+
+    MOZ_ASSUME_UNREACHABLE("Invalid kind");
+}
+
 /*static*/ TypedObject *
 TypedObject::createZeroed(JSContext *cx,
                           HandleTypeDescr descr,
                           int32_t length)
 {
     // Create unattached wrapper object.
     Rooted<TypedObject*> obj(cx);
     obj = createUnattached<TypedObject>(cx, descr, length);
@@ -2070,63 +2090,140 @@ TypedObject::createZeroed(JSContext *cx,
         return obj;
       }
     }
 
     MOZ_ASSUME_UNREACHABLE("Bad TypeRepresentation Kind");
 }
 
 /*static*/ bool
-TypedObject::construct(JSContext *cx, unsigned int argc, Value *vp)
+TypedObject::constructSized(JSContext *cx, unsigned int argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    JS_ASSERT(args.callee().is<SizedTypeDescr>());
+    Rooted<SizedTypeDescr*> callee(cx, &args.callee().as<SizedTypeDescr>());
+
+    // Typed object constructors for sized types are overloaded in
+    // three ways, in order of precedence:
+    //
+    //   new TypeObj()
+    //   new TypeObj(buffer, [offset]) [1]
+    //   new TypeObj(data)
+    //
+    // [1] coming in a later patch in this series
+
+    // Zero argument constructor:
+    if (argc == 0) {
+        int32_t length = LengthForType(*callee);
+        Rooted<TypedDatum*> obj(cx, createZeroed(cx, callee, length));
+        if (!obj)
+            return false;
+        args.rval().setObject(*obj);
+        return true;
+    }
+
+    // Data constructor.
+    if (args[0].isObject()) {
+        // Create the typed object.
+        int32_t length = LengthForType(*callee);
+        Rooted<TypedDatum*> obj(cx, createZeroed(cx, callee, length));
+        if (!obj)
+            return false;
+
+        // Initialize from `arg`.
+        if (!ConvertAndCopyTo(cx, obj, args[0]))
+            return false;
+        args.rval().setObject(*obj);
+        return true;
+    }
+
+    // Something bogus.
+    JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                         nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
+    return false;
+}
+
+/*static*/ bool
+TypedObject::constructUnsized(JSContext *cx, unsigned int argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     JS_ASSERT(args.callee().is<TypeDescr>());
-    Rooted<TypeDescr*> callee(cx, &args.callee().as<TypeDescr>());
-    TypeRepresentation *typeRepr = callee->typeRepresentation();
+    Rooted<UnsizedArrayTypeDescr*> callee(cx);
+    callee = &args.callee().as<UnsizedArrayTypeDescr>();
+
+    // Typed object constructors for unsized arrays are overloaded in
+    // four ways, in order of precedence:
+    //
+    //   new TypeObj(buffer, [offset, [length]]) [1]
+    //   new TypeObj(length)
+    //   new TypeObj(data)
+    //   new TypeObj()
+    //
+    // [1] coming in a later patch in this series
 
-    // Determine the length based on the target type.
-    uint32_t nextArg = 0;
-    int32_t length = 0;
-    switch (typeRepr->kind()) {
-      case TypeDescr::Scalar:
-      case TypeDescr::Reference:
-      case TypeDescr::Struct:
-      case TypeDescr::X4:
-        length = 0;
-        break;
+    // Zero argument constructor:
+    if (argc == 0) {
+        Rooted<TypedDatum*> obj(cx, createZeroed(cx, callee, 0));
+        if (!obj)
+            return false;
+        args.rval().setObject(*obj);
+        return true;
+    }
 
-      case TypeDescr::SizedArray:
-        length = typeRepr->asSizedArray()->length();
-        break;
+    // Length constructor.
+    if (args[0].isInt32()) {
+        int32_t length = args[0].toInt32();
+        Rooted<TypedDatum*> obj(cx, createZeroed(cx, callee, length));
+        if (!obj)
+            return false;
+        args.rval().setObject(*obj);
+        return true;
+    }
 
-      case TypeDescr::UnsizedArray:
-        // First argument is a length.
-        if (nextArg >= argc || !args[nextArg].isInt32()) {
+    // Data constructor for unsized values
+    if (args[0].isObject()) {
+        // Read length out of the object.
+        RootedObject arg(cx, &args[0].toObject());
+        RootedValue lengthVal(cx);
+        if (!JSObject::getProperty(cx, arg, arg, cx->names().length, &lengthVal))
+            return false;
+        if (!lengthVal.isInt32()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage,
                                  nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
             return false;
         }
-        length = args[nextArg++].toInt32();
-        break;
+        int32_t length = lengthVal.toInt32();
+
+        // Check that length * elementSize does not overflow.
+        int32_t elementSize = callee->elementType().size();
+        int32_t byteLength;
+        if (!SafeMul(elementSize, length, &byteLength)) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                                 nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
+            return false;
+        }
+
+        // Create the unsized array.
+        Rooted<TypedDatum*> obj(cx, createZeroed(cx, callee, length));
+        if (!obj)
+            return false;
+
+        // Initialize from `arg`
+        if (!ConvertAndCopyTo(cx, obj, args[0]))
+            return false;
+        args.rval().setObject(*obj);
+        return true;
     }
 
-    // Create zeroed wrapper object.
-    Rooted<TypedObject*> obj(cx, createZeroed(cx, callee, length));
-    if (!obj)
-        return nullptr;
-
-    if (nextArg < argc) {
-        RootedValue initial(cx, args[nextArg++]);
-        if (!ConvertAndCopyTo(cx, obj, initial))
-            return false;
-    }
-
-    args.rval().setObject(*obj);
-    return true;
+    // Something bogus.
+    JS_ReportErrorNumber(cx, js_GetErrorMessage,
+                         nullptr, JSMSG_TYPEDOBJECT_BAD_ARGS);
+    return false;
 }
 
 /******************************************************************************
  * Handles
  */
 
 const Class TypedHandle::class_ = {
     "Handle",
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -447,17 +447,20 @@ class TypedObject : public TypedDatum
     // Creates a new typed object whose memory is freshly allocated
     // and initialized with zeroes (or, in the case of references, an
     // appropriate default value).
     static TypedObject *createZeroed(JSContext *cx,
                                      HandleTypeDescr typeObj,
                                      int32_t length);
 
     // user-accessible constructor (`new TypeDescriptor(...)`)
-    static bool construct(JSContext *cx, unsigned argc, Value *vp);
+    static bool constructSized(JSContext *cx, unsigned argc, Value *vp);
+
+    // user-accessible constructor (`new TypeDescriptor(...)`)
+    static bool constructUnsized(JSContext *cx, unsigned argc, Value *vp);
 };
 
 typedef Handle<TypedObject*> HandleTypedObject;
 
 class TypedHandle : public TypedDatum
 {
   public:
     static const Class class_;
--- a/js/src/tests/ecma_6/TypedObject/referencetypetrace.js
+++ b/js/src/tests/ecma_6/TypedObject/referencetypetrace.js
@@ -44,17 +44,17 @@ function TestArrayElements(RefType) {
   assertCanReach(s1, rabbit);
   s1[0] = null;
   assertCannotReach(s1, rabbit);
 }
 
 function TestUnsizedArrayElements(RefType) {
   var rabbit = {};
   var S1 = new ArrayType(RefType);
-  var s1 = new S1(1, [rabbit]);
+  var s1 = new S1([rabbit]);
   assertCanReach(s1, rabbit);
   s1[0] = null;
   assertCannotReach(s1, rabbit);
 }
 
 function TestStructInArray(RefType) {
   var rabbit = {};
   var S2 = new StructType({f: RefType, g: RefType});
--- a/js/src/tests/ecma_6/TypedObject/unsizedarrays.js
+++ b/js/src/tests/ecma_6/TypedObject/unsizedarrays.js
@@ -11,18 +11,18 @@ var { ArrayType, StructType, uint8, floa
 var ObjectType = TypedObject.Object;
 
 function runTests() {
   print(BUGNUMBER + ": " + summary);
 
   (function SimpleArrayOfTwoObjects() {
     print("SimpleArrayOfTwoObjects");
     var Objects = new ArrayType(ObjectType);
-    var objects2 = new Objects(2, [{f: "Hello"},
-                                   {f: "World"}]);
+    var objects2 = new Objects([{f: "Hello"},
+                                {f: "World"}]);
     assertEq(objects2[0].f, "Hello");
     assertEq(objects2[1].f, "World");
     assertEq(objects2.length, 2);
   })();
 
   (function EmbedUnsizedArraysBad() {
     print("EmbedUnsizedArraysBad");
     var Objects = new ArrayType(ObjectType);
@@ -30,17 +30,17 @@ function runTests() {
     assertThrows(() => new StructType({f: Objects}));
   })();
 
   (function MultipleSizes() {
     print("MultipleSizes");
     var Uints = new ArrayType(uint32);
     var Point = new StructType({values: new ArrayType(uint32).dimension(3)});
 
-    var uints = new Uints(3, [0, 1, 2]);
+    var uints = new Uints([0, 1, 2]);
     var point = new Point({values: uints});
 
     assertEq(uints.length, point.values.length);
     for (var i = 0; i < uints.length; i++) {
       assertEq(uints[i], i);
       assertEq(uints[i], point.values[i]);
     }
   })();