Improve ctypes error handling per review comments.

This commit is contained in:
Dan Witte 2009-09-23 18:01:21 -07:00
parent 6b434c4683
commit 323f802805
5 changed files with 123 additions and 29 deletions

View File

@ -98,10 +98,38 @@ jsvalToDoubleStrict(jsval aValue, jsdouble *dp)
return false;
}
static nsresult
TypeError(JSContext *cx, const char *message)
JSErrorFormatString ErrorFormatString[CTYPESERR_LIMIT] = {
#define MSG_DEF(name, number, count, exception, format) \
{ format, count, exception } ,
#include "ctypes.msg"
#undef MSG_DEF
};
const JSErrorFormatString*
GetErrorMessage(void* userRef, const char* locale, const uintN errorNumber)
{
JS_ReportError(cx, "%s", message);
if (0 < errorNumber && errorNumber < CTYPESERR_LIMIT)
return &ErrorFormatString[errorNumber];
return NULL;
}
static const char*
ToSource(JSContext* cx, jsval vp)
{
JSString* str = JS_ValueToSource(cx, vp);
if (str)
return JS_GetStringBytes(str);
JS_ClearPendingException(cx);
return "<<error converting value to string>>";
}
static nsresult
TypeError(JSContext* cx, const char* expected, jsval actual)
{
const char* src = ToSource(cx, actual);
JS_ReportErrorNumber(cx, GetErrorMessage, NULL,
CTYPESMSG_TYPE_ERROR, expected, src);
return NS_ERROR_FAILURE;
}
@ -196,67 +224,67 @@ PrepareValue(JSContext* aContext, const Type& aType, jsval aValue, Value& aResul
// Programs can convert explicitly, if needed, using `Boolean(v)` or `!!v`.
if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint8) ||
aResult.mValue.mUint8 > 1)
return TypeError(aContext, "Expected boolean value");
return TypeError(aContext, "boolean", aValue);
aResult.mData = &aResult.mValue.mUint8;
break;
case nsIForeignLibrary::INT8:
// Do not implicitly lose bits.
if (!jsvalToIntStrict(aValue, &aResult.mValue.mInt8))
return TypeError(aContext, "Expected int8 value");
return TypeError(aContext, "int8", aValue);
aResult.mData = &aResult.mValue.mInt8;
break;
case nsIForeignLibrary::INT16:
// Do not implicitly lose bits.
if (!jsvalToIntStrict(aValue, &aResult.mValue.mInt16))
return TypeError(aContext, "Expected int16 value");
return TypeError(aContext, "int16", aValue);
aResult.mData = &aResult.mValue.mInt16;
break;
case nsIForeignLibrary::INT32:
// Do not implicitly lose bits.
if (!jsvalToIntStrict(aValue, &aResult.mValue.mInt32))
return TypeError(aContext, "Expected int32 value");
return TypeError(aContext, "int32", aValue);
aResult.mData = &aResult.mValue.mInt32;
case nsIForeignLibrary::INT64:
// Do not implicitly lose bits.
if (!jsvalToIntStrict(aValue, &aResult.mValue.mInt64))
return TypeError(aContext, "Expected int64 value");
return TypeError(aContext, "int64", aValue);
aResult.mData = &aResult.mValue.mInt64;
break;
case nsIForeignLibrary::UINT8:
// Do not implicitly lose bits.
if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint8))
return TypeError(aContext, "Expected uint8 value");
return TypeError(aContext, "uint8", aValue);
aResult.mData = &aResult.mValue.mUint8;
break;
case nsIForeignLibrary::UINT16:
// Do not implicitly lose bits.
if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint16))
return TypeError(aContext, "Expected uint16 value");
return TypeError(aContext, "uint16", aValue);
aResult.mData = &aResult.mValue.mUint16;
break;
case nsIForeignLibrary::UINT32:
// Do not implicitly lose bits.
if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint32))
return TypeError(aContext, "Expected uint32 value");
return TypeError(aContext, "uint32", aValue);
aResult.mData = &aResult.mValue.mUint32;
case nsIForeignLibrary::UINT64:
// Do not implicitly lose bits.
if (!jsvalToIntStrict(aValue, &aResult.mValue.mUint64))
return TypeError(aContext, "Expected uint64 value");
return TypeError(aContext, "uint64", aValue);
aResult.mData = &aResult.mValue.mUint64;
break;
case nsIForeignLibrary::FLOAT:
if (!jsvalToDoubleStrict(aValue, &d))
return TypeError(aContext, "Expected number");
return TypeError(aContext, "float", aValue);
// The following cast silently throws away some bits, but there's
// no good way around it. Sternly requiring that the 64-bit double
@ -267,7 +295,7 @@ PrepareValue(JSContext* aContext, const Type& aType, jsval aValue, Value& aResul
break;
case nsIForeignLibrary::DOUBLE:
if (!jsvalToDoubleStrict(aValue, &d))
return TypeError(aContext, "Expected number");
return TypeError(aContext, "double", aValue);
aResult.mValue.mDouble = d;
aResult.mData = &aResult.mValue.mDouble;
@ -281,7 +309,7 @@ PrepareValue(JSContext* aContext, const Type& aType, jsval aValue, Value& aResul
} else {
// Don't implicitly convert to string. Users can implicitly convert
// with `String(x)` or `""+x`.
return TypeError(aContext, "Expected string");
return TypeError(aContext, "string", aValue);
}
aResult.mData = &aResult.mValue.mPointer;
@ -295,7 +323,7 @@ PrepareValue(JSContext* aContext, const Type& aType, jsval aValue, Value& aResul
} else {
// Don't implicitly convert to string. Users can implicitly convert
// with `String(x)` or `""+x`.
return TypeError(aContext, "Expected string");
return TypeError(aContext, "ustring", aValue);
}
aResult.mData = &aResult.mValue.mPointer;
@ -488,8 +516,10 @@ Function::Init(JSContext* aContext,
NS_ENSURE_SUCCESS(rv, rv);
// disallow void argument types
if (mArgTypes[i].mType == nsIForeignLibrary::VOID)
return TypeError(aContext, "Cannot have void argument type");
if (mArgTypes[i].mType == nsIForeignLibrary::VOID) {
JS_ReportError(aContext, "Cannot have void argument type");
return NS_ERROR_INVALID_ARG;
}
// ffi_prep_cif requires an array of ffi_types; prepare it separately.
mFFITypes.AppendElement(&mArgTypes[i].mFFIType);

View File

@ -51,6 +51,18 @@
namespace mozilla {
namespace ctypes {
// for JS error reporting
enum ErrorNum {
#define MSG_DEF(name, number, count, exception, format) \
name = number,
#include "ctypes.msg"
#undef MSG_DEF
CTYPESERR_LIMIT
};
const JSErrorFormatString*
GetErrorMessage(void* userRef, const char* locale, const uintN errorNumber);
struct Type
{
ffi_type mFFIType;

View File

@ -60,7 +60,7 @@ jsvalToUint16(JSContext* aContext, jsval aVal, PRUint16& aResult)
}
}
JS_ReportError(aContext, "Parameter must be an integer");
JS_ReportError(aContext, "Parameter must be a valid ABI constant");
return NS_ERROR_INVALID_ARG;
}

52
js/ctypes/ctypes.msg Normal file
View File

@ -0,0 +1,52 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is js-ctypes.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation <http://www.mozilla.org/>.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Dan Witte <dwitte@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* This is the jsctypes error message file.
*
* For syntax details, see js/src/js.msg.
*/
MSG_DEF(CTYPESMSG_PLACEHOLDER_0, 0, 0, JSEXN_NONE, NULL)
MSG_DEF(CTYPESMSG_PLACEHOLDER_1, 1, 0, JSEXN_NONE, NULL)
MSG_DEF(CTYPESMSG_PLACEHOLDER_2, 2, 0, JSEXN_NONE, NULL)
// This error has to be number 3 in order to be a TypeError,
// due to a bug in the js engine.
MSG_DEF(CTYPESMSG_TYPE_ERROR, 3, 2, JSEXN_TYPEERR, "expected type {0}, got {1}")

View File

@ -114,7 +114,7 @@ function run_short_tests(library) {
{toString: function () { return 7; }},
{valueOf: function () { return 7; }}];
for (var i = 0; i < vals.length; i++)
do_check_throws(function () { test_s_s(vals[i]); }, Error);
do_check_throws(function () { test_s_s(vals[i]); }, TypeError);
var test_s_ss = library.declare("test_s_ss", Types.DEFAULT, Types.INT16, Types.INT16, Types.INT16);
do_check_eq(test_s_ss(5, 5), 10);
@ -123,8 +123,8 @@ function run_short_tests(library) {
// here, since it's binary-compatible.)
var test_us_us = library.declare("test_s_s", Types.DEFAULT, Types.UINT16, Types.UINT16);
do_check_eq(test_us_us(0xffff), 0xffff);
do_check_throws(function () { test_us_us(0x10000); }, Error);
do_check_throws(function () { test_us_us(-1); }, Error);
do_check_throws(function () { test_us_us(0x10000); }, TypeError);
do_check_throws(function () { test_us_us(-1); }, TypeError);
}
function run_int_tests(library) {
@ -146,7 +146,7 @@ function run_int_tests(library) {
{toString: function () { return 7; }},
{valueOf: function () { return 7; }}];
for (var i = 0; i < vals.length; i++)
do_check_throws(function () { test_i_i(vals[i]); }, Error);
do_check_throws(function () { test_i_i(vals[i]); }, TypeError);
var test_i_ii = library.declare("test_i_ii", Types.DEFAULT, Types.INT32, Types.INT32, Types.INT32);
do_check_eq(test_i_ii(5, 5), 10);
@ -155,8 +155,8 @@ function run_int_tests(library) {
// here, since it's binary-compatible.)
var test_ui_ui = library.declare("test_i_i", Types.DEFAULT, Types.UINT32, Types.UINT32);
do_check_eq(test_ui_ui(0xffffffff), 0xffffffff);
do_check_throws(function () { test_ui_ui(0x100000000); }, Error);
do_check_throws(function () { test_ui_ui(-1); }, Error);
do_check_throws(function () { test_ui_ui(0x100000000); }, TypeError);
do_check_throws(function () { test_ui_ui(-1); }, TypeError);
}
function run_float_tests(library) {
@ -179,7 +179,7 @@ function run_float_tests(library) {
{toString: function () { return 7; }},
{valueOf: function () { return 7; }}];
for (var i = 0; i < vals.length; i++)
do_check_throws(function () { test_d_d(vals[i]); }, Error);
do_check_throws(function () { test_f_f(vals[i]); }, TypeError);
var test_f_ff = library.declare("test_f_ff", Types.DEFAULT, Types.FLOAT, Types.FLOAT, Types.FLOAT);
do_check_eq(test_f_ff(5, 5), 10);
@ -203,7 +203,7 @@ function run_double_tests(library) {
{toString: function () { return 7; }},
{valueOf: function () { return 7; }}];
for (var i = 0; i < vals.length; i++)
do_check_throws(function () { test_d_d(vals[i]); }, Error);
do_check_throws(function () { test_d_d(vals[i]); }, TypeError);
var test_d_dd = library.declare("test_d_dd", Types.DEFAULT, Types.DOUBLE, Types.DOUBLE, Types.DOUBLE);
do_check_eq(test_d_dd(5, 5), 10);
@ -218,7 +218,7 @@ function run_string_tests(library) {
// don't convert anything else to a string
var vals = [true, 0, 1/3, undefined, {}, {toString: function () { return "bad"; }}, []];
for (var i = 0; i < vals.length; i++)
do_check_throws(function() { test_ansi_len(vals[i]); }, Error);
do_check_throws(function() { test_ansi_len(vals[i]); }, TypeError);
var test_wide_len = library.declare("test_wide_len", Types.DEFAULT, Types.INT32, Types.USTRING);
do_check_eq(test_wide_len("hello world"), 11);
@ -237,6 +237,6 @@ function run_string_tests(library) {
function run_mixed_tests(library) {
var test_i_if_floor = library.declare("test_i_if_floor", Types.DEFAULT, Types.INT32, Types.INT32, Types.FLOAT);
do_check_eq(test_i_if_floor(5, 5.5), 10);
do_check_throws(function() { test_i_if_floor(5.5, 5); }, Error);
do_check_throws(function() { test_i_if_floor(5.5, 5); }, TypeError);
}