Bug 891107 - Part 8: Show information about field name and type in struct field related error messages in js-ctypes. r=jorendorff
authorTooru Fujisawa <arai_a@mac.com>
Fri, 07 Aug 2015 06:58:39 +0900
changeset 288496 d96ab48369288689e7452ba168e1d3bf54673421
parent 288495 1e8e3d17142899118207b146ed5b994127722ef3
child 288497 a5ea955d64f2174ebd018fb1c9ce36486a9910c3
push id30082
push userryanvm@gmail.com
push dateSun, 13 Mar 2016 23:08:35 +0000
treeherdermozilla-central@f0c0480732d3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs891107
milestone48.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
Bug 891107 - Part 8: Show information about field name and type in struct field related error messages in js-ctypes. r=jorendorff
js/src/ctypes/CTypes.cpp
js/src/ctypes/ctypes.msg
js/src/jit-test/tests/ctypes/struct-field.js
toolkit/components/ctypes/tests/unit/test_jsctypes.js
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -1333,16 +1333,29 @@ ArgumentTypeMismatch(JSContext* cx, cons
                      const char* type)
 {
   JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                        CTYPESMSG_ARG_TYPE_MISMATCH, arg, func, type);
   return false;
 }
 
 static bool
+DuplicateFieldError(JSContext* cx, Handle<JSFlatString*> name)
+{
+  JSAutoByteString nameBytes;
+  const char* nameStr = nameBytes.encodeLatin1(cx, name);
+  if (!nameStr)
+    return false;
+
+  JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                       CTYPESMSG_DUPLICATE_FIELD, nameStr);
+  return false;
+}
+
+static bool
 EmptyFinalizerCallError(JSContext* cx, const char* funName)
 {
   JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                        CTYPESMSG_EMPTY_FIN_CALL, funName);
   return false;
 }
 
 static bool
@@ -1407,16 +1420,120 @@ FieldCountMismatch(JSContext* cx,
   JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                        CTYPESMSG_FIELD_MISMATCH,
                        valStr, structStr, expectedCountStr, actualCountStr,
                        posStr);
   return false;
 }
 
 static bool
+FieldDescriptorCountError(JSContext* cx, Value val, size_t length)
+{
+  RootedValue typeVal(cx, val);
+  JSAutoByteString valBytes;
+  const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
+  if (!valStr)
+    return false;
+
+  char lengthStr[16];
+  JS_snprintf(lengthStr, 16, "%u", length);
+
+  JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                       CTYPESMSG_FIELD_DESC_COUNT, valStr, lengthStr);
+  return false;
+}
+
+static bool
+FieldDescriptorNameError(JSContext* cx, HandleId id)
+{
+  JSAutoByteString idBytes;
+  RootedValue idVal(cx, IdToValue(id));
+  const char* propStr = CTypesToSourceForError(cx, idVal, idBytes);
+  if (!propStr)
+    return false;
+
+  JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                       CTYPESMSG_FIELD_DESC_NAME, propStr);
+  return false;
+}
+
+static bool
+FieldDescriptorSizeError(JSContext* cx, HandleObject typeObj, HandleId id)
+{
+  RootedValue typeVal(cx, ObjectValue(*typeObj));
+  JSAutoByteString typeBytes;
+  const char* typeStr = CTypesToSourceForError(cx, typeVal, typeBytes);
+  if (!typeStr)
+    return false;
+
+  RootedString idStr(cx, IdToString(cx, id));
+  JSAutoByteString idBytes;
+  const char* propStr = idBytes.encodeLatin1(cx, idStr);
+  if (!propStr)
+    return false;
+
+  JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                       CTYPESMSG_FIELD_DESC_SIZE, typeStr, propStr);
+  return false;
+}
+
+static bool
+FieldDescriptorNameTypeError(JSContext* cx, Value val)
+{
+  RootedValue typeVal(cx, val);
+  JSAutoByteString valBytes;
+  const char* valStr = CTypesToSourceForError(cx, typeVal, valBytes);
+  if (!valStr)
+    return false;
+
+  JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                       CTYPESMSG_FIELD_DESC_NAMETYPE, valStr);
+  return false;
+}
+
+static bool
+FieldDescriptorTypeError(JSContext* cx, HandleValue poroVal, HandleId id)
+{
+  JSAutoByteString typeBytes;
+  const char* typeStr = CTypesToSourceForError(cx, poroVal, typeBytes);
+  if (!typeStr)
+    return false;
+
+  RootedString idStr(cx, IdToString(cx, id));
+  JSAutoByteString idBytes;
+  const char* propStr = idBytes.encodeLatin1(cx, idStr);
+  if (!propStr)
+    return false;
+
+  JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                       CTYPESMSG_FIELD_DESC_TYPE, typeStr, propStr);
+  return false;
+}
+
+static bool
+FieldMissingError(JSContext* cx, JSObject* typeObj, JSFlatString* name_)
+{
+  JSAutoByteString typeBytes;
+  RootedString name(cx, name_);
+  RootedValue typeVal(cx, ObjectValue(*typeObj));
+  const char* typeStr = CTypesToSourceForError(cx, typeVal, typeBytes);
+  if (!typeStr)
+    return false;
+
+  JSAutoByteString nameBytes;
+  const char* nameStr = nameBytes.encodeLatin1(cx, name);
+  if (!nameStr)
+    return false;
+
+  JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                       CTYPESMSG_FIELD_MISSING, typeStr, nameStr);
+  return false;
+}
+
+static bool
 FinalizerSizeError(JSContext* cx, HandleObject funObj, HandleValue actual)
 {
   MOZ_ASSERT(CType::IsCType(funObj));
 
   JSAutoByteString valBytes;
   const char* valStr = CTypesToSourceForError(cx, actual, valBytes);
   if (!valStr)
     return false;
@@ -5603,56 +5720,56 @@ ArrayType::AddressOfElement(JSContext* c
 
 /*******************************************************************************
 ** StructType implementation
 *******************************************************************************/
 
 // For a struct field descriptor 'val' of the form { name : type }, extract
 // 'name' and 'type'.
 static JSFlatString*
-ExtractStructField(JSContext* cx, Value val, MutableHandleObject typeObj)
+ExtractStructField(JSContext* cx, HandleValue val, MutableHandleObject typeObj)
 {
   if (val.isPrimitive()) {
-    JS_ReportError(cx, "struct field descriptors require a valid name and type");
+    FieldDescriptorNameTypeError(cx, val);
     return nullptr;
   }
 
   RootedObject obj(cx, &val.toObject());
   Rooted<IdVector> props(cx, IdVector(cx));
   if (!JS_Enumerate(cx, obj, &props))
     return nullptr;
 
   // make sure we have one, and only one, property
   if (props.length() != 1) {
-    JS_ReportError(cx, "struct field descriptors must contain one property");
+    FieldDescriptorCountError(cx, val, props.length());
     return nullptr;
   }
 
   RootedId nameid(cx, props[0]);
   if (!JSID_IS_STRING(nameid)) {
-    JS_ReportError(cx, "struct field descriptors require a valid name and type");
+    FieldDescriptorNameError(cx, nameid);
     return nullptr;
   }
 
   RootedValue propVal(cx);
   if (!JS_GetPropertyById(cx, obj, nameid, &propVal))
     return nullptr;
 
   if (propVal.isPrimitive() || !CType::IsCType(&propVal.toObject())) {
-    JS_ReportError(cx, "struct field descriptors require a valid name and type");
+    FieldDescriptorTypeError(cx, propVal, nameid);
     return nullptr;
   }
 
   // Undefined size or zero size struct members are illegal.
   // (Zero-size arrays are legal as struct members in C++, but libffi will
   // choke on a zero-size struct, so we disallow them.)
   typeObj.set(&propVal.toObject());
   size_t size;
   if (!CType::GetSafeSize(typeObj, &size) || size == 0) {
-    JS_ReportError(cx, "struct field types must have defined and nonzero size");
+    FieldDescriptorSizeError(cx, typeObj, nameid);
     return nullptr;
   }
 
   return JSID_TO_FLAT_STRING(nameid);
 }
 
 // For a struct field with 'name' and 'type', add an element of the form
 // { name : type }.
@@ -5778,18 +5895,17 @@ StructType::DefineInternal(JSContext* cx
       RootedObject fieldType(cx, nullptr);
       Rooted<JSFlatString*> name(cx, ExtractStructField(cx, item, &fieldType));
       if (!name)
         return false;
 
       // Make sure each field name is unique
       FieldInfoHash::AddPtr entryPtr = fields.lookupForAdd(name);
       if (entryPtr) {
-        JS_ReportError(cx, "struct fields must have unique names");
-        return false;
+        return DuplicateFieldError(cx, name);
       }
 
       // Add the field to the StructType's 'prototype' property.
       AutoStableStringChars nameChars(cx);
       if (!nameChars.initTwoByte(cx, name))
         return false;
 
       RootedFunction getter(cx, NewFunctionWithReserved(cx, StructType::FieldGetter, 0, 0, nullptr));
@@ -6096,21 +6212,17 @@ StructType::LookupField(JSContext* cx, J
 {
   MOZ_ASSERT(CType::IsCType(obj));
   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
 
   FieldInfoHash::Ptr ptr = GetFieldInfo(obj)->lookup(name);
   if (ptr)
     return &ptr->value();
 
-  JSAutoByteString bytes(cx, name);
-  if (!bytes)
-    return nullptr;
-
-  JS_ReportError(cx, "%s does not name a field", bytes.ptr());
+  FieldMissingError(cx, obj, name);
   return nullptr;
 }
 
 JSObject*
 StructType::BuildFieldsArray(JSContext* cx, JSObject* obj)
 {
   MOZ_ASSERT(CType::IsCType(obj));
   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
--- a/js/src/ctypes/ctypes.msg
+++ b/js/src/ctypes/ctypes.msg
@@ -23,17 +23,24 @@ MSG_DEF(CTYPESMSG_TYPE_ERROR,    2, JSEX
 
 /* array */
 MSG_DEF(CTYPESMSG_ARRAY_MISMATCH,4, JSEXN_TYPEERR, "length of {0} does not match to the length of the type {1} (expected {2}, got {3})")
 MSG_DEF(CTYPESMSG_ARRAY_OVERFLOW,4, JSEXN_TYPEERR, "length of {0} does not fit in the length of the type {1} (expected {2} or lower, got {3})")
 MSG_DEF(CTYPESMSG_INVALID_INDEX, 1, JSEXN_TYPEERR, "{0} is not a valid array index")
 MSG_DEF(CTYPESMSG_INVALID_RANGE, 2, JSEXN_RANGEERR, "array index {0} is out of bounds for array of length {1}")
 
 /* struct */
+MSG_DEF(CTYPESMSG_DUPLICATE_FIELD, 1, JSEXN_TYPEERR, "struct fields must have unique names, '{0}' field appears twice")
+MSG_DEF(CTYPESMSG_FIELD_DESC_COUNT,2, JSEXN_TYPEERR, "struct field descriptors must contain one property (got {0} with {1} properties)")
+MSG_DEF(CTYPESMSG_FIELD_DESC_NAME,1, JSEXN_TYPEERR, "{0} is not a valid name of struct field descriptors")
+MSG_DEF(CTYPESMSG_FIELD_DESC_SIZE,2, JSEXN_TYPEERR, "struct field type must have defined and nonzero size (got {0} for '{1}' field)")
+MSG_DEF(CTYPESMSG_FIELD_DESC_NAMETYPE,1, JSEXN_TYPEERR, "struct field descriptors require a valid name and type (got {0})")
+MSG_DEF(CTYPESMSG_FIELD_DESC_TYPE,2, JSEXN_TYPEERR, "{0} is not a valid type of struct field descriptors for '{1}' field")
 MSG_DEF(CTYPESMSG_FIELD_MISMATCH,5, JSEXN_TYPEERR, "property count of {0} does not match to field count of the type {1} (expected {2}, got {3}){4}")
+MSG_DEF(CTYPESMSG_FIELD_MISSING, 2, JSEXN_TYPEERR, "{0} does not have a field named '{1}'")
 MSG_DEF(CTYPESMSG_PROP_NONSTRING,3, JSEXN_TYPEERR, "property name {0} of {1} is not a string{2}")
 
 /* data finalizer */
 MSG_DEF(CTYPESMSG_EMPTY_FIN,     1, JSEXN_TYPEERR, "attempting to convert an empty CDataFinalizer{0}")
 MSG_DEF(CTYPESMSG_EMPTY_FIN_CALL,1, JSEXN_TYPEERR, "{0} called on empty CDataFinalizer")
 MSG_DEF(CTYPESMSG_FIN_SIZE_ERROR,2, JSEXN_TYPEERR, "expected an object with the same size as argument 1 of {0}, got {1}")
 
 /* native function */
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ctypes/struct-field.js
@@ -0,0 +1,21 @@
+load(libdir + 'asserts.js');
+
+function test() {
+  assertTypeErrorMessage(() => { ctypes.StructType("a", [ 1 ]); },
+                          "struct field descriptors require a valid name and type (got the number 1)");
+  assertTypeErrorMessage(() => { ctypes.StructType("a", [ { x: 1, y: 2 } ]); },
+                          "struct field descriptors must contain one property (got the object ({x:1, y:2}) with 2 properties)");
+  assertTypeErrorMessage(() => { ctypes.StructType("a", [ { 1: 1 } ]); },
+                         "the number 1 is not a valid name of struct field descriptors");
+  assertTypeErrorMessage(() => { ctypes.StructType("a", [ { "x": 1 } ]); },
+                         "the number 1 is not a valid type of struct field descriptors for 'x' field");
+  assertTypeErrorMessage(() => { ctypes.StructType("a", [ { "x": ctypes.StructType("b") } ]); },
+                         "struct field type must have defined and nonzero size (got ctypes.StructType(\"b\") for 'x' field)");
+  assertTypeErrorMessage(() => { ctypes.StructType("a", [ { "x": ctypes.int32_t, }, { "x": ctypes.int32_t } ]); },
+                         "struct fields must have unique names, 'x' field appears twice");
+  assertTypeErrorMessage(() => { ctypes.StructType("a", [ { "x": ctypes.int32_t, } ])().addressOfField("z"); },
+                         "ctypes.StructType(\"a\", [{ \"x\": ctypes.int32_t }]) does not have a field named 'z'");
+}
+
+if (typeof ctypes === "object")
+  test();
--- a/toolkit/components/ctypes/tests/unit/test_jsctypes.js
+++ b/toolkit/components/ctypes/tests/unit/test_jsctypes.js
@@ -1382,35 +1382,35 @@ function run_StructType_tests() {
   do_check_throws(function() { ctypes.StructType(); }, TypeError);
   do_check_throws(function() { ctypes.StructType("a", [], 5); }, TypeError);
   do_check_throws(function() { ctypes.StructType(null, []); }, TypeError);
   do_check_throws(function() { ctypes.StructType("a", null); }, TypeError);
 
   // Check that malformed descriptors are an error.
   do_check_throws(function() {
     ctypes.StructType("a", [{"x":ctypes.int32_t}, {"x":ctypes.int8_t}]);
-  }, Error);
+  }, TypeError);
   do_check_throws(function() {
     ctypes.StructType("a", [5]);
-  }, Error);
+  }, TypeError);
   do_check_throws(function() {
     ctypes.StructType("a", [{}]);
-  }, Error);
+  }, TypeError);
   do_check_throws(function() {
     ctypes.StructType("a", [{5:ctypes.int32_t}]);
-  }, Error);
+  }, TypeError);
   do_check_throws(function() {
     ctypes.StructType("a", [{"5":ctypes.int32_t}]);
-  }, Error);
+  }, TypeError);
   do_check_throws(function() {
     ctypes.StructType("a", [{"x":5}]);
-  }, Error);
+  }, TypeError);
   do_check_throws(function() {
     ctypes.StructType("a", [{"x":ctypes.int32_t()}]);
-  }, Error);
+  }, TypeError);
 
   // Check that opaque structs work.
   let opaque_t = ctypes.StructType("a");
   do_check_eq(opaque_t.name, "a");
   do_check_eq(opaque_t.toString(), "type a");
   do_check_eq(opaque_t.toSource(), 'ctypes.StructType("a")');
   do_check_true(opaque_t.prototype === undefined);
   do_check_true(opaque_t.fields === undefined);
@@ -1428,29 +1428,29 @@ function run_StructType_tests() {
   do_check_eq(ptrValue(opaqueptr), 1);
   do_check_throws(function() {
     opaqueptr.value = ctypes.StructType("a").ptr();
   }, TypeError);
 
   // Check that 'define' works.
   do_check_throws(function() { opaque_t.define(); }, TypeError);
   do_check_throws(function() { opaque_t.define([], 0); }, TypeError);
-  do_check_throws(function() { opaque_t.define([{}]); }, Error);
-  do_check_throws(function() { opaque_t.define([{ a: 0 }]); }, Error);
+  do_check_throws(function() { opaque_t.define([{}]); }, TypeError);
+  do_check_throws(function() { opaque_t.define([{ a: 0 }]); }, TypeError);
   do_check_throws(function() {
     opaque_t.define([{ a: ctypes.int32_t, b: ctypes.int64_t }]);
-  }, Error);
+  }, TypeError);
   do_check_throws(function() {
     opaque_t.define([{ a: ctypes.int32_t }, { b: 0 }]);
-  }, Error);
+  }, TypeError);
   do_check_false(opaque_t.hasOwnProperty("prototype"));
 
   // Check that circular references work with opaque structs...
   // but not crazy ones.
-  do_check_throws(function() { opaque_t.define([{ b: opaque_t }]); }, Error);
+  do_check_throws(function() { opaque_t.define([{ b: opaque_t }]); }, TypeError);
   let circular_t = ctypes.StructType("circular", [{ a: opaqueptr_t }]);
   opaque_t.define([{ b: circular_t }]);
   let opaque = opaque_t();
   let circular = circular_t(opaque.address());
   opaque.b = circular;
   do_check_eq(circular.a.toSource(), opaque.address().toSource());
   do_check_eq(opaque.b.toSource(), circular.toSource());
 
@@ -1579,17 +1579,17 @@ function run_StructType_tests() {
   g2.a = 7;
   do_check_eq(g2.a, 7);
   do_check_eq(s.b.a, 7);
 
   g_a = s.addressOfField("b");
   do_check_true(g_a.constructor === g_t.ptr);
   do_check_eq(g_a.contents.a, s.b.a);
   do_check_throws(function() { s.addressOfField(); }, TypeError);
-  do_check_throws(function() { s.addressOfField("d"); }, Error);
+  do_check_throws(function() { s.addressOfField("d"); }, TypeError);
   do_check_throws(function() { s.addressOfField("a", 2); }, TypeError);
 
   do_check_eq(s.toSource(), "s_t(4, {\"a\": 7, \"b\": 2}, 10)");
   do_check_eq(s.toSource(), s.toString());
   eval("var s2 = " + s.toSource());
   do_check_true(s2.constructor === s_t);
   do_check_eq(s.b.b, s2.b.b);
 
@@ -1640,20 +1640,20 @@ function run_StructType_tests() {
   let z_t = ctypes.StructType("z_t", []);
   do_check_eq(z_t.size, 1);
   do_check_eq(z_t.fields.length, 0);
 
   // Check that structs containing arrays of undefined or zero length
   // are illegal, but arrays of defined length work.
   do_check_throws(function() {
     ctypes.StructType("z_t", [{ a: ctypes.int32_t.array() }]);
-  }, Error);
+  }, TypeError);
   do_check_throws(function() {
     ctypes.StructType("z_t", [{ a: ctypes.int32_t.array(0) }]);
-  }, Error);
+  }, TypeError);
   z_t = ctypes.StructType("z_t", [{ a: ctypes.int32_t.array(6) }]);
   do_check_eq(z_t.size, ctypes.int32_t.size * 6);
   let z = z_t([1, 2, 3, 4, 5, 6]);
   do_check_eq(z.a[3], 4);
 }
 
 function ptrValue(p) {
   return ctypes.cast(p, ctypes.uintptr_t).value.toString();