From d7e724835fb72c15aa02c7803d4bba36b318d0f0 Mon Sep 17 00:00:00 2001 From: Brindusan Cristian Date: Wed, 25 Jul 2018 22:57:09 +0300 Subject: [PATCH] Backed out 15 changesets (bug 1475409, bug 1461450, bug 1474369, bug 1471726) for build bustages on xptcstubs_gcc_x86_unix.cpp:55:1. CLOSED TREE Backed out changeset 79dbf5b9d8db (bug 1474369) Backed out changeset 7e2e13953e19 (bug 1474369) Backed out changeset 9d0b9a4c4651 (bug 1474369) Backed out changeset 9f14f7b8bc3d (bug 1475409) Backed out changeset bddd838d1910 (bug 1475409) Backed out changeset 850f21ca1f45 (bug 1475409) Backed out changeset 320802ab02e3 (bug 1474369) Backed out changeset db67bf0e7f91 (bug 1474369) Backed out changeset f355d9be9912 (bug 1474369) Backed out changeset e1b6a5f74642 (bug 1474369) Backed out changeset 459635cdfc08 (bug 1474369) Backed out changeset 44414b1cd32e (bug 1461450) Backed out changeset a5d04cf5d67f (bug 1461450) Backed out changeset 40885fbf99c6 (bug 1471726) Backed out changeset da79e75b9cb3 (bug 1471726) --- accessible/xpcom/AccEventGen.py | 2 +- js/xpconnect/src/XPCConvert.cpp | 467 +++++++++--------- js/xpconnect/src/XPCInlines.h | 5 +- js/xpconnect/src/XPCWrappedJSClass.cpp | 32 +- js/xpconnect/src/XPCWrappedNative.cpp | 117 +++-- js/xpconnect/src/xpcprivate.h | 72 +-- .../tests/components/js/xpctest_params.js | 8 - .../components/native/xpctest_params.cpp | 80 --- js/xpconnect/tests/idl/xpctest_params.idl | 19 - js/xpconnect/tests/unit/test_params.js | 22 - xpcom/ds/nsIVariant.idl | 2 +- xpcom/ds/nsTArray.h | 13 - xpcom/ds/nsVariant.cpp | 2 +- xpcom/idl-parser/xpidl/jsonxpt.py | 10 +- xpcom/idl-parser/xpidl/runtests.py | 2 +- xpcom/idl-parser/xpidl/xpidl.py | 279 +++-------- xpcom/reflect/xptcall/xptcall.h | 24 +- xpcom/reflect/xptinfo/xptcodegen.py | 9 +- xpcom/reflect/xptinfo/xptinfo.h | 258 +++++----- xpcom/rust/xpcom/src/reexports.rs | 2 +- xpcom/rust/xpcom/xpcom_macros/src/lib.rs | 4 +- xpcom/tests/gtest/TestTArray2.cpp | 19 - 22 files changed, 565 insertions(+), 883 deletions(-) diff --git a/accessible/xpcom/AccEventGen.py b/accessible/xpcom/AccEventGen.py index 1bcfdd0c8ade..ac3d2a89b2be 100755 --- a/accessible/xpcom/AccEventGen.py +++ b/accessible/xpcom/AccEventGen.py @@ -197,7 +197,7 @@ def interfaces(iface): interfaces = [] while iface.base: interfaces.append(iface) - iface = iface.idl.getName(xpidl.TypeId(iface.base), iface.location) + iface = iface.idl.getName(iface.base, iface.location) interfaces.append(iface) interfaces.reverse() return interfaces diff --git a/js/xpconnect/src/XPCConvert.cpp b/js/xpconnect/src/XPCConvert.cpp index fa204f59109b..d1ba48b9d14f 100644 --- a/js/xpconnect/src/XPCConvert.cpp +++ b/js/xpconnect/src/XPCConvert.cpp @@ -58,7 +58,7 @@ XPCConvert::IsMethodReflectable(const nsXPTMethodInfo& info) // Reflected methods can't use native types. All native types end up // getting tagged as void*, so this check is easy. - if (type.Tag() == nsXPTType::T_VOID) + if (type.TagPart() == nsXPTType::T_VOID) return false; } return true; @@ -108,7 +108,7 @@ XPCConvert::NativeData2JS(MutableHandleValue d, const void* s, if (pErr) *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE; - switch (type.Tag()) { + switch (type.TagPart()) { case nsXPTType::T_I8 : d.setInt32(*static_cast(s)); return true; @@ -385,16 +385,9 @@ XPCConvert::NativeData2JS(MutableHandleValue d, const void* s, return true; } - case nsXPTType::T_LEGACY_ARRAY: - return NativeArray2JS(d, *static_cast(s), - type.ArrayElementType(), iid, arrlen, pErr); - case nsXPTType::T_ARRAY: - { - auto* array = static_cast(s); - return NativeArray2JS(d, array->Elements(), type.ArrayElementType(), - iid, array->Length(), pErr); - } + return NativeArray2JS(d, static_cast(s), + type.ArrayElementType(), iid, arrlen, pErr); default: NS_ERROR("bad type"); @@ -455,7 +448,7 @@ XPCConvert::JSData2Native(void* d, HandleValue s, bool sizeis = type.Tag() == TD_PSTRING_SIZE_IS || type.Tag() == TD_PWSTRING_SIZE_IS; - switch (type.Tag()) { + switch (type.TagPart()) { case nsXPTType::T_I8 : return ConvertToPrimitive(cx, s, static_cast(d)); case nsXPTType::T_I16 : @@ -841,70 +834,9 @@ XPCConvert::JSData2Native(void* d, HandleValue s, return ok; } - case nsXPTType::T_LEGACY_ARRAY: - { - void** dest = (void**)d; - const nsXPTType& elty = type.ArrayElementType(); - - *dest = nullptr; - - // FIXME: XPConnect historically has shortcut the JSArray2Native codepath in - // its caller if arrlen is 0, allowing arbitrary values to be passed as - // arrays and interpreted as the empty array (bug 1458987). - // - // NOTE: Once this is fixed, null/undefined should be allowed for arrays if - // arrlen is 0. - if (arrlen == 0) { - return true; - } - - bool ok = JSArray2Native(s, elty, iid, pErr, [&] (uint32_t* aLength) -> void* { - // Check that we have enough elements in our array. - if (*aLength < arrlen) { - if (pErr) - *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY; - return nullptr; - } - *aLength = arrlen; - - // Allocate the backing buffer & return it. - *dest = moz_xmalloc(*aLength * elty.Stride()); - if (!*dest) { - if (pErr) - *pErr = NS_ERROR_OUT_OF_MEMORY; - return nullptr; - } - return *dest; - }); - - if (!ok && *dest) { - // An error occurred, free any allocated backing buffer. - free(*dest); - *dest = nullptr; - } - return ok; - } - case nsXPTType::T_ARRAY: - { - auto* dest = (xpt::detail::UntypedTArray*)d; - const nsXPTType& elty = type.ArrayElementType(); - - bool ok = JSArray2Native(s, elty, iid, pErr, [&] (uint32_t* aLength) -> void* { - if (!dest->SetLength(elty, *aLength)) { - if (pErr) - *pErr = NS_ERROR_OUT_OF_MEMORY; - return nullptr; - } - return dest->Elements(); - }); - - if (!ok) { - // An error occurred, free any allocated backing buffer. - dest->Clear(); - } - return ok; - } + return JSArray2Native((void**)d, s, arrlen, + type.ArrayElementType(), iid, pErr); default: NS_ERROR("bad type"); @@ -1400,16 +1332,39 @@ XPCConvert::JSValToXPCException(MutableHandleValue s, // array fun... +static bool +ValidArrayType(const nsXPTType& type) +{ + switch (type.Tag()) { + // These types aren't allowed to be in arrays. + case nsXPTType::T_VOID: + case nsXPTType::T_DOMSTRING: + case nsXPTType::T_UTF8STRING: + case nsXPTType::T_CSTRING: + case nsXPTType::T_ASTRING: + case nsXPTType::T_PSTRING_SIZE_IS: + case nsXPTType::T_PWSTRING_SIZE_IS: + case nsXPTType::T_ARRAY: + return false; + default: + return true; + } +} + // static bool -XPCConvert::NativeArray2JS(MutableHandleValue d, const void* buf, +XPCConvert::NativeArray2JS(MutableHandleValue d, const void* const* s, const nsXPTType& type, const nsID* iid, uint32_t count, nsresult* pErr) { - MOZ_ASSERT(buf, "bad param"); + MOZ_ASSERT(s, "bad param"); AutoJSContext cx; + // XXX add support for putting chars in a string rather than an array + + // XXX add support to indicate *which* array element was not convertable + RootedObject array(cx, JS_NewArrayObject(cx, count)); if (!array) return false; @@ -1417,9 +1372,12 @@ XPCConvert::NativeArray2JS(MutableHandleValue d, const void* buf, if (pErr) *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE; + if (!ValidArrayType(type)) + return false; + RootedValue current(cx, JS::NullValue()); for (uint32_t i = 0; i < count; ++i) { - if (!NativeData2JS(¤t, type.ElementPtr(buf, i), type, iid, 0, pErr) || + if (!NativeData2JS(¤t, type.ElementPtr(*s, i), type, iid, 0, pErr) || !JS_DefineElement(cx, array, i, current, JSPROP_ENUMERATE)) return false; } @@ -1430,123 +1388,223 @@ XPCConvert::NativeArray2JS(MutableHandleValue d, const void* buf, 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_t. DataViews +// are not supported. + // static bool -XPCConvert::JSArray2Native(JS::HandleValue aJSVal, - const nsXPTType& aEltType, - const nsIID* aIID, - nsresult* pErr, - ArrayAllocFixupLen aAllocFixupLen) +XPCConvert::JSTypedArray2Native(void** d, + JSObject* jsArray, + uint32_t count, + const nsXPTType& type, + nsresult* pErr) { - // Wrap aAllocFixupLen to check length is within bounds & initialize the - // allocated memory if needed. - auto allocFixupLen = [&] (uint32_t* aLength) -> void* { - if (*aLength > (UINT32_MAX / aEltType.Stride())) { - return nullptr; // Byte length doesn't fit in uint32_t - } + MOZ_ASSERT(jsArray, "bad param"); + MOZ_ASSERT(d, "bad param"); + MOZ_ASSERT(JS_IsTypedArrayObject(jsArray), "not a typed array"); - void* buf = aAllocFixupLen(aLength); + // Check the actual length of the input array against the + // given size_is. + uint32_t len = JS_GetTypedArrayLength(jsArray); + if (len < count) { + if (pErr) + *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY; - // Ensure the buffer has valid values for each element. We can skip this - // for arithmetic types, as they do not require initialization. - if (buf && !aEltType.IsArithmetic()) { - for (uint32_t i = 0; i < *aLength; ++i) { - InitializeValue(aEltType, aEltType.ElementPtr(buf, i)); - } - } - return buf; - }; + return false; + } + + uint8_t expected; + switch (JS_GetArrayBufferViewType(jsArray)) { + case js::Scalar::Int8: + expected = nsXPTType::T_I8; + break; + + case js::Scalar::Uint8: + case js::Scalar::Uint8Clamped: + expected = nsXPTType::T_U8; + break; + + case js::Scalar::Int16: + expected = nsXPTType::T_I16; + break; + + case js::Scalar::Uint16: + expected = nsXPTType::T_U16; + break; + + case js::Scalar::Int32: + expected = nsXPTType::T_I32; + break; + + case js::Scalar::Uint32: + expected = nsXPTType::T_U32; + break; + + case js::Scalar::Float32: + expected = nsXPTType::T_FLOAT; + break; + + case js::Scalar::Float64: + expected = nsXPTType::T_DOUBLE; + break; + + // Yet another array type was defined? It is not supported yet... + default: + if (pErr) + *pErr = NS_ERROR_XPC_BAD_CONVERT_JS; + return false; + } + + // Check that the type we got is the type we expected. + if (expected != type.Tag()) { + if (pErr) + *pErr = NS_ERROR_XPC_BAD_CONVERT_JS; + return false; + } + + // Check if the size would overflow uint32_t. + if (count > (UINT32_MAX / type.Stride())) { + if (pErr) + *pErr = NS_ERROR_OUT_OF_MEMORY; + return false; + } + + // Get the backing memory buffer to copy out of. + JS::AutoCheckCannotGC nogc; + bool isShared; + void* buf = JS_GetArrayBufferViewData(jsArray, &isShared, nogc); + + // Require opting in to shared memory - a future project. + if (isShared) { + if (pErr) + *pErr = NS_ERROR_XPC_BAD_CONVERT_JS; + return false; + } + + // Allocate the buffer, and copy. + *d = moz_xmalloc(count * type.Stride()); + if (!*d) { + if (pErr) + *pErr = NS_ERROR_OUT_OF_MEMORY; + return false; + } + + memcpy(*d, buf, count * type.Stride()); + + if (pErr) + *pErr = NS_OK; + + return true; +} + +// static +bool +XPCConvert::JSArray2Native(void** d, HandleValue s, + uint32_t count, const nsXPTType& type, + const nsID* iid, nsresult* pErr) +{ + MOZ_ASSERT(d, "bad param"); AutoJSContext cx; - // JSArray2Native only accepts objects (Array and TypedArray). - if (!aJSVal.isObject()) { - if (pErr) - *pErr = NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY; - return false; + // FIXME: XPConnect historically has shortcut the JSArray2Native codepath in + // its caller if count is 0, allowing arbitrary values to be passed as + // arrays and interpreted as the empty array (bug 1458987). + // + // NOTE: Once this is fixed, null/undefined should be allowed for arrays if + // count is 0. + if (count == 0) { + *d = nullptr; + return true; } - RootedObject jsarray(cx, &aJSVal.toObject()); + + // XXX add support for getting chars from strings + + // XXX add support to indicate *which* array element was not convertable if (pErr) *pErr = NS_ERROR_XPC_BAD_CONVERT_JS; - if (JS_IsTypedArrayObject(jsarray)) { - // Fast conversion of typed arrays to native using memcpy. No float or - // double canonicalization is done. 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_t. - // DataViews are not supported. + if (!ValidArrayType(type)) + return false; - nsXPTTypeTag tag; - switch (JS_GetArrayBufferViewType(jsarray)) { - case js::Scalar::Int8: tag = TD_INT8; break; - case js::Scalar::Uint8: tag = TD_UINT8; break; - case js::Scalar::Uint8Clamped: tag = TD_UINT8; break; - case js::Scalar::Int16: tag = TD_INT16; break; - case js::Scalar::Uint16: tag = TD_UINT16; break; - case js::Scalar::Int32: tag = TD_INT32; break; - case js::Scalar::Uint32: tag = TD_UINT32; break; - case js::Scalar::Float32: tag = TD_FLOAT; break; - case js::Scalar::Float64: tag = TD_DOUBLE; break; - default: return false; - } - if (aEltType.Tag() != tag) { - return false; - } - - // Get the backing memory buffer to copy out of. - JS::AutoCheckCannotGC nogc; - bool isShared = false; - const void* data = JS_GetArrayBufferViewData(jsarray, &isShared, nogc); - - // Require opting in to shared memory - a future project. - if (isShared) { - return false; - } - - uint32_t length = JS_GetTypedArrayLength(jsarray); - void* buf = allocFixupLen(&length); - if (!buf) { - return false; - } - - // Directly copy data into the allocated target buffer. - memcpy(buf, data, length * aEltType.Stride()); - return true; + if (s.isNullOrUndefined()) { + if (pErr) + *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY; + return false; } - // If jsarray is not a TypedArrayObject, check for an Array object. - uint32_t length = 0; - bool isArray = false; - if (!JS_IsArrayObject(cx, jsarray, &isArray) || !isArray || - !JS_GetArrayLength(cx, jsarray, &length)) { + if (!s.isObject()) { + if (pErr) + *pErr = NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY; + return false; + } + + RootedObject jsarray(cx, &s.toObject()); + + // If this is a typed array, then try a fast conversion with memcpy. + if (JS_IsTypedArrayObject(jsarray)) { + return JSTypedArray2Native(d, jsarray, count, type, pErr); + } + + bool isArray; + if (!JS_IsArrayObject(cx, jsarray, &isArray) || !isArray) { if (pErr) *pErr = NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY; return false; } - void* buf = allocFixupLen(&length); - if (!buf) { + uint32_t len; + if (!JS_GetArrayLength(cx, jsarray, &len) || len < count) { + if (pErr) + *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY; return false; } - // Translate each array element separately. - RootedValue current(cx); - for (uint32_t i = 0; i < length; ++i) { - if (!JS_GetElement(cx, jsarray, i, ¤t) || - !JSData2Native(aEltType.ElementPtr(buf, i), current, - aEltType, aIID, 0, pErr)) { - // Array element conversion failed. Clean up all elements converted - // before the error. Caller handles freeing 'buf'. - for (uint32_t j = 0; j < i; ++j) { - CleanupValue(aEltType, aEltType.ElementPtr(buf, j)); - } - return false; - } + if (count > (UINT32_MAX / type.Stride())) { + if (pErr) + *pErr = NS_ERROR_OUT_OF_MEMORY; + return false; } - return true; + // Allocate the destination array, and begin converting elements. + *d = moz_xmalloc(count * type.Stride()); + if (!*d) { + if (pErr) + *pErr = NS_ERROR_OUT_OF_MEMORY; + return false; + } + + RootedValue current(cx); + uint32_t initedCount; + for (initedCount = 0; initedCount < count; ++initedCount) { + if (!JS_GetElement(cx, jsarray, initedCount, ¤t) || + !JSData2Native(type.ElementPtr(*d, initedCount), + current, type, iid, 0, pErr)) + break; + } + + // Check if we handled every array element. + if (initedCount == count) { + if (pErr) + *pErr = NS_OK; + return true; + } + + // Something failed! Clean up after ourselves. + for (uint32_t i = 0; i < initedCount; ++i) { + CleanupValue(type, type.ElementPtr(*d, i)); + } + free(*d); + *d = nullptr; + return false; } /***************************************************************************/ @@ -1561,7 +1619,7 @@ xpc::InnerCleanupValue(const nsXPTType& aType, void* aValue, uint32_t aArrayLen) MOZ_ASSERT(aArrayLen == 0 || aType.Tag() == nsXPTType::T_PSTRING_SIZE_IS || aType.Tag() == nsXPTType::T_PWSTRING_SIZE_IS || - aType.Tag() == nsXPTType::T_LEGACY_ARRAY, + aType.Tag() == nsXPTType::T_ARRAY, "Array lengths may only appear for certain types!"); switch (aType.Tag()) { @@ -1598,8 +1656,8 @@ xpc::InnerCleanupValue(const nsXPTType& aType, void* aValue, uint32_t aArrayLen) free(*(void**)aValue); break; - // Legacy Array Type - case nsXPTType::T_LEGACY_ARRAY: + // Array Types + case nsXPTType::T_ARRAY: { const nsXPTType& elty = aType.ArrayElementType(); void* elements = *(void**)aValue; @@ -1610,69 +1668,10 @@ xpc::InnerCleanupValue(const nsXPTType& aType, void* aValue, uint32_t aArrayLen) free(elements); break; } - - // Array Type - case nsXPTType::T_ARRAY: - { - const nsXPTType& elty = aType.ArrayElementType(); - auto* array = (xpt::detail::UntypedTArray*)aValue; - - for (uint32_t i = 0; i < array->Length(); ++i) { - CleanupValue(elty, elty.ElementPtr(array->Elements(), i)); - } - array->Clear(); - break; - } - - // Clear the JS::Value to `undefined` - case nsXPTType::T_JSVAL: - ((JS::Value*)aValue)->setUndefined(); - break; - - // Non-arithmetic types requiring no cleanup - case nsXPTType::T_VOID: - break; - - default: - MOZ_CRASH("Unknown Type!"); } - // Clear any non-complex values to the valid '0' state. - if (!aType.IsComplex()) { - aType.ZeroValue(aValue); - } -} - -/***************************************************************************/ - -// Implementation of xpc::InitializeValue. - -void -xpc::InitializeValue(const nsXPTType& aType, void* aValue) -{ - switch (aType.Tag()) { - // Types which require custom, specific initialization. - case nsXPTType::T_JSVAL: - new (aValue) JS::Value(); - MOZ_ASSERT(reinterpret_cast(aValue)->isUndefined()); - break; - - case nsXPTType::T_ASTRING: - case nsXPTType::T_DOMSTRING: - new (aValue) nsString(); - break; - case nsXPTType::T_CSTRING: - case nsXPTType::T_UTF8STRING: - new (aValue) nsCString(); - break; - - case nsXPTType::T_ARRAY: - new (aValue) xpt::detail::UntypedTArray(); - break; - - // The remaining types all have valid states where all bytes are '0'. - default: - aType.ZeroValue(aValue); - break; + // Null out the pointer if we have it. + if (aType.HasPointerRepr()) { + *(void**)aValue = nullptr; } } diff --git a/js/xpconnect/src/XPCInlines.h b/js/xpconnect/src/XPCInlines.h index a30d341e523f..18fee45400d9 100644 --- a/js/xpconnect/src/XPCInlines.h +++ b/js/xpconnect/src/XPCInlines.h @@ -530,10 +530,7 @@ xpc::CleanupValue(const nsXPTType& aType, // Check if we can do a cheap early return, and only perform the inner call // if we can't. We never have to clean up null pointer types or arithmetic // types. - // - // NOTE: We can skip zeroing arithmetic types in CleanupValue, as they are - // already in a valid state. - if (aType.IsArithmetic() || (aType.IsPointer() && !*(void**)aValue)) { + if (aType.IsArithmetic() || (aType.HasPointerRepr() && !*(void**)aValue)) { return; } xpc::InnerCleanupValue(aType, aValue, aArrayLen); diff --git a/js/xpconnect/src/XPCWrappedJSClass.cpp b/js/xpconnect/src/XPCWrappedJSClass.cpp index 03ace25c39ad..fb09b3da3b43 100644 --- a/js/xpconnect/src/XPCWrappedJSClass.cpp +++ b/js/xpconnect/src/XPCWrappedJSClass.cpp @@ -668,7 +668,7 @@ nsXPCWrappedJSClass::GetArraySizeFromParam(const nsXPTMethodInfo* method, nsXPTCMiniVariant* nativeParams, uint32_t* result) const { - if (type.Tag() != nsXPTType::T_LEGACY_ARRAY && + if (type.Tag() != nsXPTType::T_ARRAY && type.Tag() != nsXPTType::T_PSTRING_SIZE_IS && type.Tag() != nsXPTType::T_PWSTRING_SIZE_IS) { *result = 0; @@ -745,27 +745,25 @@ nsXPCWrappedJSClass::CleanupOutparams(const nsXPTMethodInfo* info, if (!param.IsOut()) continue; + // Extract the array length so we can use it in CleanupValue. + uint32_t arrayLen = 0; + if (!GetArraySizeFromParam(info, param.Type(), nativeParams, &arrayLen)) + continue; + MOZ_ASSERT(param.IsIndirect(), "Outparams are always indirect"); - // Call 'CleanupValue' on parameters which we know to be initialized: - // 1. Complex parameters (initialized by caller) - // 2. 'inout' parameters (initialized by caller) - // 3. 'out' parameters when 'inOutOnly' is 'false' (initialized by us) - // - // We skip non-complex 'out' parameters before the call, as they may - // contain random junk. - if (param.Type().IsComplex() || param.IsIn() || !inOutOnly) { - uint32_t arrayLen = 0; - if (!GetArraySizeFromParam(info, param.Type(), nativeParams, &arrayLen)) - continue; - + // The inOutOnly flag is necessary because full outparams may contain + // uninitialized junk before the call is made, and we don't want to try + // to clean up uninitialized junk. + if (!inOutOnly || param.IsIn()) { xpc::CleanupValue(param.Type(), nativeParams[i].val.p, arrayLen); } - // Ensure our parameters are in a clean state. Complex values are always - // handled by CleanupValue, and others have a valid null representation. - if (!param.Type().IsComplex()) { - param.Type().ZeroValue(nativeParams[i].val.p); + // Even if we didn't call CleanupValue, null out any pointers. This is + // just to protect C++ callers which may read garbage if they forget to + // check the error value. + if (param.Type().HasPointerRepr()) { + *(void**)nativeParams[i].val.p = nullptr; } } } diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index e9f85d3ef562..8c43cdc1b801 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -1245,6 +1245,10 @@ CallMethodHelper::Call() CallMethodHelper::~CallMethodHelper() { for (nsXPTCVariant& param : mDispatchParams) { + // Only clean up values which need cleanup. + if (!param.DoesValNeedCleanup()) + continue; + uint32_t arraylen = 0; if (!GetArraySizeFromParam(param.type, UndefinedHandleValue, &arraylen)) continue; @@ -1258,7 +1262,7 @@ CallMethodHelper::GetArraySizeFromParam(const nsXPTType& type, HandleValue maybeArray, uint32_t* result) { - if (type.Tag() != nsXPTType::T_LEGACY_ARRAY && + if (type.Tag() != nsXPTType::T_ARRAY && type.Tag() != nsXPTType::T_PSTRING_SIZE_IS && type.Tag() != nsXPTType::T_PWSTRING_SIZE_IS) { *result = 0; @@ -1473,39 +1477,25 @@ CallMethodHelper::InitializeDispatchParams() mJSContextIndex = mMethodInfo->IndexOfJSContext(); - // Allocate enough space in mDispatchParams up-front. - if (!mDispatchParams.AppendElements(paramCount + wantsJSContext + wantsOptArgc)) { - Throw(NS_ERROR_OUT_OF_MEMORY, mCallContext); - return false; + // iterate through the params to clear flags (for safe cleanup later) + for (uint8_t i = 0; i < paramCount + wantsJSContext + wantsOptArgc; i++) { + nsXPTCVariant* dp = mDispatchParams.AppendElement(); + dp->ClearFlags(); + dp->val.p = nullptr; } - // Initialize each parameter to a valid state (for safe cleanup later). - for (uint8_t i = 0, paramIdx = 0; i < mDispatchParams.Length(); i++) { - nsXPTCVariant& dp = mDispatchParams[i]; + // Fill in the JSContext argument + if (wantsJSContext) { + nsXPTCVariant* dp = &mDispatchParams[mJSContextIndex]; + dp->type = nsXPTType::T_VOID; + dp->val.p = mCallContext; + } - if (i == mJSContextIndex) { - // Fill in the JSContext argument - dp.type = nsXPTType::T_VOID; - dp.val.p = mCallContext; - } else if (i == mOptArgcIndex) { - // Fill in the optional_argc argument - dp.type = nsXPTType::T_U8; - dp.val.u8 = std::min(mArgc, paramCount) - requiredArgs; - } else { - // Initialize normal arguments. - const nsXPTParamInfo& param = mMethodInfo->Param(paramIdx); - dp.type = param.Type(); - xpc::InitializeValue(dp.type, &dp.val); - - // Specify the correct storage/calling semantics. This will also set - // the `ptr` field to be self-referential. - if (param.IsIndirect()) { - dp.SetIndirect(); - } - - // Advance to the next normal parameter. - paramIdx++; - } + // Fill in the optional_argc argument + if (wantsOptArgc) { + nsXPTCVariant* dp = &mDispatchParams[mOptArgcIndex]; + dp->type = nsXPTType::T_U8; + dp->val.u8 = std::min(mArgc, paramCount) - requiredArgs; } return true; @@ -1532,8 +1522,40 @@ bool CallMethodHelper::ConvertIndependentParam(uint8_t i) { const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i); - const nsXPTType& type = paramInfo.Type(); + const nsXPTType& type = paramInfo.GetType(); nsXPTCVariant* dp = GetDispatchParam(i); + dp->type = type; + MOZ_ASSERT(!paramInfo.IsShared(), "[shared] implies [noscript]!"); + + // Specify the correct storage/calling semantics. + if (paramInfo.IsIndirect()) + dp->SetIndirect(); + + // Some types are always stored within the nsXPTCVariant, and passed + // indirectly, regardless of in/out-ness. These types are stored in the + // nsXPTCVariant's extended value. + switch (type.Tag()) { + // Ensure that the jsval has a valid value. + case nsXPTType::T_JSVAL: + new (&dp->ext.jsval) JS::Value(); + MOZ_ASSERT(dp->ext.jsval.isUndefined()); + break; + + // Initialize our temporary string class values so they can be assigned + // to by the XPCConvert logic. + case nsXPTType::T_ASTRING: + case nsXPTType::T_DOMSTRING: + new (&dp->ext.nsstr) nsString(); + break; + case nsXPTType::T_CSTRING: + case nsXPTType::T_UTF8STRING: + new (&dp->ext.nscstr) nsCString(); + break; + } + + // Flag cleanup for anything that isn't self-contained. + if (!type.IsArithmetic()) + dp->SetValNeedsCleanup(); // Even if there's nothing to convert, we still need to examine the // JSObject container for out-params. If it's null or otherwise invalid, @@ -1619,8 +1641,18 @@ bool CallMethodHelper::ConvertDependentParam(uint8_t i) { const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i); - const nsXPTType& type = paramInfo.Type(); + const nsXPTType& type = paramInfo.GetType(); + nsXPTCVariant* dp = GetDispatchParam(i); + dp->type = type; + + // Specify the correct storage/calling semantics. + if (paramInfo.IsIndirect()) + dp->SetIndirect(); + + // Make sure we clean up all of our dependent types. All of them require + // allocations of some kind. + dp->SetValNeedsCleanup(); // Even if there's nothing to convert, we still need to examine the // JSObject container for out-params. If it's null or otherwise invalid, @@ -1680,18 +1712,14 @@ TraceParam(JSTracer* aTrc, void* aVal, const nsXPTType& aType, if (aType.Tag() == nsXPTType::T_JSVAL) { JS::UnsafeTraceRoot(aTrc, (JS::Value*)aVal, "XPCWrappedNative::CallMethod param"); - } else if (aType.Tag() == nsXPTType::T_ARRAY) { - auto* array = (xpt::detail::UntypedTArray*)aVal; + } else if (aType.Tag() == nsXPTType::T_ARRAY && *(void**)aVal) { const nsXPTType& elty = aType.ArrayElementType(); - - for (uint32_t i = 0; i < array->Length(); ++i) { - TraceParam(aTrc, elty.ElementPtr(array->Elements(), i), elty); + if (elty.Tag() != nsXPTType::T_JSVAL) { + return; } - } else if (aType.Tag() == nsXPTType::T_LEGACY_ARRAY && *(void**)aVal) { - const nsXPTType& elty = aType.ArrayElementType(); for (uint32_t i = 0; i < aArrayLen; ++i) { - TraceParam(aTrc, elty.ElementPtr(*(void**)aVal, i), elty); + TraceParam(aTrc, elty.ElementPtr(aVal, i), elty); } } } @@ -1701,9 +1729,10 @@ CallMethodHelper::trace(JSTracer* aTrc) { // We need to note each of our initialized parameters which contain jsvals. for (nsXPTCVariant& param : mDispatchParams) { - // We only need to trace parameters which have an innermost JSVAL. - if (param.type.InnermostType().Tag() != nsXPTType::T_JSVAL) { - return; + if (!param.DoesValNeedCleanup()) { + MOZ_ASSERT(param.type.Tag() != nsXPTType::T_JSVAL, + "JSVals are marked as needing cleanup (even though they don't)"); + continue; } uint32_t arrayLen = 0; diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 1c951e769c9b..a20898bdd9e8 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -1963,6 +1963,31 @@ public: // for the WN case. You probably want UnwrapReflectorToISupports. static bool GetISupportsFromJSObject(JSObject* obj, nsISupports** iface); + /** + * Convert a native array into a JS::Value. + * + * @param d [out] the resulting JS::Value + * @param s the native array we're working with + * @param type the type of objects in the array + * @param iid the interface of each object in the array that we want + * @param count the number of items in the array + * @param scope the default scope to put on the new JSObjects' parent chain + * @param pErr [out] relevant error code, if any. + */ + static bool NativeArray2JS(JS::MutableHandleValue d, const void* const* s, + const nsXPTType& type, const nsID* iid, + uint32_t count, nsresult* pErr); + + static bool JSArray2Native(void** d, JS::HandleValue s, + uint32_t count, const nsXPTType& type, + const nsID* iid, nsresult* pErr); + + static bool JSTypedArray2Native(void** d, + JSObject* jsarray, + uint32_t count, + const nsXPTType& type, + nsresult* pErr); + static nsresult JSValToXPCException(JS::MutableHandleValue s, const char* ifaceName, const char* methodName, @@ -1977,40 +2002,6 @@ public: JS::Value* jsExceptionPtr); private: - /** - * Convert a native array into a JS::Value. - * - * @param d [out] the resulting JS::Value - * @param buf the native buffer containing input values - * @param type the type of objects in the array - * @param iid the interface of each object in the array that we want - * @param count the number of items in the array - * @param scope the default scope to put on the new JSObjects' parent chain - * @param pErr [out] relevant error code, if any. - */ - static bool NativeArray2JS(JS::MutableHandleValue d, const void* buf, - const nsXPTType& type, const nsID* iid, - uint32_t count, nsresult* pErr); - - typedef std::function ArrayAllocFixupLen; - - /** - * Convert a JS::Value into a native array. - * - * @param aJSVal the JS::Value to convert - * @param aEltType the type of objects in the array - * @param aIID the interface of each object in the array - * @param pErr [out] relevant error code, if any - * @param aAllocFixupLen function called with the JS Array's length to - * allocate the backing buffer. This function may - * modify the length of array to be converted. - */ - static bool JSArray2Native(JS::HandleValue aJSVal, - const nsXPTType& aEltType, - const nsIID* aIID, - nsresult* pErr, - ArrayAllocFixupLen aAllocFixupLen); - XPCConvert() = delete; }; @@ -3050,7 +3041,7 @@ nsIPrincipal* GetObjectPrincipal(JSObject* obj); // value : char[16_t]** (free) // TD_INTERFACE_TYPE, TD_INTERFACE_IS_TYPE // value : nsISupports** (release) -// TD_LEGACY_ARRAY (NOTE: aArrayLen should be passed) +// TD_ARRAY (NOTE: aArrayLen should be passed) // value : void** (cleanup elements & free) // TD_DOMOBJECT // value : T** (cleanup) @@ -3071,17 +3062,6 @@ void InnerCleanupValue(const nsXPTType& aType, void* aValue, uint32_t aArrayLen); -// In order to be able to safely call CleanupValue on a generated value, the -// data behind it needs to be initialized to a safe value. This method handles -// initializing the backing data to a safe value to use as an argument to -// XPCConvert methods, or xpc::CleanupValue. -// -// The pointer `aValue` must point to a block of memory at least aType.Stride() -// bytes large, and correctly aligned. -// -// This method accepts the same types as xpc::CleanupValue. -void InitializeValue(const nsXPTType& aType, void* aValue); - } // namespace xpc namespace mozilla { diff --git a/js/xpconnect/tests/components/js/xpctest_params.js b/js/xpconnect/tests/components/js/xpctest_params.js index 1233317a57f6..f8eaa2083858 100644 --- a/js/xpconnect/tests/components/js/xpctest_params.js +++ b/js/xpconnect/tests/components/js/xpctest_params.js @@ -64,19 +64,11 @@ TestParams.prototype = { testAUTF8String: f, testACString: f, testJsval: f, - testShortSequence: f, - testDoubleSequence: f, - testAStringSequence: f, - testACStringSequence: f, - testInterfaceSequence: f, - testJsvalSequence: f, - testInterfaceIsSequence: f_is, testShortArray: f_is, testDoubleArray: f_is, testStringArray: f_is, testWstringArray: f_is, testInterfaceArray: f_is, - testJsvalArray: f_is, testSizedString: f_is, testSizedWstring: f_is, testInterfaceIs: f_is, diff --git a/js/xpconnect/tests/components/native/xpctest_params.cpp b/js/xpconnect/tests/components/native/xpctest_params.cpp index 50de1cef3878..e88746d89375 100644 --- a/js/xpconnect/tests/components/native/xpctest_params.cpp +++ b/js/xpconnect/tests/components/native/xpctest_params.cpp @@ -28,14 +28,6 @@ nsXPCTestParams::~nsXPCTestParams() return NS_OK; \ } -#define SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP) { \ - _retval.SwapElements(b); \ - b = a; \ - for (uint32_t i = 0; i < b.Length(); ++i) \ - TAKE_OWNERSHIP(b[i]); \ - return NS_OK; \ -} - #define TAKE_OWNERSHIP_NOOP(val) {} #define TAKE_OWNERSHIP_INTERFACE(val) {static_cast(val)->AddRef();} #define TAKE_OWNERSHIP_STRING(val) { \ @@ -231,13 +223,6 @@ NS_IMETHODIMP nsXPCTestParams::TestInterfaceArray(uint32_t aLength, nsIXPCTestIn BUFFER_METHOD_IMPL(nsIXPCTestInterfaceA*, 0, TAKE_OWNERSHIP_INTERFACE); } -NS_IMETHODIMP nsXPCTestParams::TestJsvalArray(uint32_t aLength, JS::Value *a, - uint32_t* bLength, JS::Value **b, - uint32_t* rvLength, JS::Value **rv) -{ - BUFFER_METHOD_IMPL(JS::Value, 0, TAKE_OWNERSHIP_NOOP); -} - NS_IMETHODIMP nsXPCTestParams::TestSizedString(uint32_t aLength, const char * a, uint32_t* bLength, char * *b, uint32_t* rvLength, char * *rv) @@ -321,68 +306,3 @@ NS_IMETHODIMP nsXPCTestParams::TestStringArrayOptionalSize(const char * *a, uint return NS_OK; } - -NS_IMETHODIMP -nsXPCTestParams::TestShortSequence(const nsTArray& a, nsTArray& b, nsTArray& _retval) -{ - SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP); -} - -NS_IMETHODIMP -nsXPCTestParams::TestDoubleSequence(const nsTArray& a, nsTArray& b, nsTArray& _retval) -{ - SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP); -} - -NS_IMETHODIMP -nsXPCTestParams::TestInterfaceSequence(const nsTArray>& a, - nsTArray>& b, - nsTArray>& _retval) -{ - SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP); -} - -NS_IMETHODIMP -nsXPCTestParams::TestAStringSequence(const nsTArray& a, - nsTArray& b, - nsTArray& _retval) -{ - SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP); -} - -NS_IMETHODIMP -nsXPCTestParams::TestACStringSequence(const nsTArray& a, - nsTArray& b, - nsTArray& _retval) -{ - SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP); -} - -NS_IMETHODIMP -nsXPCTestParams::TestJsvalSequence(const nsTArray& a, - nsTArray& b, - nsTArray& _retval) -{ - SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP); -} - -NS_IMETHODIMP -nsXPCTestParams::TestSequenceSequence(const nsTArray>& a, - nsTArray>& b, - nsTArray>& _retval) -{ - SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP); -} - -NS_IMETHODIMP -nsXPCTestParams::TestInterfaceIsSequence(const nsIID* aIID, const nsTArray& a, - nsIID** bIID, nsTArray& b, - nsIID** rvIID, nsTArray& _retval) -{ - // Shuffle around our nsIIDs - *rvIID = (*bIID)->Clone(); - *bIID = aIID->Clone(); - - // Perform the generic sequence shuffle. - SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_INTERFACE); -} diff --git a/js/xpconnect/tests/idl/xpctest_params.idl b/js/xpconnect/tests/idl/xpctest_params.idl index 8fda26fffcba..f324a4595a4b 100644 --- a/js/xpconnect/tests/idl/xpctest_params.idl +++ b/js/xpconnect/tests/idl/xpctest_params.idl @@ -39,19 +39,6 @@ interface nsIXPCTestParams : nsISupports { ACString testACString(in ACString a, inout ACString b); jsval testJsval(in jsval a, inout jsval b); - // Test various forms of the Array type. - Array testShortSequence(in Array a, inout Array b); - Array testDoubleSequence(in Array a, inout Array b); - Array testInterfaceSequence(in Array a, inout Array b); - Array testAStringSequence(in Array a, inout Array b); - Array testACStringSequence(in Array a, inout Array b); - Array testJsvalSequence(in Array a, inout Array b); - Array > testSequenceSequence(in Array > a, inout Array > b); - - void testInterfaceIsSequence(in nsIIDPtr aIID, [iid_is(aIID)] in Array a, - inout nsIIDPtr bIID, [iid_is(bIID)] inout Array b, - out nsIIDPtr rvIID, [retval, iid_is(rvIID)] out Array rv); - // // Dependent parameters use the same types as above, but are handled much differently. // @@ -95,12 +82,6 @@ interface nsIXPCTestParams : nsISupports { out unsigned long rvLength, out nsIIDPtr rvIID, [retval, array, size_is(rvLength), iid_is(rvIID)] out nsQIResult rv); - // Test arrays of jsvals - void testJsvalArray(in unsigned long aLength, [array, size_is(aLength)] in jsval a, - inout unsigned long bLength, [array, size_is(bLength)] inout jsval b, - out unsigned long rvLength, [retval, array, size_is(rvLength)] out jsval rv); - - // Test for out dipper parameters void testOutAString(out AString o); diff --git a/js/xpconnect/tests/unit/test_params.js b/js/xpconnect/tests/unit/test_params.js index e41d755616a6..2058c3d1f0b3 100644 --- a/js/xpconnect/tests/unit/test_params.js +++ b/js/xpconnect/tests/unit/test_params.js @@ -163,8 +163,6 @@ function test_component(contractid) { ["we", "are", "being", "sooo", "international", "right", "now"], 7, arrayComparator(standardComparator)); doIsTest("testInterfaceArray", [makeA(), makeA()], 2, [makeA(), makeA(), makeA(), makeA(), makeA(), makeA()], 6, arrayComparator(interfaceComparator)); - doIsTest("testJsvalArray", [{ cheese: 'whiz', apple: 8 }, [1, 5, '3'], /regex/], 3, - ['apple', 2.2e10, 3.3e30, { only: "wheedle", except: {} }], 4, arrayComparator(standardComparator)); // Test typed arrays and ArrayBuffer aliasing. var arrayBuffer = new ArrayBuffer(16); @@ -197,24 +195,4 @@ function test_component(contractid) { // Test type mismatch (int16 <-> uint16); this should throw BAD_CONVERT_JS. doTypedArrayMismatchTest("testShortArray", new Uint16Array([0, 7, 4, 3]), 4, new Uint16Array([1, 5, 6]), 3); - - // Test Sequence types. - doTest("testShortSequence", [2, 4, 6], [1, 3, 5, 7], arrayComparator(standardComparator)); - doTest("testDoubleSequence", [-10, -0.5], [1, 3, 1e11, -8e-5 ], arrayComparator(fuzzComparator)); - doTest("testACStringSequence", ["mary", "hat", "hey", "lid", "tell", "lam"], - ["ids", "fleas", "woes", "wide", "has", "know", "!"], - arrayComparator(standardComparator)); - doTest("testAStringSequence", ["沒有語言", "的偉大嗎?]"], - ["we", "are", "being", "sooo", "international", "right", "now"], - arrayComparator(standardComparator)); - - doTest("testInterfaceSequence", [makeA(), makeA()], - [makeA(), makeA(), makeA(), makeA(), makeA(), makeA()], arrayComparator(interfaceComparator)); - - doTest("testJsvalSequence", [{ cheese: 'whiz', apple: 8 }, [1, 5, '3'], /regex/], - ['apple', 2.2e10, 3.3e30, { only: "wheedle", except: {} }], arrayComparator(standardComparator)); - - doIsTest("testInterfaceIsSequence", [makeA(), makeA(), makeA(), makeA(), makeA()], Ci['nsIXPCTestInterfaceA'], - [makeB(), makeB(), makeB()], Ci['nsIXPCTestInterfaceB'], - arrayComparator(interfaceComparator), dotEqualsComparator); } diff --git a/xpcom/ds/nsIVariant.idl b/xpcom/ds/nsIVariant.idl index 296ed5dc7195..6d7647e65284 100644 --- a/xpcom/ds/nsIVariant.idl +++ b/xpcom/ds/nsIVariant.idl @@ -34,7 +34,7 @@ interface nsIDataType : nsISupports const uint16_t VTYPE_WCHAR_STR = 17; // TD_PWSTRING = 17, const uint16_t VTYPE_INTERFACE = 18; // TD_INTERFACE_TYPE = 18, const uint16_t VTYPE_INTERFACE_IS = 19; // TD_INTERFACE_IS_TYPE = 19, - const uint16_t VTYPE_ARRAY = 20; // TD_LEGACY_ARRAY = 20, + const uint16_t VTYPE_ARRAY = 20; // TD_ARRAY = 20, const uint16_t VTYPE_STRING_SIZE_IS = 21; // TD_PSTRING_SIZE_IS = 21, const uint16_t VTYPE_WSTRING_SIZE_IS = 22; // TD_PWSTRING_SIZE_IS = 22, const uint16_t VTYPE_UTF8STRING = 23; // TD_UTF8STRING = 23, diff --git a/xpcom/ds/nsTArray.h b/xpcom/ds/nsTArray.h index cd79e548f94b..06c6af9c4190 100644 --- a/xpcom/ds/nsTArray.h +++ b/xpcom/ds/nsTArray.h @@ -2535,13 +2535,6 @@ public: this->AppendElements(aOther); } - AutoTArray(self_type&& aOther) - : nsTArray() - { - Init(); - this->SwapElements(aOther); - } - explicit AutoTArray(const base_type& aOther) : mAlign() { @@ -2576,12 +2569,6 @@ public: return *this; } - self_type& operator=(self_type&& aOther) - { - base_type::operator=(std::move(aOther)); - return *this; - } - template self_type& operator=(const nsTArray_Impl& aOther) { diff --git a/xpcom/ds/nsVariant.cpp b/xpcom/ds/nsVariant.cpp index 48f28668776d..c82cb46b5142 100644 --- a/xpcom/ds/nsVariant.cpp +++ b/xpcom/ds/nsVariant.cpp @@ -1672,7 +1672,7 @@ nsVariantBase::nsVariantBase() {nsIDataType::VTYPE_WCHAR_STR , TD_PWSTRING }, {nsIDataType::VTYPE_INTERFACE , TD_INTERFACE_TYPE }, {nsIDataType::VTYPE_INTERFACE_IS , TD_INTERFACE_IS_TYPE}, - {nsIDataType::VTYPE_ARRAY , TD_LEGACY_ARRAY }, + {nsIDataType::VTYPE_ARRAY , TD_ARRAY }, {nsIDataType::VTYPE_STRING_SIZE_IS , TD_PSTRING_SIZE_IS }, {nsIDataType::VTYPE_WSTRING_SIZE_IS , TD_PWSTRING_SIZE_IS }, {nsIDataType::VTYPE_UTF8STRING , TD_UTF8STRING }, diff --git a/xpcom/idl-parser/xpidl/jsonxpt.py b/xpcom/idl-parser/xpidl/jsonxpt.py index ded2c234a9e8..42e2cffdd873 100644 --- a/xpcom/idl-parser/xpidl/jsonxpt.py +++ b/xpcom/idl-parser/xpidl/jsonxpt.py @@ -63,18 +63,10 @@ def get_type(type, calltype, iid_is=None, size_is=None): return ret if isinstance(type, xpidl.Array): - # NB: For a Array we pass down the iid_is to get the type of T. + # NB: For an Array we pass down the iid_is to get the type of T. # This allows Arrays of InterfaceIs types to work. return { 'tag': 'TD_ARRAY', - 'element': get_type(type.type, calltype, iid_is), - } - - if isinstance(type, xpidl.LegacyArray): - # NB: For a Legacy [array] T we pass down iid_is to get the type of T. - # This allows [array] of InterfaceIs types to work. - return { - 'tag': 'TD_LEGACY_ARRAY', 'size_is': size_is, 'element': get_type(type.type, calltype, iid_is), } diff --git a/xpcom/idl-parser/xpidl/runtests.py b/xpcom/idl-parser/xpidl/runtests.py index 04607511d7ab..9d2d9c9df3c0 100644 --- a/xpcom/idl-parser/xpidl/runtests.py +++ b/xpcom/idl-parser/xpidl/runtests.py @@ -81,7 +81,7 @@ long bar(in long a, in float b, [array] in long c); self.assertEqual("in", m.params[1].paramtype) self.assertEqual("long", m.params[2].type) self.assertEqual("in", m.params[2].paramtype) - self.assertTrue(isinstance(m.params[2].realtype, xpidl.LegacyArray)) + self.assertTrue(isinstance(m.params[2].realtype, xpidl.Array)) self.assertEqual("long", m.params[2].realtype.type.name) def testAttribute(self): diff --git a/xpcom/idl-parser/xpidl/xpidl.py b/xpcom/idl-parser/xpidl/xpidl.py index 9ed6de01b817..6939537214b8 100755 --- a/xpcom/idl-parser/xpidl/xpidl.py +++ b/xpcom/idl-parser/xpidl/xpidl.py @@ -12,7 +12,6 @@ import os.path import re from ply import lex from ply import yacc -from collections import namedtuple """A type conforms to the following pattern: @@ -21,7 +20,7 @@ from collections import namedtuple def nativeType(self, calltype): 'returns a string representation of the native type - calltype must be 'in', 'out', 'inout', or 'element' + calltype must be 'in', 'out', or 'inout' Interface members const/method/attribute conform to the following pattern: @@ -132,9 +131,6 @@ class Builtin(object): return self.nativename.endswith('*') def nativeType(self, calltype, shared=False, const=False): - if self.name in ["string", "wstring"] and calltype == 'element': - raise IDLError("Use string class types for string Array elements", self.location) - if const: print >>sys.stderr, IDLError( "[const] doesn't make sense on builtin types.", self.location, warning=True) @@ -148,7 +144,7 @@ class Builtin(object): else: const = '' return "%s%s %s" % (const, self.nativename, - '*' if 'out' in calltype else '') + calltype != 'in' and '*' or '') def rustType(self, calltype, shared=False, const=False): # We want to rewrite any *mut pointers to *const pointers if constness @@ -325,7 +321,6 @@ class Include(object): class IDL(object): def __init__(self, productions): - self.hasSequence = False self.productions = productions self.deps = [] @@ -333,19 +328,10 @@ class IDL(object): self.namemap.set(object) def getName(self, id, location): - if id.name == 'Array': - if id.params is None or len(id.params) != 1: - raise IDLError("Array takes exactly 1 parameter", location) - self.hasSequence = True - return Array(self.getName(id.params[0], location), location) - - if id.params is not None: - raise IDLError("Generic type '%s' unrecognized" % id.name, location) - try: - return self.namemap[id.name] + return self.namemap[id] except KeyError: - raise IDLError("type '%s' not found" % id.name, location) + raise IDLError("type '%s' not found" % id, location) def hasName(self, id): return id in self.namemap @@ -368,8 +354,6 @@ class IDL(object): for p in self.productions: if p.kind == 'include': yield p - if self.hasSequence: - yield Include("nsTArray.h", BuiltinLocation) def needsJSTypes(self): for p in self.productions: @@ -412,14 +396,12 @@ class Typedef(object): parent.setName(self) self.realtype = parent.getName(self.type, self.location) - if not isinstance(self.realtype, (Builtin, Native, Typedef)): - raise IDLError("Unsupported typedef target type", self.location) - def isScriptable(self): return self.realtype.isScriptable() def nativeType(self, calltype): - return "%s %s" % (self.name, '*' if 'out' in calltype else '') + return "%s %s" % (self.name, + calltype != 'in' and '*' or '') def rustType(self, calltype): return "%s%s" % (calltype != 'in' and '*mut ' or '', @@ -458,9 +440,8 @@ class Forward(object): return True def nativeType(self, calltype): - if calltype == 'element': - return 'RefPtr<%s>' % self.name - return "%s *%s" % (self.name, '*' if 'out' in calltype else '') + return "%s %s" % (self.name, + calltype != 'in' and '* *' or '*') def rustType(self, calltype): if rustBlacklistedForward(self.name): @@ -478,19 +459,29 @@ class Native(object): modifier = None specialtype = None - # A tuple type here means that a custom value is used for each calltype: - # (in, out/inout, array element) respectively. - # A `None` here means that the written type should be used as-is. specialtypes = { 'nsid': None, - 'domstring': ('const nsAString&', 'nsAString&', 'nsString'), - 'utf8string': ('const nsACString&', 'nsACString&', 'nsCString'), - 'cstring': ('const nsACString&', 'nsACString&', 'nsCString'), - 'astring': ('const nsAString&', 'nsAString&', 'nsString'), - 'jsval': ('JS::HandleValue', 'JS::MutableHandleValue', 'JS::Value'), + 'domstring': 'nsAString', + 'utf8string': 'nsACString', + 'cstring': 'nsACString', + 'astring': 'nsAString', + 'jsval': 'JS::Value', 'promise': '::mozilla::dom::Promise', } + # Mappings from C++ native name types to rust native names. Types which + # aren't listed here are incompatible with rust code. + rust_nativenames = { + 'void': "libc::c_void", + 'char': "u8", + 'char16_t': "u16", + 'nsID': "nsID", + 'nsIID': "nsIID", + 'nsCID': "nsCID", + 'nsAString': "::nsstring::nsAString", + 'nsACString': "::nsstring::nsACString", + } + def __init__(self, name, nativename, attlist, location): self.name = name self.nativename = nativename @@ -542,75 +533,47 @@ class Native(object): def nativeType(self, calltype, const=False, shared=False): if shared: if calltype != 'out': - raise IDLError("[shared] only applies to out parameters.", self.location) + raise IDLError("[shared] only applies to out parameters.") const = True - if isinstance(self.nativename, tuple): - if calltype == 'in': - return self.nativename[0] + ' ' - elif 'out' in calltype: - return self.nativename[1] + ' ' - else: - return self.nativename[2] + ' ' - - # 'in' nsid parameters should be made 'const' - if self.specialtype == 'nsid' and calltype == 'in': + if self.specialtype not in [None, 'promise'] and calltype == 'in': const = True - if calltype == 'element': - if self.isRef(calltype): - raise IDLError("[ref] qualified type unsupported in Array", self.location) - - # Promises should be held in RefPtr in Arrays - if self.specialtype == 'promise': - return 'RefPtr' - - # We don't support nsIDPtr, in Array currently, although - # this or support for Array will be needed to replace - # [array] completely. - if self.specialtype == 'nsid': - raise IDLError("Array not yet supported. " - "File an XPConnect bug if you need it.", self.location) + if self.specialtype == 'jsval': + if calltype == 'out' or calltype == 'inout': + return "JS::MutableHandleValue " + return "JS::HandleValue " if self.isRef(calltype): - m = '& ' # [ref] is always passed with a single indirection + m = '& ' + elif self.isPtr(calltype): + m = '*' + ((self.modifier == 'ptr' and calltype != 'in') and '*' or '') else: - m = '* ' if 'out' in calltype else '' - if self.isPtr(calltype): - m += '* ' + m = calltype != 'in' and '*' or '' return "%s%s %s" % (const and 'const ' or '', self.nativename, m) def rustType(self, calltype, const=False, shared=False): - # For the most part, 'native' types don't make sense in rust, as they - # are native C++ types. However, we can support a few types here, as - # they're important. - # - # NOTE: This code doesn't try to perfectly match C++ constness, as - # constness doesn't affect ABI, and raw pointers are already unsafe. + if shared: + if calltype != 'out': + raise IDLError("[shared] only applies to out parameters.") + const = True - if self.modifier not in ['ptr', 'ref']: - raise RustNoncompat('Rust only supports [ref] / [ptr] native types') + if self.specialtype is not None and calltype == 'in': + const = True - prefix = '*mut ' if 'out' in calltype else '*const ' - if 'out' in calltype and self.modifier == 'ptr': - prefix += '*mut ' + if self.nativename not in self.rust_nativenames: + raise RustNoncompat("native type %s is unsupported" % self.nativename) + name = self.rust_nativenames[self.nativename] - if self.specialtype == 'nsid': - return prefix + self.nativename - if self.specialtype in ['cstring', 'utf8string']: - if 'element' in calltype: - return '::nsstring::nsCString' - return prefix + '::nsstring::nsACString' - if self.specialtype in ['astring', 'domstring']: - if 'element' in calltype: - return '::nsstring::nsString' - return prefix + '::nsstring::nsAString' - if self.nativename == 'void': - return prefix + 'libc::c_void' - - if self.specialtype: - raise RustNoncompat("specialtype %s unsupported" % self.specialtype) - raise RustNoncompat("native type %s unsupported" % self.nativename) + if self.isRef(calltype): + m = const and '&' or '&mut ' + elif self.isPtr(calltype): + m = (const and '*const ' or '*mut ') + if self.modifier == 'ptr' and calltype != 'in': + m += '*mut ' + else: + m = calltype != 'in' and '*mut ' or '' + return "%s%s" % (m, name) def __str__(self): return "native %s(%s)\n" % (self.name, self.nativename) @@ -650,13 +613,11 @@ class WebIDL(object): return True # All DOM objects are script exposed. def nativeType(self, calltype): - if calltype == 'element': - return 'RefPtr<%s>' % self.native - return "%s *%s" % (self.native, '*' if 'out' in calltype else '') + return "%s %s" % (self.native, calltype != 'in' and '* *' or '*') def rustType(self, calltype): # Just expose the type as a void* - we can't do any better. - return "%s*const libc::c_void" % ('*mut ' if 'out' in calltype else '') + return "%s*const libc::c_void" % (calltype != 'in' and '*mut ' or '') def __str__(self): return "webidl %s\n" % self.name @@ -701,7 +662,7 @@ class Interface(object): if hasattr(member, 'doccomments'): member.doccomments[0:0] = self.doccomments break - self.doccomments = parent.getName(TypeId(self.name), None).doccomments + self.doccomments = parent.getName(self.name, None).doccomments if self.attributes.function: has_method = False @@ -716,7 +677,7 @@ class Interface(object): parent.setName(self) if self.base is not None: - realbase = parent.getName(TypeId(self.base), self.location) + realbase = parent.getName(self.base, self.location) if realbase.kind != 'interface': raise IDLError("interface '%s' inherits from non-interface type '%s'" % (self.name, self.base), self.location) @@ -752,13 +713,12 @@ class Interface(object): return True def nativeType(self, calltype, const=False): - if calltype == 'element': - return 'RefPtr<%s>' % self.name - return "%s%s *%s" % ('const ' if const else '', self.name, - '*' if 'out' in calltype else '') + return "%s%s %s" % (const and 'const ' or '', + self.name, + calltype != 'in' and '* *' or '*') - def rustType(self, calltype, const=False): - return "%s*const %s" % ('*mut ' if 'out' in calltype else '', + def rustType(self, calltype): + return "%s*const %s" % (calltype != 'in' and '*mut ' or '', self.name) def __str__(self): @@ -777,9 +737,9 @@ class Interface(object): # The constant may be in a base class iface = self while name not in iface.namemap and iface is not None: - iface = self.idl.getName(TypeId(self.base), self.location) + iface = self.idl.getName(self.base, self.location) if iface is None: - raise IDLError("cannot find symbol '%s'" % name, self.location) + raise IDLError("cannot find symbol '%s'" % name) c = iface.namemap.get(name, location) if c.kind != 'const': raise IDLError("symbol '%s' is not a constant", c.location) @@ -788,7 +748,7 @@ class Interface(object): def needsJSTypes(self): for m in self.members: - if m.kind == "attribute" and m.type == TypeId("jsval"): + if m.kind == "attribute" and m.type == "jsval": return True if m.kind == "method" and m.needsJSTypes(): return True @@ -798,7 +758,7 @@ class Interface(object): ''' Returns the number of entries in the vtable for this interface. ''' total = sum(member.count() for member in self.members) if self.base is not None: - realbase = self.idl.getName(TypeId(self.base), self.location) + realbase = self.idl.getName(self.base, self.location) total += realbase.countEntries() return total @@ -1124,7 +1084,7 @@ class Method(object): def needsJSTypes(self): if self.implicit_jscontext: return True - if self.type == TypeId("jsval"): + if self.type == "jsval": return True for p in self.params: t = p.realtype @@ -1206,7 +1166,7 @@ class Param(object): def resolve(self, method): self.realtype = method.iface.idl.getName(self.type, self.location) if self.array: - self.realtype = LegacyArray(self.realtype) + self.realtype = Array(self.realtype) if (self.null is not None and getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'): raise IDLError("'Null' attribute can only be used on DOMString", @@ -1251,80 +1211,20 @@ class Param(object): self.name) -class LegacyArray(object): +class Array(object): def __init__(self, basetype): self.type = basetype - self.location = self.type.location def isScriptable(self): return self.type.isScriptable() def nativeType(self, calltype, const=False): - if 'element' in calltype: - raise IDLError("nested [array] unsupported", self.location) - - # For legacy reasons, we have to add a 'const ' to builtin pointer array - # types. (`[array] in string` and `[array] in wstring` parameters) - if calltype == 'in' and isinstance(self.type, Builtin) and self.type.isPointer(): - const = True - - return "%s%s*%s" % ('const ' if const else '', - self.type.nativeType('legacyelement'), - '*' if 'out' in calltype else '') + return "%s%s*" % (const and 'const ' or '', + self.type.nativeType(calltype)) def rustType(self, calltype, const=False): - return "%s%s%s" % ('*mut ' if 'out' in calltype else '', - '*const ' if const else '*mut ', - self.type.rustType('legacyelement')) - - -class Array(object): - kind = 'array' - - def __init__(self, type, location): - self.type = type - self.location = location - - @property - def name(self): - return "Array<%s>" % self.type.name - - def resolve(self, idl): - idl.getName(self.type, self.location) - - def isScriptable(self): - return self.type.isScriptable() - - def nativeType(self, calltype): - if calltype == 'legacyelement': - raise IDLError("[array] Array is unsupported", self.location) - - base = 'nsTArray<%s>' % self.type.nativeType('element') - if 'out' in calltype: - return '%s& ' % base - elif 'in' == calltype: - return 'const %s& ' % base - else: - return base - - def rustType(self, calltype): - # NOTE: To add Rust support, ensure 'element' is handled correctly in - # all rustType callees. - raise RustNoncompat("Array<...> types") - - -TypeId = namedtuple('TypeId', 'name params') - - -# Make str(TypeId) produce a nicer value -TypeId.__str__ = lambda self: \ - "%s<%s>" % (self.name, ', '.join(str(p) for p in self.params)) \ - if self.params is not None \ - else self.name - - -# Allow skipping 'params' in TypeId(..) -TypeId.__new__.__defaults__ = (None,) + return "%s %s" % (const and '*const' or '*mut', + self.type.rustType(calltype)) class IDLParser(object): @@ -1367,7 +1267,7 @@ class IDLParser(object): t_LSHIFT = r'<<' t_RSHIFT = r'>>' - literals = '"(){}[]<>,;:=|+-*' + literals = '"(){}[],;:=|+-*' t_ignore = ' \t' @@ -1460,7 +1360,7 @@ class IDLParser(object): p[0].insert(0, p[1]) def p_typedef(self, p): - """typedef : TYPEDEF type IDENTIFIER ';'""" + """typedef : TYPEDEF IDENTIFIER IDENTIFIER ';'""" p[0] = Typedef(type=p[2], name=p[3], location=self.getLocation(p, 1), @@ -1573,7 +1473,7 @@ class IDLParser(object): p[0] = CDATA(p[1], self.getLocation(p, 1)) def p_member_const(self, p): - """member : CONST type IDENTIFIER '=' number ';' """ + """member : CONST IDENTIFIER IDENTIFIER '=' number ';' """ p[0] = ConstMember(type=p[2], name=p[3], value=p[5], location=self.getLocation(p, 1), doccomments=p.slice[1].doccomments) @@ -1635,7 +1535,7 @@ class IDLParser(object): p[0] = lambda i: n1(i) | n2(i) def p_member_att(self, p): - """member : attributes optreadonly ATTRIBUTE type IDENTIFIER ';'""" + """member : attributes optreadonly ATTRIBUTE IDENTIFIER IDENTIFIER ';'""" if 'doccomments' in p[1]: doccomments = p[1]['doccomments'] elif p[2] is not None: @@ -1651,7 +1551,7 @@ class IDLParser(object): doccomments=doccomments) def p_member_method(self, p): - """member : attributes type IDENTIFIER '(' paramlist ')' raises ';'""" + """member : attributes IDENTIFIER IDENTIFIER '(' paramlist ')' raises ';'""" if 'doccomments' in p[1]: doccomments = p[1]['doccomments'] else: @@ -1684,7 +1584,7 @@ class IDLParser(object): p[0].insert(0, p[2]) def p_param(self, p): - """param : attributes paramtype type IDENTIFIER""" + """param : attributes paramtype IDENTIFIER IDENTIFIER""" p[0] = Param(paramtype=p[2], type=p[3], name=p[4], @@ -1722,25 +1622,6 @@ class IDLParser(object): p[0] = list(p[3]) p[0].insert(0, p[1]) - def p_type_id(self, p): - """type : IDENTIFIER""" - p[0] = TypeId(name=p[1]) - p.slice[0].doccomments = p.slice[1].doccomments - - def p_type_generic(self, p): - """type : IDENTIFIER '<' typelist '>'""" - p[0] = TypeId(name=p[1], params=p[3]) - p.slice[0].doccomments = p.slice[1].doccomments - - def p_typelist(self, p): - """typelist : type""" - p[0] = [p[1]] - - def p_typelist_continue(self, p): - """typelist : type ',' typelist""" - p[0] = list(p[3]) - p[0].insert(0, p[1]) - def p_error(self, t): if not t: raise IDLError( diff --git a/xpcom/reflect/xptcall/xptcall.h b/xpcom/reflect/xptcall/xptcall.h index e206c920a0e6..2085f3e9443d 100644 --- a/xpcom/reflect/xptcall/xptcall.h +++ b/xpcom/reflect/xptcall/xptcall.h @@ -45,6 +45,8 @@ static_assert(offsetof(nsXPTCMiniVariant, val) == 0, struct nsXPTCVariant { +// No ctors or dtors so that we can use arrays of these on the stack with no +// penalty. union ExtendedVal { // ExtendedVal is an extension on nsXPTCMiniVariant. It contains types @@ -58,7 +60,6 @@ struct nsXPTCVariant nsCString nscstr; nsString nsstr; JS::Value jsval; - xpt::detail::UntypedTArray array; // This type contains non-standard-layout types, so needs an explicit // Ctor/Dtor - we'll just delete them. @@ -78,12 +79,6 @@ struct nsXPTCVariant nsXPTType type; uint8_t flags; - // Clear to a valid, null state. - nsXPTCVariant() { - memset(this, 0, sizeof(nsXPTCVariant)); - type = nsXPTType::T_VOID; - } - enum { // @@ -93,12 +88,22 @@ struct nsXPTCVariant // Indicates that we &val.p should be passed n the stack, i.e. that // val should be passed by reference. IS_INDIRECT = 0x1, + + // Indicates that the value we hold requires some sort of cleanup (memory + // deallocation, interface release, JS::Value unrooting, etc). The precise + // cleanup that is performed depends on the 'type' field above. + // If the value is an array, this flag specifies whether the elements + // within the array require cleanup (we always clean up the array itself, + // so this flag would be redundant for that purpose). + VAL_NEEDS_CLEANUP = 0x2 }; void ClearFlags() {flags = 0;} void SetIndirect() {flags |= IS_INDIRECT;} + void SetValNeedsCleanup() {flags |= VAL_NEEDS_CLEANUP;} bool IsIndirect() const {return 0 != (flags & IS_INDIRECT);} + bool DoesValNeedCleanup() const {return 0 != (flags & VAL_NEEDS_CLEANUP);} // Implicitly convert to nsXPTCMiniVariant. operator nsXPTCMiniVariant&() { @@ -108,8 +113,9 @@ struct nsXPTCVariant return *(const nsXPTCMiniVariant*) &val; } - // As this type contains an anonymous union, we need to provide an explicit - // destructor. + // As this type contains an anonymous union, we need to provide explicit + // constructors & destructors. + nsXPTCVariant() { } ~nsXPTCVariant() { } }; diff --git a/xpcom/reflect/xptinfo/xptcodegen.py b/xpcom/reflect/xptinfo/xptcodegen.py index 4712726b8736..68ee9df37a7c 100644 --- a/xpcom/reflect/xptinfo/xptcodegen.py +++ b/xpcom/reflect/xptinfo/xptcodegen.py @@ -265,7 +265,7 @@ def link_to_cpp(interfaces, fd): def describe_type(type): # Create the type's documentation comment. tag = type['tag'][3:].lower() - if tag == 'legacy_array': + if tag == 'array': return '%s[size_is=%d]' % ( describe_type(type['element']), type['size_is']) elif tag == 'interface_type' or tag == 'domobject': @@ -280,15 +280,10 @@ def link_to_cpp(interfaces, fd): tag = type['tag'] d1 = d2 = 0 - if tag == 'TD_LEGACY_ARRAY': + if tag == 'TD_ARRAY': d1 = type['size_is'] d2 = lower_extra_type(type['element']) - elif tag == 'TD_ARRAY': - # NOTE: TD_ARRAY can hold 16 bits of type index, while - # TD_LEGACY_ARRAY can only hold 8. - d1, d2 = splitint(lower_extra_type(type['element'])) - elif tag == 'TD_INTERFACE_TYPE': d1, d2 = splitint(interface_idx(type['name'])) diff --git a/xpcom/reflect/xptinfo/xptinfo.h b/xpcom/reflect/xptinfo/xptinfo.h index 564112819bc9..296a7e788262 100644 --- a/xpcom/reflect/xptinfo/xptinfo.h +++ b/xpcom/reflect/xptinfo/xptinfo.h @@ -18,7 +18,6 @@ #include "mozilla/Assertions.h" #include "js/Value.h" #include "nsString.h" -#include "nsTArray.h" // Forward Declarations namespace mozilla { @@ -153,14 +152,6 @@ static_assert(sizeof(nsXPTInterfaceInfo) == 28, "wrong size?"); */ enum nsXPTTypeTag : uint8_t { - // Arithmetic (POD) Types - // - Do not require cleanup, - // - All bit patterns are valid, - // - Outparams may be uninitialized by caller, - // - Directly supported in xptcall. - // - // NOTE: The name 'Arithmetic' comes from Harbison/Steele. Despite being a tad - // unclear, it is used frequently in xptcall, so is unlikely to be changed. TD_INT8 = 0, TD_INT16 = 1, TD_INT32 = 2, @@ -174,42 +165,24 @@ enum nsXPTTypeTag : uint8_t TD_BOOL = 10, TD_CHAR = 11, TD_WCHAR = 12, - _TD_LAST_ARITHMETIC = TD_WCHAR, - - // Pointer Types - // - Require cleanup unless NULL, - // - All-zeros (NULL) bit pattern is valid, - // - Outparams may be uninitialized by caller, - // - Supported in xptcall as raw pointer. TD_VOID = 13, TD_PNSIID = 14, - TD_PSTRING = 15, - TD_PWSTRING = 16, - TD_INTERFACE_TYPE = 17, - TD_INTERFACE_IS_TYPE = 18, - TD_LEGACY_ARRAY = 19, - TD_PSTRING_SIZE_IS = 20, - TD_PWSTRING_SIZE_IS = 21, - TD_DOMOBJECT = 22, - TD_PROMISE = 23, - _TD_LAST_POINTER = TD_PROMISE, - - // Complex Types - // - Require cleanup, - // - Always passed indirectly, - // - Outparams must be initialized by caller, - // - Supported in xptcall due to indirection. - TD_DOMSTRING = 24, - TD_UTF8STRING = 25, - TD_CSTRING = 26, - TD_ASTRING = 27, - TD_JSVAL = 28, - TD_ARRAY = 29, - _TD_LAST_COMPLEX = TD_ARRAY + TD_DOMSTRING = 15, + TD_PSTRING = 16, + TD_PWSTRING = 17, + TD_INTERFACE_TYPE = 18, + TD_INTERFACE_IS_TYPE = 19, + TD_ARRAY = 20, + TD_PSTRING_SIZE_IS = 21, + TD_PWSTRING_SIZE_IS = 22, + TD_UTF8STRING = 23, + TD_CSTRING = 24, + TD_ASTRING = 25, + TD_JSVAL = 26, + TD_DOMOBJECT = 27, + TD_PROMISE = 28 }; -static_assert(_TD_LAST_COMPLEX < 32, "nsXPTTypeTag must fit in 5 bits"); - /* * A nsXPTType is a union used to identify the type of a method argument or @@ -220,34 +193,27 @@ static_assert(_TD_LAST_COMPLEX < 32, "nsXPTTypeTag must fit in 5 bits"); */ struct nsXPTType { - nsXPTTypeTag Tag() const { return static_cast(mTag); } + // NOTE: This is uint8_t instead of nsXPTTypeTag so that it can be compared + // with the nsXPTType::* re-exports. + uint8_t Tag() const { return mTag; } - // The index in the function argument list which should be used when - // determining the iid_is or size_is properties of this dependent type. uint8_t ArgNum() const { MOZ_ASSERT(Tag() == TD_INTERFACE_IS_TYPE || Tag() == TD_PSTRING_SIZE_IS || Tag() == TD_PWSTRING_SIZE_IS || - Tag() == TD_LEGACY_ARRAY); + Tag() == TD_ARRAY); return mData1; } + const nsXPTType& ArrayElementType() const { + MOZ_ASSERT(Tag() == TD_ARRAY); + return xpt::detail::GetType(mData2); + } + private: - // Helper for reading 16-bit data values split between mData1 and mData2. uint16_t Data16() const { return ((uint16_t)mData1 << 8) | mData2; } public: - // Get the type of the element in the current array or sequence. Arrays only - // fit 8 bits of type data, while sequences support up to 16 bits of type data - // due to not needing to store an ArgNum. - const nsXPTType& ArrayElementType() const { - if (Tag() == TD_LEGACY_ARRAY) { - return xpt::detail::GetType(mData2); - } - MOZ_ASSERT(Tag() == TD_ARRAY); - return xpt::detail::GetType(Data16()); - } - // We store the 16-bit iface value as two 8-bit values in order to // avoid 16-bit alignment requirements for XPTTypeDescriptor, which // reduces its size and also the size of XPTParamDescriptor. @@ -261,44 +227,43 @@ public: return xpt::detail::GetDOMObjectInfo(Data16()); } - // See the comments in nsXPTTypeTag for an explanation as to what each of - // these categories mean. - bool IsArithmetic() const { return Tag() <= _TD_LAST_ARITHMETIC; } - bool IsPointer() const { return !IsArithmetic() && Tag() <= _TD_LAST_POINTER; } - bool IsComplex() const { return Tag() > _TD_LAST_POINTER; } + // 'Arithmetic' here roughly means that the value is self-contained and + // doesn't depend on anything else in memory (ie: not a pointer, not an + // XPCOM object, not a jsval, etc). + // + // Supposedly this terminology comes from Harbison/Steele, but it's still + // a rather crappy name. We'd change it if it wasn't used all over the + // place in xptcall. :-( + bool IsArithmetic() const { return Tag() <= TD_WCHAR; } bool IsInterfacePointer() const { return Tag() == TD_INTERFACE_TYPE || Tag() == TD_INTERFACE_IS_TYPE; } + bool IsArray() const { return Tag() == TD_ARRAY; } + bool IsDependent() const { - return (Tag() == TD_ARRAY && InnermostType().IsDependent()) || - Tag() == TD_INTERFACE_IS_TYPE || Tag() == TD_LEGACY_ARRAY || + return Tag() == TD_INTERFACE_IS_TYPE || Tag() == TD_ARRAY || Tag() == TD_PSTRING_SIZE_IS || Tag() == TD_PWSTRING_SIZE_IS; } // Unwrap a nested type to its innermost value (e.g. through arrays). const nsXPTType& InnermostType() const { - if (Tag() == TD_LEGACY_ARRAY || Tag() == TD_ARRAY) { + if (Tag() == TD_ARRAY) { return ArrayElementType().InnermostType(); } return *this; } - // In-memory size of native type in bytes. + // Helper methods for working with the type's native representation. inline size_t Stride() const; + inline bool HasPointerRepr() const; // Offset the given base pointer to reference the element at the given index. void* ElementPtr(const void* aBase, uint32_t aIndex) const { return (char*)aBase + (aIndex * Stride()); } - // Zero out a native value of the given type. The type must not be 'complex'. - void ZeroValue(void* aValue) const { - MOZ_RELEASE_ASSERT(!IsComplex(), "Cannot zero a complex value"); - memset(aValue, 0, Stride()); - } - // Indexes into the extra types array of a small set of known types. enum class Idx : uint8_t { @@ -324,7 +289,7 @@ public: // Helper methods for fabricating nsXPTType values used by xpconnect. static nsXPTType MkArrayType(Idx aInner) { MOZ_ASSERT(aInner <= Idx::INTERFACE_IS_TYPE); - return { TD_LEGACY_ARRAY, false, false, false, 0, (uint8_t)aInner }; + return { TD_ARRAY, false, false, false, 0, (uint8_t)aInner }; } static const nsXPTType& Get(Idx aInner) { MOZ_ASSERT(aInner <= Idx::INTERFACE_IS_TYPE); @@ -335,41 +300,42 @@ public: // nsXPTType backwards compatibility // /////////////////////////////////////// - nsXPTType& operator=(nsXPTTypeTag aPrefix) { mTag = aPrefix; return *this; } - operator nsXPTTypeTag() const { return Tag(); } + nsXPTType& operator=(uint8_t aPrefix) { mTag = aPrefix; return *this; } + operator uint8_t() const { return TagPart(); }; + uint8_t TagPart() const { return mTag; }; -#define TD_ALIAS_(name_, value_) static constexpr nsXPTTypeTag name_ = value_ - TD_ALIAS_(T_I8 , TD_INT8 ); - TD_ALIAS_(T_I16 , TD_INT16 ); - TD_ALIAS_(T_I32 , TD_INT32 ); - TD_ALIAS_(T_I64 , TD_INT64 ); - TD_ALIAS_(T_U8 , TD_UINT8 ); - TD_ALIAS_(T_U16 , TD_UINT16 ); - TD_ALIAS_(T_U32 , TD_UINT32 ); - TD_ALIAS_(T_U64 , TD_UINT64 ); - TD_ALIAS_(T_FLOAT , TD_FLOAT ); - TD_ALIAS_(T_DOUBLE , TD_DOUBLE ); - TD_ALIAS_(T_BOOL , TD_BOOL ); - TD_ALIAS_(T_CHAR , TD_CHAR ); - TD_ALIAS_(T_WCHAR , TD_WCHAR ); - TD_ALIAS_(T_VOID , TD_VOID ); - TD_ALIAS_(T_IID , TD_PNSIID ); - TD_ALIAS_(T_DOMSTRING , TD_DOMSTRING ); - TD_ALIAS_(T_CHAR_STR , TD_PSTRING ); - TD_ALIAS_(T_WCHAR_STR , TD_PWSTRING ); - TD_ALIAS_(T_INTERFACE , TD_INTERFACE_TYPE ); - TD_ALIAS_(T_INTERFACE_IS , TD_INTERFACE_IS_TYPE); - TD_ALIAS_(T_LEGACY_ARRAY , TD_LEGACY_ARRAY ); - TD_ALIAS_(T_PSTRING_SIZE_IS , TD_PSTRING_SIZE_IS ); - TD_ALIAS_(T_PWSTRING_SIZE_IS , TD_PWSTRING_SIZE_IS ); - TD_ALIAS_(T_UTF8STRING , TD_UTF8STRING ); - TD_ALIAS_(T_CSTRING , TD_CSTRING ); - TD_ALIAS_(T_ASTRING , TD_ASTRING ); - TD_ALIAS_(T_JSVAL , TD_JSVAL ); - TD_ALIAS_(T_DOMOBJECT , TD_DOMOBJECT ); - TD_ALIAS_(T_PROMISE , TD_PROMISE ); - TD_ALIAS_(T_ARRAY , TD_ARRAY ); -#undef TD_ALIAS_ + enum // Re-export TD_ interfaces from nsXPTType + { + T_I8 = TD_INT8 , + T_I16 = TD_INT16 , + T_I32 = TD_INT32 , + T_I64 = TD_INT64 , + T_U8 = TD_UINT8 , + T_U16 = TD_UINT16 , + T_U32 = TD_UINT32 , + T_U64 = TD_UINT64 , + T_FLOAT = TD_FLOAT , + T_DOUBLE = TD_DOUBLE , + T_BOOL = TD_BOOL , + T_CHAR = TD_CHAR , + T_WCHAR = TD_WCHAR , + T_VOID = TD_VOID , + T_IID = TD_PNSIID , + T_DOMSTRING = TD_DOMSTRING , + T_CHAR_STR = TD_PSTRING , + T_WCHAR_STR = TD_PWSTRING , + T_INTERFACE = TD_INTERFACE_TYPE , + T_INTERFACE_IS = TD_INTERFACE_IS_TYPE, + T_ARRAY = TD_ARRAY , + T_PSTRING_SIZE_IS = TD_PSTRING_SIZE_IS , + T_PWSTRING_SIZE_IS = TD_PWSTRING_SIZE_IS , + T_UTF8STRING = TD_UTF8STRING , + T_CSTRING = TD_CSTRING , + T_ASTRING = TD_ASTRING , + T_JSVAL = TD_JSVAL , + T_DOMOBJECT = TD_DOMOBJECT , + T_PROMISE = TD_PROMISE + }; //////////////////////////////////////////////////////////////// // Ensure these fields are in the same order as xptcodegen.py // @@ -410,10 +376,15 @@ struct nsXPTParamInfo const nsXPTType& GetType() const { return Type(); } // XXX remove (backcompat) // Whether this parameter is passed indirectly on the stack. All out/inout - // params are passed indirectly, and complex types are always passed - // indirectly. + // params are passed indirectly, although some types are passed indirectly + // unconditionally. bool IsIndirect() const { - return IsOut() || Type().IsComplex(); + return IsOut() || + mType.Tag() == TD_JSVAL || + mType.Tag() == TD_ASTRING || + mType.Tag() == TD_DOMSTRING || + mType.Tag() == TD_CSTRING || + mType.Tag() == TD_UTF8STRING; } //////////////////////////////////////////////////////////////// @@ -531,34 +502,6 @@ struct nsXPTDOMObjectInfo namespace xpt { namespace detail { -// The UntypedTArray type allows low-level access from XPConnect to nsTArray -// internals without static knowledge of the array element type in question. -class UntypedTArray - : public nsTArray_base -{ -public: - void* Elements() const { - return static_cast(Hdr() + 1); - } - - // Changes the length and capacity to be at least large enough for aTo elements. - bool SetLength(const nsXPTType& aEltTy, uint32_t aTo) { - if (!EnsureCapacity(aTo, aEltTy.Stride())) { - return false; - } - mHdr->mLength = aTo; - return true; - } - - // Free backing memory for the nsTArray object. - void Clear() { - if (mHdr != EmptyHdr() && !UsesAutoArrayBuffer()) { - nsTArrayFallibleAllocator::Free(mHdr); - } - mHdr = EmptyHdr(); - } -}; - /** * The compressed representation of constants from XPT. Not part of the public * interface, as we also need to support Shim interfaces. @@ -650,11 +593,39 @@ GetString(uint32_t aIndex) } // namespace detail } // namespace xpt +inline bool +nsXPTType::HasPointerRepr() const +{ + // This method should return `true` if the given type would be represented as + // a pointer when not passed indirectly. + switch (Tag()) { + case TD_VOID: + case TD_PNSIID: + case TD_PSTRING: + case TD_PWSTRING: + case TD_INTERFACE_TYPE: + case TD_INTERFACE_IS_TYPE: + case TD_ARRAY: + case TD_PSTRING_SIZE_IS: + case TD_PWSTRING_SIZE_IS: + case TD_DOMOBJECT: + case TD_PROMISE: + return true; + default: + return false; + } +} + inline size_t nsXPTType::Stride() const { // Compute the stride to use when walking an array of the given type. - switch (Tag()) { + // + // NOTE: We cast to nsXPTTypeTag here so we get a warning if a type is missed + // in this switch statement. It's important that this method returns a value + // for every possible type. + + switch (static_cast(Tag())) { case TD_INT8: return sizeof(int8_t); case TD_INT16: return sizeof(int16_t); case TD_INT32: return sizeof(int32_t); @@ -668,7 +639,6 @@ nsXPTType::Stride() const case TD_BOOL: return sizeof(bool); case TD_CHAR: return sizeof(char); case TD_WCHAR: return sizeof(char16_t); - case TD_VOID: return sizeof(void*); case TD_PNSIID: return sizeof(nsIID*); case TD_DOMSTRING: return sizeof(nsString); @@ -676,17 +646,15 @@ nsXPTType::Stride() const case TD_PWSTRING: return sizeof(char16_t*); case TD_INTERFACE_TYPE: return sizeof(nsISupports*); case TD_INTERFACE_IS_TYPE: return sizeof(nsISupports*); - case TD_LEGACY_ARRAY: return sizeof(void*); + case TD_ARRAY: return sizeof(void*); case TD_PSTRING_SIZE_IS: return sizeof(char*); case TD_PWSTRING_SIZE_IS: return sizeof(char16_t*); - case TD_DOMOBJECT: return sizeof(void*); - case TD_PROMISE: return sizeof(void*); - case TD_UTF8STRING: return sizeof(nsCString); case TD_CSTRING: return sizeof(nsCString); case TD_ASTRING: return sizeof(nsString); case TD_JSVAL: return sizeof(JS::Value); - case TD_ARRAY: return sizeof(xpt::detail::UntypedTArray); + case TD_DOMOBJECT: return sizeof(void*); + case TD_PROMISE: return sizeof(void*); } MOZ_CRASH("Unknown type"); diff --git a/xpcom/rust/xpcom/src/reexports.rs b/xpcom/rust/xpcom/src/reexports.rs index 623e8a44580e..5673c0d6d5ef 100644 --- a/xpcom/rust/xpcom/src/reexports.rs +++ b/xpcom/rust/xpcom/src/reexports.rs @@ -11,7 +11,7 @@ // re-export libc so it can be used by the procedural macro. pub extern crate libc; -pub use nsstring::{nsACString, nsAString, nsCString, nsString}; +pub use nsstring::{nsACString, nsAString}; pub use nserror::{nsresult, NsresultExt, NS_ERROR_NO_INTERFACE, NS_OK}; diff --git a/xpcom/rust/xpcom/xpcom_macros/src/lib.rs b/xpcom/rust/xpcom/xpcom_macros/src/lib.rs index 260d4ca64b18..7b576b1ca568 100644 --- a/xpcom/rust/xpcom/xpcom_macros/src/lib.rs +++ b/xpcom/rust/xpcom/xpcom_macros/src/lib.rs @@ -563,9 +563,7 @@ fn xpcom(init: DeriveInput) -> Result> { #[allow(unused_imports)] use ::xpcom::interfaces::*; #[allow(unused_imports)] - use ::xpcom::reexports::{ - libc, nsACString, nsAString, nsCString, nsString, nsresult - }; + use ::xpcom::reexports::{libc, nsACString, nsAString, nsresult}; unsafe { // NOTE: This is split into multiple lines to make the diff --git a/xpcom/tests/gtest/TestTArray2.cpp b/xpcom/tests/gtest/TestTArray2.cpp index e8b9e9da3a40..ac00a9336c78 100644 --- a/xpcom/tests/gtest/TestTArray2.cpp +++ b/xpcom/tests/gtest/TestTArray2.cpp @@ -382,25 +382,6 @@ TEST(TArray, test_move_array) { differentAllocatorMoveableArray2 = std::move(autoMoveableArray2); ASSERT_EQ(Moveable::Count(), 8); - - AutoTArray moveableAutoArray; - for (uint32_t i = 0; i < 4; ++i) { - ASSERT_TRUE(moveableAutoArray.AppendElement(Moveable())); - } - - ASSERT_EQ(Moveable::Count(), 12); - - const AutoTArray& constRefMoveableAutoArray = moveableAutoArray; - - ASSERT_EQ(Moveable::Count(), 12); - - AutoTArray copyMoveableAutoArray(constRefMoveableAutoArray); - - ASSERT_EQ(Moveable::Count(), 16); - - AutoTArray movedMoveableAutoArray(std::move(moveableAutoArray)); - - ASSERT_EQ(Moveable::Count(), 16); } //----