Bug 699156: Support TypedArrays in XPConnect. r=bholley,evilpie

This commit is contained in:
Antti Haapala 2011-12-03 09:33:20 -05:00
parent 92f0b64ffe
commit 55185ebf5e
7 changed files with 223 additions and 21 deletions

View File

@ -58,6 +58,8 @@
#include "dombindings.h"
#include "nsWrapperCacheInlines.h"
#include "jstypedarray.h"
using namespace mozilla;
//#define STRICT_CHECK_OF_UNICODE
@ -1689,13 +1691,169 @@ failure:
#undef POPULATE
}
// Check that the tag part of the type matches the type
// of the array. If the check succeeds, check that the size
// of the output does not exceed PR_UINT32_MAX bytes. Allocate
// the memory and copy the elements by memcpy.
static JSBool
CheckTargetAndPopulate(const nsXPTType& type,
PRUint8 requiredType,
size_t typeSize,
JSUint32 count,
JSObject* tArr,
void** output,
nsresult* pErr)
{
// Check that the element type expected by the interface matches
// the type of the elements in the typed array exactly, including
// signedness.
if (type.TagPart() != requiredType) {
if (pErr)
*pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
return false;
}
// Calulate the maximum number of elements that can fit in
// PR_UINT32_MAX bytes.
size_t max = PR_UINT32_MAX / typeSize;
// This could overflow on 32-bit systems so check max first.
size_t byteSize = count * typeSize;
if (count > max || !(*output = nsMemory::Alloc(byteSize))) {
if (pErr)
*pErr = NS_ERROR_OUT_OF_MEMORY;
return false;
}
memcpy(*output, JS_GetTypedArrayData(tArr), byteSize);
return true;
}
// Fast conversion of typed arrays to native using memcpy.
// No float or double canonicalization is done. Called by
// JSarray2Native whenever a TypedArray is met. ArrayBuffers
// are not accepted; create a properly typed array view on them
// first. The element type of array must match the XPCOM
// type in size, type and signedness exactly. As an exception,
// Uint8ClampedArray is allowed for arrays of uint8.
// static
JSBool
XPCConvert::JSTypedArray2Native(XPCCallContext& ccx,
void** d,
JSObject* jsArray,
JSUint32 count,
const nsXPTType& type,
nsresult* pErr)
{
NS_ABORT_IF_FALSE(jsArray, "bad param");
NS_ABORT_IF_FALSE(d, "bad param");
NS_ABORT_IF_FALSE(js_IsTypedArray(jsArray), "not a typed array");
// Check the actual length of the input array against the
// given size_is.
JSUint32 len = JS_GetTypedArrayLength(jsArray);
if (len < count) {
if (pErr)
*pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
return false;
}
void* output = nsnull;
switch (JS_GetTypedArrayType(jsArray)) {
case js::TypedArray::TYPE_INT8:
if (!CheckTargetAndPopulate(nsXPTType::T_I8, type,
sizeof(int8), count,
jsArray, &output, pErr)) {
return false;
}
break;
case js::TypedArray::TYPE_UINT8:
case js::TypedArray::TYPE_UINT8_CLAMPED:
if (!CheckTargetAndPopulate(nsXPTType::T_U8, type,
sizeof(uint8), count,
jsArray, &output, pErr)) {
return false;
}
break;
case js::TypedArray::TYPE_INT16:
if (!CheckTargetAndPopulate(nsXPTType::T_I16, type,
sizeof(int16), count,
jsArray, &output, pErr)) {
return false;
}
break;
case js::TypedArray::TYPE_UINT16:
if (!CheckTargetAndPopulate(nsXPTType::T_U16, type,
sizeof(uint16), count,
jsArray, &output, pErr)) {
return false;
}
break;
case js::TypedArray::TYPE_INT32:
if (!CheckTargetAndPopulate(nsXPTType::T_I32, type,
sizeof(int32), count,
jsArray, &output, pErr)) {
return false;
}
break;
case js::TypedArray::TYPE_UINT32:
if (!CheckTargetAndPopulate(nsXPTType::T_U32, type,
sizeof(uint32), count,
jsArray, &output, pErr)) {
return false;
}
break;
case js::TypedArray::TYPE_FLOAT32:
if (!CheckTargetAndPopulate(nsXPTType::T_FLOAT, type,
sizeof(float), count,
jsArray, &output, pErr)) {
return false;
}
break;
case js::TypedArray::TYPE_FLOAT64:
if (!CheckTargetAndPopulate(nsXPTType::T_DOUBLE, type,
sizeof(double), count,
jsArray, &output, pErr)) {
return false;
}
break;
// Yet another array type was defined? It is not supported yet...
default:
if (pErr)
*pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
return false;
}
*d = output;
if (pErr)
*pErr = NS_OK;
return true;
}
// static
JSBool
XPCConvert::JSArray2Native(XPCCallContext& ccx, void** d, jsval s,
JSUint32 count, const nsXPTType& type,
const nsID* iid, uintN* pErr)
const nsID* iid, nsresult* pErr)
{
NS_PRECONDITION(d, "bad param");
NS_ABORT_IF_FALSE(d, "bad param");
JSContext* cx = ccx.GetJSContext();
@ -1731,13 +1889,19 @@ XPCConvert::JSArray2Native(XPCCallContext& ccx, void** d, jsval s,
}
jsarray = JSVAL_TO_OBJECT(s);
// If this is a typed array, then do a fast conversion with memcpy.
if (js_IsTypedArray(jsarray)) {
return JSTypedArray2Native(ccx, d, jsarray, count, type, pErr);
}
if (!JS_IsArrayObject(cx, jsarray)) {
if (pErr)
*pErr = NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY;
return false;
}
jsuint len;
JSUint32 len;
if (!JS_GetArrayLength(cx, jsarray, &len) || len < count) {
if (pErr)
*pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
@ -1781,7 +1945,7 @@ XPCConvert::JSArray2Native(XPCCallContext& ccx, void** d, jsval s,
case nsXPTType::T_U64 : POPULATE(na, uint64); break;
case nsXPTType::T_FLOAT : POPULATE(na, float); break;
case nsXPTType::T_DOUBLE : POPULATE(na, double); break;
case nsXPTType::T_BOOL : POPULATE(na, bool); break;
case nsXPTType::T_BOOL : POPULATE(na, bool); break;
case nsXPTType::T_CHAR : POPULATE(na, char); break;
case nsXPTType::T_WCHAR : POPULATE(na, jschar); break;
case nsXPTType::T_VOID : NS_ERROR("bad type"); goto failure;
@ -1874,7 +2038,7 @@ XPCConvert::NativeStringWithSize2JS(JSContext* cx,
JSBool
XPCConvert::JSStringWithSize2Native(XPCCallContext& ccx, void* d, jsval s,
JSUint32 count, const nsXPTType& type,
uintN* pErr)
nsresult* pErr)
{
NS_PRECONDITION(!JSVAL_IS_NULL(s), "bad param");
NS_PRECONDITION(d, "bad param");

View File

@ -2434,8 +2434,7 @@ CallMethodHelper::GatherAndConvertResults()
!GetInterfaceTypeFromParam(i, datum_type, &param_iid))
return false;
uintN err;
nsresult err;
if (isArray) {
XPCLazyCallContext lccx(mCallContext);
if (!XPCConvert::NativeArray2JS(lccx, &v, (const void**)&dp->val,

View File

@ -3327,7 +3327,14 @@ public:
static JSBool JSArray2Native(XPCCallContext& ccx, void** d, jsval s,
JSUint32 count, const nsXPTType& type,
const nsID* iid, uintN* pErr);
const nsID* iid, nsresult* pErr);
static JSBool JSTypedArray2Native(XPCCallContext& ccx,
void** d,
JSObject* jsarray,
JSUint32 count,
const nsXPTType& type,
nsresult* pErr);
static JSBool NativeStringWithSize2JS(JSContext* cx,
jsval* d, const void* s,

View File

@ -97,7 +97,7 @@ TestParams.prototype = {
testACString: f,
testJsval: f,
testShortArray: f_is,
testLongLongArray: f_is,
testDoubleArray: f_is,
testStringArray: f_is,
testWstringArray: f_is,
testInterfaceArray: f_is,

View File

@ -246,14 +246,14 @@ NS_IMETHODIMP nsXPCTestParams::TestShortArray(PRUint32 aLength, PRInt16 *a,
BUFFER_METHOD_IMPL(PRInt16, 0, TAKE_OWNERSHIP_NOOP);
}
/* void testLongLongArray (in unsigned long aLength, [array, size_is (aLength)] in long long a,
* inout unsigned long bLength, [array, size_is (bLength)] inout long long b,
* out unsigned long rvLength, [array, size_is (rvLength), retval] out long long rv); */
NS_IMETHODIMP nsXPCTestParams::TestLongLongArray(PRUint32 aLength, PRInt64 *a,
PRUint32 *bLength NS_INOUTPARAM, PRInt64 **b NS_INOUTPARAM,
PRUint32 *rvLength NS_OUTPARAM, PRInt64 **rv NS_OUTPARAM)
/* void testDoubleArray (in unsigned long aLength, [array, size_is (aLength)] in double a,
* inout unsigned long bLength, [array, size_is (bLength)] inout double b,
* out unsigned long rvLength, [array, size_is (rvLength), retval] out double rv); */
NS_IMETHODIMP nsXPCTestParams::TestDoubleArray(PRUint32 aLength, double *a,
PRUint32 *bLength NS_INOUTPARAM, double **b NS_INOUTPARAM,
PRUint32 *rvLength NS_OUTPARAM, double **rv NS_OUTPARAM)
{
BUFFER_METHOD_IMPL(PRInt64, 0, TAKE_OWNERSHIP_NOOP);
BUFFER_METHOD_IMPL(double, 0, TAKE_OWNERSHIP_NOOP);
}
/* void testStringArray (in unsigned long aLength, [array, size_is (aLength)] in string a,

View File

@ -47,7 +47,7 @@
interface nsIXPCTestInterfaceA;
interface nsIXPCTestInterfaceB;
[scriptable, uuid(b94cd289-d0df-4d25-8995-facf687d921d)]
[scriptable, uuid(fe2b7433-ac3b-49ef-9344-b67228bfdd46)]
interface nsIXPCTestParams : nsISupports {
// These types correspond to the ones in typelib.py
@ -79,9 +79,9 @@ interface nsIXPCTestParams : nsISupports {
void testShortArray(in unsigned long aLength, [array, size_is(aLength)] in short a,
inout unsigned long bLength, [array, size_is(bLength)] inout short b,
out unsigned long rvLength, [retval, array, size_is(rvLength)] out short rv);
void testLongLongArray(in unsigned long aLength, [array, size_is(aLength)] in long long a,
inout unsigned long bLength, [array, size_is(bLength)] inout long long b,
out unsigned long rvLength, [retval, array, size_is(rvLength)] out long long rv);
void testDoubleArray(in unsigned long aLength, [array, size_is(aLength)] in double a,
inout unsigned long bLength, [array, size_is(bLength)] inout double b,
out unsigned long rvLength, [retval, array, size_is(rvLength)] out double rv);
void testStringArray(in unsigned long aLength, [array, size_is(aLength)] in string a,
inout unsigned long bLength, [array, size_is(bLength)] inout string b,
out unsigned long rvLength, [retval, array, size_is(rvLength)] out string rv);

View File

@ -119,6 +119,22 @@ function test_component(contractid) {
do_check_true(dotEqualsComparator(val1IID, bIID.value));
}
// Check that the given call (type mismatch) results in an exception being thrown.
function doTypedArrayMismatchTest(name, val1, val1Size, val2, val2Size) {
var comparator = arrayComparator(standardComparator);
var error = false;
try {
doIsTest(name, val1, val1Size, val2, val2Size, comparator);
// An exception was not thrown as would have been expected.
do_check_true(false);
}
catch (e) {
// An exception was thrown as expected.
do_check_true(true);
}
}
// Workaround for bug 687612 (inout parameters broken for dipper types).
// We do a simple test of copying a into b, and ignore the rv.
function doTestWorkaround(name, val1) {
@ -165,7 +181,8 @@ function test_component(contractid) {
// Test arrays.
doIsTest("testShortArray", [2, 4, 6], 3, [1, 3, 5, 7], 4, arrayComparator(standardComparator));
doIsTest("testLongLongArray", [-10000000000], 1, [1, 3, 1234511234551], 3, arrayComparator(standardComparator));
doIsTest("testDoubleArray", [-10, -0.5], 2, [1, 3, 1e11, -8e-5 ], 4, arrayComparator(fuzzComparator));
doIsTest("testStringArray", ["mary", "hat", "hey", "lid", "tell", "lam"], 6,
["ids", "fleas", "woes", "wide", "has", "know", "!"], 7, arrayComparator(standardComparator));
doIsTest("testWstringArray", ["沒有語言", "的偉大嗎?]"], 2,
@ -173,6 +190,13 @@ function test_component(contractid) {
doIsTest("testInterfaceArray", [makeA(), makeA()], 2,
[makeA(), makeA(), makeA(), makeA(), makeA(), makeA()], 6, arrayComparator(interfaceComparator));
// Test typed arrays and ArrayBuffer aliasing.
var arrayBuffer = new ArrayBuffer(16);
var int16Array = new Int16Array(arrayBuffer, 2, 3);
int16Array.set([-32768, 0, 32767]);
doIsTest("testShortArray", int16Array, 3, new Int16Array([1773, -32768, 32767, 7]), 4, arrayComparator(standardComparator));
doIsTest("testDoubleArray", new Float64Array([-10, -0.5]), 2, new Float64Array([0, 3.2, 1.0e10, -8.33 ]), 4, arrayComparator(fuzzComparator));
// Test sized strings.
var ssTests = ["Tis not possible, I muttered", "give me back my free hardcore!", "quoth the server:", "4〠4"];
doIsTest("testSizedString", ssTests[0], ssTests[0].length, ssTests[1], ssTests[1].length, standardComparator);
@ -186,4 +210,12 @@ function test_component(contractid) {
// Test arrays of iids.
doIs2Test("testInterfaceIsArray", [makeA(), makeA(), makeA(), makeA(), makeA()], 5, Ci['nsIXPCTestInterfaceA'],
[makeB(), makeB(), makeB()], 3, Ci['nsIXPCTestInterfaceB']);
// Test incorrect (too big) array size parameter; this should throw NOT_ENOUGH_ELEMENTS.
doTypedArrayMismatchTest("testShortArray", Int16Array([-3, 7, 4]), 4,
Int16Array([1, -32, 6]), 3);
// Test type mismatch (int16 <-> uint16); this should throw BAD_CONVERT_JS.
doTypedArrayMismatchTest("testShortArray", Uint16Array([0, 7, 4, 3]), 4,
Uint16Array([1, 5, 6]), 3);
}