Bug 745233 - Back out 33e485d0e23b, 26eb08593f89, ce72679ffb95, 10b783e85c1e (bug 720771, bug 742384) because of Linux64 PGO crashes

This commit is contained in:
Matt Brubeck 2012-04-13 10:40:27 -07:00
parent 1f214c8e6a
commit b950055584
12 changed files with 32 additions and 1904 deletions

View File

@ -21,7 +21,6 @@
*
* Contributor(s):
* Dan Witte <dwitte@mozilla.com>
* David Rajchenbach-Teller <dteller@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
@ -63,9 +62,6 @@
#include <windows.h>
#endif
#include "mozilla/StandardInteger.h"
#include "mozilla/Scoped.h"
using namespace std;
namespace js {
@ -98,7 +94,7 @@ namespace CType {
static JSBool HasInstance(JSContext* cx, JSObject* obj, const jsval* v, JSBool* bp);
/*
/**
* Get the global "ctypes" object.
*
* |obj| must be a CType object.
@ -189,8 +185,7 @@ namespace CData {
static JSBool Address(JSContext* cx, unsigned argc, jsval* vp);
static JSBool ReadString(JSContext* cx, unsigned argc, jsval* vp);
static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp);
static JSString *GetSourceString(JSContext *cx, JSObject *typeObj,
void *data);
static JSBool ErrnoGetter(JSContext* cx, JSObject *obj, jsid idval,
jsval* vp);
@ -200,118 +195,6 @@ namespace CData {
#endif // defined(XP_WIN)
}
namespace CDataFinalizer {
/*
* Attach a C function as a finalizer to a JS object.
*
* This function is available from JS as |ctypes.withFinalizer|.
*
* JavaScript signature:
* function(CData, CData): CDataFinalizer
* value finalizer finalizable
*
* Where |finalizer| is a one-argument function taking a value
* with the same type as |value|.
*/
static JSBool Construct(JSContext* cx, unsigned argc, jsval *vp);
/*
* Private data held by |CDataFinalizer|.
*
* See also |enum CDataFinalizerSlot| for the slots of
* |CDataFinalizer|.
*
* Note: the private data may be NULL, if |dispose|, |forget| or the
* finalizer has already been called.
*/
struct Private {
/*
* The C data to pass to the code.
* Finalization/|dispose|/|forget| release this memory.
*/
void *cargs;
/*
* The total size of the buffer pointed by |cargs|
*/
size_t cargs_size;
/*
* Low-level signature information.
* Finalization/|dispose|/|forget| release this memory.
*/
ffi_cif CIF;
/*
* The C function to invoke during finalization.
* Do not deallocate this.
*/
uintptr_t code;
/*
* A buffer for holding the return value.
* Finalization/|dispose|/|forget| release this memory.
*/
void *rvalue;
};
/*
* Methods of instances of |CDataFinalizer|
*/
namespace Methods {
static JSBool Dispose(JSContext* cx, unsigned argc, jsval *vp);
static JSBool Forget(JSContext* cx, unsigned argc, jsval *vp);
static JSBool ToSource(JSContext* cx, unsigned argc, jsval *vp);
static JSBool ToString(JSContext* cx, unsigned argc, jsval *vp);
}
/*
* Utility functions
*
* @return true if |obj| is a CDataFinalizer, false otherwise.
*/
static bool IsCDataFinalizer(JSObject *obj);
/*
* Clean up the finalization information of a CDataFinalizer.
*
* Used by |Finalize|, |Dispose| and |Forget|.
*
* @param p The private information of the CDataFinalizer. If NULL,
* this function does nothing.
* @param obj Either NULL, if the object should not be cleaned up (i.e.
* during finalization) or a CDataFinalizer JSObject. Always use NULL
* if you are calling from a finalizer.
*/
static void Cleanup(Private *p, JSObject *obj);
/*
* Perform the actual call to the finalizer code.
*/
static void CallFinalizer(CDataFinalizer::Private *p,
int* errnoStatus,
int32_t* lastErrorStatus);
/*
* Return the CType of a CDataFinalizer object, or NULL if the object
* has been cleaned-up already.
*/
static JSObject *GetCType(JSContext *cx, JSObject *obj);
/*
* Perform finalization of a |CDataFinalizer|
*/
static void Finalize(JSFreeOp *fop, JSObject *obj);
/*
* Return the jsval contained by this finalizer.
*
* Note that the jsval is actually not recorded, but converted back from C.
*/
static bool GetValue(JSContext *cx, JSObject *obj, jsval *result);
}
// Int64Base provides functions common to Int64 and UInt64.
namespace Int64Base {
JSObject* Construct(JSContext* cx, JSObject* proto, uint64_t data,
@ -417,30 +300,6 @@ static JSClass sCClosureClass = {
NULL, NULL, NULL, NULL, CClosure::Trace
};
/*
* Class representing the prototype of CDataFinalizer.
*/
static JSClass sCDataFinalizerProtoClass = {
"CDataFinalizer",
0,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
};
/*
* Class representing instances of CDataFinalizer.
*
* Instances of CDataFinalizer have both private data (with type
* |CDataFinalizer::Private|) and slots (see |CDataFinalizerSlots|).
*/
static JSClass sCDataFinalizerClass = {
"CDataFinalizer",
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(CDATAFINALIZER_SLOTS),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CDataFinalizer::Finalize,
};
#define CTYPESFN_FLAGS \
(JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)
@ -453,9 +312,6 @@ static JSClass sCDataFinalizerClass = {
#define CDATAFN_FLAGS \
(JSPROP_READONLY | JSPROP_PERMANENT)
#define CDATAFINALIZERFN_FLAGS \
(JSPROP_READONLY | JSPROP_PERMANENT)
static JSPropertySpec sCTypeProps[] = {
{ "name", 0, CTYPESPROP_FLAGS, CType::NameGetter, NULL },
{ "size", 0, CTYPESPROP_FLAGS, CType::SizeGetter, NULL },
@ -485,18 +341,6 @@ static JSFunctionSpec sCDataFunctions[] = {
JS_FS_END
};
static JSPropertySpec sCDataFinalizerProps[] = {
{ 0, 0, 0, NULL, NULL }
};
static JSFunctionSpec sCDataFinalizerFunctions[] = {
JS_FN("dispose", CDataFinalizer::Methods::Dispose, 0, CDATAFINALIZERFN_FLAGS),
JS_FN("forget", CDataFinalizer::Methods::Forget, 0, CDATAFINALIZERFN_FLAGS),
JS_FN("toString", CDataFinalizer::Methods::ToString, 0, CDATAFINALIZERFN_FLAGS),
JS_FN("toSource", CDataFinalizer::Methods::ToSource, 0, CDATAFINALIZERFN_FLAGS),
JS_FS_END
};
static JSFunctionSpec sPointerFunction =
JS_FN("PointerType", PointerType::Create, 1, CTYPESCTOR_FLAGS);
@ -640,7 +484,6 @@ static JSPropertySpec sModuleProps[] = {
};
static JSFunctionSpec sModuleFunctions[] = {
JS_FN("CDataFinalizer", CDataFinalizer::Construct, 2, CTYPESFN_FLAGS),
JS_FN("open", Library::Open, 1, CTYPESFN_FLAGS),
JS_FN("cast", CData::Cast, 2, CTYPESFN_FLAGS),
JS_FN("getRuntime", CData::GetRuntime, 1, CTYPESFN_FLAGS),
@ -662,16 +505,9 @@ NewUCString(JSContext* cx, const AutoString& from)
return JS_NewUCStringCopyN(cx, from.begin(), from.length());
}
/*
* Return a size rounded up to a multiple of a power of two.
*
* Note: |align| must be a power of 2.
*/
JS_ALWAYS_INLINE size_t
Align(size_t val, size_t align)
{
// Ensure that align is a power of two.
MOZ_ASSERT(align != 0 && (align & (align - 1)) == 0);
return ((val - 1) | (align - 1)) + 1;
}
@ -1133,26 +969,6 @@ GetCallbacks(JSObject* obj)
return static_cast<JSCTypesCallbacks*>(JSVAL_TO_PRIVATE(result));
}
// Utility function to access a property of an object as an object
// returns false and sets the error if the property does not exist
// or is not an object
bool GetObjectProperty(JSContext *cx, JSObject *obj,
const char *property, JSObject **result)
{
jsval val;
if (!JS_GetProperty(cx, obj, property, &val)) {
return false;
}
if (JSVAL_IS_PRIMITIVE(val)) {
JS_ReportError(cx, "missing or non-object field");
return false;
}
*result = JSVAL_TO_OBJECT(val);
return true;
}
JS_BEGIN_EXTERN_C
JS_PUBLIC_API(JSBool)
@ -1176,30 +992,6 @@ JS_InitCTypesClass(JSContext* cx, JSObject* global)
!JS_DefineProperties(cx, ctypes, sModuleProps))
return false;
// Set up ctypes.CDataFinalizer.prototype.
JSObject* ctor;
if (!GetObjectProperty(cx, ctypes, "CDataFinalizer", &ctor)) {
return NULL;
}
JSObject* prototype = JS_NewObject(cx, &sCDataFinalizerProtoClass,
NULL, ctypes);
if (!prototype)
return NULL;
if (!JS_DefineProperties(cx, prototype, sCDataFinalizerProps) ||
!JS_DefineFunctions(cx, prototype, sCDataFinalizerFunctions))
return NULL;
if (!JS_DefineProperty(cx, ctor, "prototype", OBJECT_TO_JSVAL(prototype),
NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
return NULL;
if (!JS_DefineProperty(cx, prototype, "constructor", OBJECT_TO_JSVAL(ctor),
NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
return NULL;
// Seal the ctypes object, to prevent modification.
return JS_FreezeObject(cx, ctypes);
}
@ -1481,15 +1273,7 @@ jsvalToInteger(JSContext* cx, jsval val, IntegerType* result)
return ConvertExact(i, result);
}
if (CDataFinalizer::IsCDataFinalizer(obj)) {
jsval innerData;
if (!CDataFinalizer::GetValue(cx, obj, &innerData)) {
return false; // Nothing to convert
}
return jsvalToInteger(cx, innerData, result);
}
return false;
return false;
}
if (JSVAL_IS_BOOLEAN(val)) {
// Implicitly promote boolean values to 0 or 1, like C.
@ -1660,15 +1444,6 @@ jsvalToBigInteger(JSContext* cx,
int64_t i = Int64Base::GetInt(obj);
return ConvertExact(i, result);
}
if (CDataFinalizer::IsCDataFinalizer(obj)) {
jsval innerData;
if (!CDataFinalizer::GetValue(cx, obj, &innerData)) {
return false; // Nothing to convert
}
return jsvalToBigInteger(cx, innerData, allowString, result);
}
}
return false;
}
@ -2009,40 +1784,20 @@ ImplicitConvert(JSContext* cx,
{
JS_ASSERT(CType::IsSizeDefined(targetType));
// First, check if val is either a CData object or a CDataFinalizer
// of type targetType.
// First, check if val is a CData object of type targetType.
JSObject* sourceData = NULL;
JSObject* sourceType = NULL;
if (!JSVAL_IS_PRIMITIVE(val)) {
if (CData::IsCData(JSVAL_TO_OBJECT(val))) {
sourceData = JSVAL_TO_OBJECT(val);
sourceType = CData::GetCType(sourceData);
if (!JSVAL_IS_PRIMITIVE(val) &&
CData::IsCData(JSVAL_TO_OBJECT(val))) {
sourceData = JSVAL_TO_OBJECT(val);
sourceType = CData::GetCType(sourceData);
// If the types are equal, copy the buffer contained within the CData.
// (Note that the buffers may overlap partially or completely.)
if (CType::TypesEqual(sourceType, targetType)) {
size_t size = CType::GetSize(sourceType);
memmove(buffer, CData::GetData(sourceData), size);
return true;
}
} else if (CDataFinalizer::IsCDataFinalizer(JSVAL_TO_OBJECT(val))) {
sourceData = JSVAL_TO_OBJECT(val);
sourceType = CDataFinalizer::GetCType(cx, sourceData);
CDataFinalizer::Private *p = (CDataFinalizer::Private *)
JS_GetPrivate(sourceData);
if (!p) {
// We have called |dispose| or |forget| already.
JS_ReportError(cx, "Attempting to convert an empty CDataFinalizer");
return JS_FALSE;
}
// If the types are equal, copy the buffer contained within the CData.
if (CType::TypesEqual(sourceType, targetType)) {
memmove(buffer, p->cargs, p->cargs_size);
return true;
}
// If the types are equal, copy the buffer contained within the CData.
// (Note that the buffers may overlap partially or completely.)
if (CType::TypesEqual(sourceType, targetType)) {
size_t size = CType::GetSize(sourceType);
memmove(buffer, CData::GetData(sourceData), size);
return true;
}
}
@ -6271,26 +6026,6 @@ CData::ReadString(JSContext* cx, unsigned argc, jsval* vp)
return JS_TRUE;
}
JSString *
CData::GetSourceString(JSContext *cx, JSObject *typeObj, void *data)
{
// Walk the types, building up the toSource() string.
// First, we build up the type expression:
// 't.ptr' for pointers;
// 't.array([n])' for arrays;
// 'n' for structs, where n = t.name, the struct's name. (We assume this is
// bound to a variable in the current scope.)
AutoString source;
BuildTypeSource(cx, typeObj, true, source);
AppendString(source, "(");
if (!BuildDataSource(cx, typeObj, data, false, source))
return NULL;
AppendString(source, ")");
return NewUCString(cx, source);
}
JSBool
CData::ToSource(JSContext* cx, unsigned argc, jsval* vp)
{
@ -6311,11 +6046,24 @@ CData::ToSource(JSContext* cx, unsigned argc, jsval* vp)
JSObject* typeObj = CData::GetCType(obj);
void* data = CData::GetData(obj);
result = CData::GetSourceString(cx, typeObj, data);
} else {
result = JS_NewStringCopyZ(cx, "[CData proto object]");
}
// Walk the types, building up the toSource() string.
// First, we build up the type expression:
// 't.ptr' for pointers;
// 't.array([n])' for arrays;
// 'n' for structs, where n = t.name, the struct's name. (We assume this is
// bound to a variable in the current scope.)
AutoString source;
BuildTypeSource(cx, typeObj, true, source);
AppendString(source, "(");
if (!BuildDataSource(cx, typeObj, data, false, source))
return JS_FALSE;
AppendString(source, ")");
result = NewUCString(cx, source);
}
else
result = JS_NewStringCopyZ(cx, "[CData proto object]");
if (!result)
return JS_FALSE;
@ -6350,511 +6098,6 @@ CData::LastErrorGetter(JSContext* cx, JSObject* obj, jsid, jsval* vp)
}
#endif // defined(XP_WIN)
JSBool
CDataFinalizer::Methods::ToSource(JSContext *cx, unsigned argc, jsval *vp)
{
JSObject* objThis = JS_THIS_OBJECT(cx, vp);
if (!objThis || !CDataFinalizer::IsCDataFinalizer(objThis)) {
JS_ReportError(cx, "not a CDataFinalizer");
return JS_FALSE;
}
CDataFinalizer::Private *p = (CDataFinalizer::Private *)
JS_GetPrivate(objThis);
JSString *strMessage;
if (!p) {
strMessage = JS_NewStringCopyZ(cx, "ctypes.CDataFinalizer()");
} else {
JSObject *objType = CDataFinalizer::GetCType(cx, objThis);
if (!objType) {
JS_ReportError(cx, "CDataFinalizer has no type");
return JS_FALSE;
}
AutoString source;
AppendString(source, "ctypes.CDataFinalizer(");
JSString *srcValue = CData::GetSourceString(cx, objType, p->cargs);
if (!srcValue) {
return JS_FALSE;
}
AppendString(source, srcValue);
AppendString(source, ", ");
jsval valCodePtrType = JS_GetReservedSlot(objThis,
SLOT_DATAFINALIZER_CODETYPE);
if (JSVAL_IS_PRIMITIVE(valCodePtrType)) {
return JS_FALSE;
}
JSString *srcDispose =
CData::GetSourceString(cx, JSVAL_TO_OBJECT(valCodePtrType),
&(p->code));
if (!srcDispose) {
return JS_FALSE;
}
AppendString(source, srcDispose);
AppendString(source, ")");
strMessage = NewUCString(cx, source);
}
if (!strMessage) {
// This is a memory issue, no error message
return JS_FALSE;
}
JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(strMessage));
return JS_TRUE;
}
JSBool
CDataFinalizer::Methods::ToString(JSContext *cx, unsigned argc, jsval *vp)
{
JSObject* objThis = JS_THIS_OBJECT(cx, vp);
if (!objThis || !CDataFinalizer::IsCDataFinalizer(objThis)) {
JS_ReportError(cx, "not a CDataFinalizer");
return JS_FALSE;
}
JSString *strMessage;
jsval value;
if (!JS_GetPrivate(objThis)) {
// Pre-check whether CDataFinalizer::GetValue can fail
// to avoid reporting an error when not appropriate.
strMessage = JS_NewStringCopyZ(cx, "[CDataFinalizer - empty]");
if (!strMessage) {
return JS_FALSE;
}
} else if (!CDataFinalizer::GetValue(cx, objThis, &value)) {
JS_NOT_REACHED("Could not convert an empty CDataFinalizer");
} else {
strMessage = JS_ValueToString(cx, value);
if (!strMessage) {
return JS_FALSE;
}
}
JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(strMessage));
return JS_TRUE;
}
bool
CDataFinalizer::IsCDataFinalizer(JSObject *obj)
{
return JS_GetClass(obj) == &sCDataFinalizerClass;
}
JSObject *
CDataFinalizer::GetCType(JSContext *cx, JSObject *obj)
{
MOZ_ASSERT(IsCDataFinalizer(obj));
jsval valData = JS_GetReservedSlot(obj,
SLOT_DATAFINALIZER_VALTYPE);
if (JSVAL_IS_VOID(valData)) {
return NULL;
}
return JSVAL_TO_OBJECT(valData);
}
bool
CDataFinalizer::GetValue(JSContext *cx, JSObject *obj, jsval *aResult)
{
MOZ_ASSERT(IsCDataFinalizer(obj));
CDataFinalizer::Private *p = (CDataFinalizer::Private *)
JS_GetPrivate(obj);
if (!p) {
JS_ReportError(cx, "Attempting to get the value of an empty CDataFinalizer");
return false; // We have called |dispose| or |forget| already.
}
return ConvertToJS(cx, GetCType(cx, obj),
/*parent*/NULL, p -> cargs, false, true, aResult);
}
/*
* Attach a C function as a finalizer to a JS object.
*
* Pseudo-JS signature:
* function(CData<T>, CData<T -> U>): CDataFinalizer<T>
* value, finalizer
*
* This function attaches strong references to the following values:
* - the CType of |value|
*
* Note: This function takes advantage of the fact that non-variadic
* CData functions are initialized during creation.
*/
JSBool
CDataFinalizer::Construct(JSContext* cx, unsigned argc, jsval *vp)
{
JSObject* objSelf = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
JSObject *objProto;
if (!GetObjectProperty(cx, objSelf, "prototype", &objProto)) {
JS_ReportError(cx, "CDataFinalizer.prototype does not exist");
return JS_FALSE;
}
// Get arguments
if (argc == 0) { // Special case: the empty (already finalized) object
JSObject *objResult = JS_NewObject(cx, &sCDataFinalizerClass, objProto, NULL);
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(objResult));
return JS_TRUE;
}
if (argc != 2) {
JS_ReportError(cx, "CDataFinalizer takes 2 arguments");
return JS_FALSE;
}
jsval* argv = JS_ARGV(cx, vp);
jsval valCodePtr = argv[1];
if (!JSVAL_IS_OBJECT(valCodePtr) || JSVAL_IS_NULL(valCodePtr)) {
return TypeError(cx, "_a CData object_ of a function pointer type",
valCodePtr);
}
JSObject *objCodePtr = JSVAL_TO_OBJECT(valCodePtr);
//Note: Using a custom argument formatter here would be awkward (requires
//a destructor just to uninstall the formatter).
// 2. Extract argument type of |objCodePtr|
if (!CData::IsCData(objCodePtr)) {
return TypeError(cx, "a _CData_ object of a function pointer type",
valCodePtr);
}
JSObject *objCodePtrType = CData::GetCType(objCodePtr);
MOZ_ASSERT(objCodePtrType);
TypeCode typCodePtr = CType::GetTypeCode(objCodePtrType);
if (typCodePtr != TYPE_pointer) {
return TypeError(cx, "a CData object of a function _pointer_ type",
OBJECT_TO_JSVAL(objCodePtrType));
}
JSObject *objCodeType = PointerType::GetBaseType(objCodePtrType);
MOZ_ASSERT(objCodeType);
TypeCode typCode = CType::GetTypeCode(objCodeType);
if (typCode != TYPE_function) {
return TypeError(cx, "a CData object of a _function_ pointer type",
OBJECT_TO_JSVAL(objCodePtrType));
}
uintptr_t code = *reinterpret_cast<uintptr_t*>(CData::GetData(objCodePtr));
if (!code) {
return TypeError(cx, "a CData object of a _non-NULL_ function pointer type",
OBJECT_TO_JSVAL(objCodePtrType));
}
FunctionInfo* funInfoFinalizer =
FunctionType::GetFunctionInfo(objCodeType);
MOZ_ASSERT(funInfoFinalizer);
if ((funInfoFinalizer->mArgTypes.length() != 1)
|| (funInfoFinalizer->mIsVariadic)) {
return TypeError(cx, "a function accepting exactly one argument",
OBJECT_TO_JSVAL(objCodeType));
}
JSObject *objArgType = funInfoFinalizer->mArgTypes[0];
// Invariant: At this stage, we know that funInfoFinalizer->mIsVariadic
// is |false|. Therefore, funInfoFinalizer->mCIF has already been initialized.
bool freePointer = false;
// 3. Perform dynamic cast of |argv[0]| into |objType|, store it in |cargs|
size_t sizeArg;
jsval valData = argv[0];
if (!CType::GetSafeSize(objArgType, &sizeArg)) {
return TypeError(cx, "(an object with known size)", valData);
}
ScopedFreePtr<void> cargs(malloc(sizeArg));
if (!ImplicitConvert(cx, valData, objArgType, cargs.get(),
false, &freePointer)) {
return TypeError(cx, "(an object that can be converted to the following type)",
OBJECT_TO_JSVAL(objArgType));
}
if (freePointer) {
// Note: We could handle that case, if necessary.
JS_ReportError(cx, "Internal Error during CDataFinalizer. Object cannot be represented");
return JS_FALSE;
}
// 4. Prepare buffer for holding return value
JSObject *returnType = funInfoFinalizer->mReturnType;
ScopedFreePtr<void> rvalue;
if (CType::GetTypeCode(returnType) != TYPE_void_t) {
rvalue = malloc(Align(CType::GetSize(returnType),
sizeof(ffi_arg)));
} //Otherwise, simply do not allocate
// 5. Create |objResult|
JSObject *objResult = JS_NewObject(cx, &sCDataFinalizerClass, objProto, NULL);
if (!objResult) {
return JS_FALSE;
}
// Used by GetCType
JS_SetReservedSlot(objResult,
SLOT_DATAFINALIZER_VALTYPE,
OBJECT_TO_JSVAL(objArgType));
// Used by ToSource
JS_SetReservedSlot(objResult,
SLOT_DATAFINALIZER_CODETYPE,
OBJECT_TO_JSVAL(objCodePtrType));
ffi_abi abi;
if (!GetABI(cx, OBJECT_TO_JSVAL(funInfoFinalizer->mABI), &abi)) {
JS_ReportError(cx, "Internal Error: "
"Invalid ABI specification in CDataFinalizer");
return false;
}
ffi_type* rtype = CType::GetFFIType(cx, funInfoFinalizer->mReturnType);
if (!rtype) {
JS_ReportError(cx, "Internal Error: "
"Could not access ffi type of CDataFinalizer");
return JS_FALSE;
}
// 7. Store C information as private
ScopedFreePtr<CDataFinalizer::Private>
p((CDataFinalizer::Private*)malloc(sizeof(CDataFinalizer::Private)));
memmove(&p->CIF, &funInfoFinalizer->mCIF, sizeof(ffi_cif));
p->cargs = cargs.forget();
p->rvalue = rvalue.forget();
p->cargs_size = sizeArg;
p->code = code;
JS_SetPrivate(objResult, p.forget());
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(objResult));
return JS_TRUE;
}
/*
* Actually call the finalizer. Does not perform any cleanup on the object.
*
* Preconditions: |this| must be a |CDataFinalizer|, |p| must be non-null.
* The function fails if |this| has gone through |Forget|/|Dispose|
* or |Finalize|.
*
* This function does not alter the value of |errno|/|GetLastError|.
*
* If argument |errnoStatus| is non-NULL, it receives the value of |errno|
* immediately after the call. Under Windows, if argument |lastErrorStatus|
* is non-NULL, it receives the value of |GetLastError| immediately after the
* call. On other platforms, |lastErrorStatus| is ignored.
*/
void
CDataFinalizer::CallFinalizer(CDataFinalizer::Private *p,
int* errnoStatus,
int32_t* lastErrorStatus)
{
int savedErrno = errno;
errno = 0;
#if defined(XP_WIN)
int32_t savedLastError = GetLastError();
SetLastError(0);
#endif // defined(XP_WIN)
ffi_call(&p->CIF, FFI_FN(p->code), p->rvalue, &p->cargs);
if (errnoStatus) {
*errnoStatus = errno;
}
errno = savedErrno;
#if defined(XP_WIN)
if (lastErrorStatus) {
*lastErrorStatus = GetLastError();
}
SetLastError(savedLastError);
#endif // defined(XP_WIN)
}
/*
* Forget the value.
*
* Preconditions: |this| must be a |CDataFinalizer|.
* The function fails if |this| has gone through |Forget|/|Dispose|
* or |Finalize|.
*
* Does not call the finalizer. Cleans up the Private memory and releases all
* strong references.
*/
JSBool
CDataFinalizer::Methods::Forget(JSContext* cx, unsigned argc, jsval *vp)
{
if (argc != 0) {
JS_ReportError(cx, "CDataFinalizer.prototype.forget takes no arguments");
return JS_FALSE;
}
JSObject *obj = JS_THIS_OBJECT(cx, vp);
if (!obj || !CDataFinalizer::IsCDataFinalizer(obj)) {
return TypeError(cx, "a CDataFinalizer", OBJECT_TO_JSVAL(obj));
}
CDataFinalizer::Private *p = (CDataFinalizer::Private *)
JS_GetPrivate(obj);
if (!p) {
JS_ReportError(cx, "forget called on an empty CDataFinalizer");
return JS_FALSE;
}
jsval valJSData;
if (!ConvertToJS(cx, GetCType(cx, obj), NULL, p->cargs, false, true, &valJSData)) {
JS_ReportError(cx, "CDataFinalizer value cannot be represented");
return JS_FALSE;
}
CDataFinalizer::Cleanup(p, obj);
JS_SET_RVAL(cx, vp, valJSData);
return JS_TRUE;
}
/*
* Clean up the value.
*
* Preconditions: |this| must be a |CDataFinalizer|.
* The function fails if |this| has gone through |Forget|/|Dispose|
* or |Finalize|.
*
* Calls the finalizer, cleans up the Private memory and releases all
* strong references.
*/
JSBool
CDataFinalizer::Methods::Dispose(JSContext* cx, unsigned argc, jsval *vp)
{
if (argc != 0) {
JS_ReportError(cx, "CDataFinalizer.prototype.dispose takes no arguments");
return JS_FALSE;
}
JSObject *obj = JS_THIS_OBJECT(cx, vp);
if (!obj || !CDataFinalizer::IsCDataFinalizer(obj)) {
return TypeError(cx, "a CDataFinalizer", OBJECT_TO_JSVAL(obj));
}
CDataFinalizer::Private *p = (CDataFinalizer::Private *)
JS_GetPrivate(obj);
if (!p) {
JS_ReportError(cx, "dispose called on an empty CDataFinalizer.");
return JS_FALSE;
}
jsval valType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_VALTYPE);
JS_ASSERT(!JSVAL_IS_PRIMITIVE(valType));
JSObject *objCTypes = CType::GetGlobalCTypes(cx, JSVAL_TO_OBJECT(valType));
jsval valCodePtrType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_CODETYPE);
JS_ASSERT(!JSVAL_IS_PRIMITIVE(valCodePtrType));
JSObject *objCodePtrType = JSVAL_TO_OBJECT(valCodePtrType);
JSObject *objCodeType = PointerType::GetBaseType(objCodePtrType);
JS_ASSERT(objCodeType);
JS_ASSERT(CType::GetTypeCode(objCodeType) == TYPE_function);
JSObject *resultType = FunctionType::GetFunctionInfo(objCodeType)->mReturnType;
jsval result = JSVAL_VOID;
int errnoStatus;
#if defined(XP_WIN)
int32_t lastErrorStatus;
CDataFinalizer::CallFinalizer(p, &errnoStatus, &lastErrorStatus);
#else
CDataFinalizer::CallFinalizer(p, &errnoStatus, NULL);
#endif // defined(XP_WIN)
JS_SetReservedSlot(objCTypes, SLOT_ERRNO, INT_TO_JSVAL(errnoStatus));
#if defined(XP_WIN)
JS_SetReservedSlot(objCTypes, SLOT_LASTERROR, INT_TO_JSVAL(lastErrorStatus));
#endif // defined(XP_WIN)
if (ConvertToJS(cx, resultType, NULL, p->rvalue, false, true, &result)) {
CDataFinalizer::Cleanup(p, obj);
JS_SET_RVAL(cx, vp, result);
return true;
}
CDataFinalizer::Cleanup(p, obj);
return false;
}
/*
* Perform finalization.
*
* Preconditions: |this| must be the result of |CDataFinalizer|.
* It may have gone through |Forget|/|Dispose|.
*
* If |this| has not gone through |Forget|/|Dispose|, calls the
* finalizer, cleans up the Private memory and releases all
* strong references.
*/
void
CDataFinalizer::Finalize(JSFreeOp* fop, JSObject* obj)
{
CDataFinalizer::Private *p = (CDataFinalizer::Private *)
JS_GetPrivate(obj);
if (!p) {
return;
}
CDataFinalizer::CallFinalizer(p, NULL, NULL);
CDataFinalizer::Cleanup(p, NULL);
}
/*
* Perform cleanup of a CDataFinalizer
*
* Release strong references, cleanup |Private|.
*
* Argument |p| contains the private information of the CDataFinalizer. If NULL,
* this function does nothing.
* Argument |obj| should contain |NULL| during finalization (or in any context
* in which the object itself should not be cleaned up), or a CDataFinalizer
* object otherwise.
*/
void
CDataFinalizer::Cleanup(CDataFinalizer::Private *p, JSObject *obj)
{
if (!p) {
return; // We have already cleaned up
}
free(p->cargs);
free(p->rvalue);
free(p);
if (!obj) {
return; // No slots to clean up
}
JS_ASSERT(CDataFinalizer::IsCDataFinalizer(obj));
JS_SetPrivate(obj, NULL);
for (int i = 0; i < CDATAFINALIZER_SLOTS; ++i) {
JS_SetReservedSlot(obj, i, JSVAL_NULL);
}
}
/*******************************************************************************
** Int64 and UInt64 implementation
*******************************************************************************/

View File

@ -430,16 +430,6 @@ enum CClosureSlot {
CCLOSURE_SLOTS
};
enum CDataFinalizerSlot {
// The type of the value (a CType JSObject).
// We hold it to permit ImplicitConvert and ToSource.
SLOT_DATAFINALIZER_VALTYPE = 0,
// The type of the function used at finalization (a CType JSObject).
// We hold it to permit |ToSource|.
SLOT_DATAFINALIZER_CODETYPE = 1,
CDATAFINALIZER_SLOTS
};
enum TypeCtorSlot {
SLOT_FN_CTORPROTO = 0 // ctypes.{Pointer,Array,Struct}Type.prototype
// JSFunction objects always get exactly two slots.

View File

@ -53,7 +53,6 @@ NO_DIST_INSTALL = 1
CPPSRCS = jsctypes-test.cpp \
jsctypes-test-errno.cpp \
jsctypes-test-finalizer.cpp \
$(NULL)
LOCAL_INCLUDES = \

View File

@ -1,303 +0,0 @@
#include "errno.h"
#include "jsctypes-test.h"
#include "jsctypes-test-finalizer.h"
#include "jsapi.h"
#if defined(XP_WIN)
#define snprintf _snprintf
#endif // defined(XP_WIN)
/**
* Shared infrastructure
*/
/**
* An array of integers representing resources.
* - 0: unacquired
* - 1: acquired
* - < 0: error, resource has been released several times.
*/
int *gFinalizerTestResources = NULL;
char **gFinalizerTestNames = NULL;
size_t gFinalizerTestSize;
void
test_finalizer_start(size_t size)
{
gFinalizerTestResources = new int[size];
gFinalizerTestNames = new char*[size];
gFinalizerTestSize = size;
for (size_t i = 0; i < size; ++i) {
gFinalizerTestResources[i] = 0;
gFinalizerTestNames[i] = NULL;
}
}
void
test_finalizer_stop()
{
delete[] gFinalizerTestResources;
}
/**
* Check if an acquired resource has been released
*/
bool
test_finalizer_resource_is_acquired(size_t i)
{
return gFinalizerTestResources[i] == 1;
}
// Resource type: size_t
// Acquire resource i
size_t
test_finalizer_acq_size_t(size_t i)
{
gFinalizerTestResources[i] = 1;
return i;
}
// Release resource i
void
test_finalizer_rel_size_t(size_t i)
{
if (--gFinalizerTestResources[i] < 0) {
MOZ_NOT_REACHED("Assertion failed");
}
}
size_t
test_finalizer_rel_size_t_return_size_t(size_t i)
{
if (-- gFinalizerTestResources[i] < 0) {
MOZ_NOT_REACHED("Assertion failed");
}
return i;
}
RECT
test_finalizer_rel_size_t_return_struct_t(size_t i)
{
if (-- gFinalizerTestResources[i] < 0) {
MOZ_NOT_REACHED("Assertion failed");
}
const PRInt32 narrowed = (PRInt32)i;
RECT result = { narrowed, narrowed, narrowed, narrowed };
return result;
}
bool
test_finalizer_cmp_size_t(size_t a, size_t b)
{
return a==b;
}
// Resource type: int32_t
// Acquire resource i
int32_t
test_finalizer_acq_int32_t(size_t i)
{
gFinalizerTestResources[i] = 1;
return i;
}
// Release resource i
void
test_finalizer_rel_int32_t(int32_t i)
{
if (--gFinalizerTestResources[i] < 0) {
MOZ_NOT_REACHED("Assertion failed");
}
}
bool
test_finalizer_cmp_int32_t(int32_t a, int32_t b)
{
return a==b;
}
// Resource type: int64_t
// Acquire resource i
int64_t
test_finalizer_acq_int64_t(size_t i)
{
gFinalizerTestResources[i] = 1;
return i;
}
// Release resource i
void
test_finalizer_rel_int64_t(int64_t i)
{
if (-- gFinalizerTestResources[i] < 0) {
MOZ_NOT_REACHED("Assertion failed");
}
}
bool
test_finalizer_cmp_int64_t(int64_t a, int64_t b)
{
return a==b;
}
// Resource type: void*
// Acquire resource i
void*
test_finalizer_acq_ptr_t(size_t i)
{
gFinalizerTestResources[i] = 1;
return (void*)&gFinalizerTestResources[i];
}
// Release resource i
void
test_finalizer_rel_ptr_t(void *i)
{
int *as_int = (int*)i;
-- (*as_int);
if (*as_int < 0) {
MOZ_NOT_REACHED("Assertion failed");
}
}
bool
test_finalizer_cmp_ptr_t(void *a, void *b)
{
return a==b;
}
// Resource type: NULL
// Acquire resource i
void*
test_finalizer_acq_null_t(size_t i)
{
gFinalizerTestResources[0] = 1;//Always index 0
return NULL;
}
// Release resource i
void
test_finalizer_rel_null_t(void *i)
{
if (i != NULL) {
MOZ_NOT_REACHED("Assertion failed");
}
gFinalizerTestResources[0] --;
}
bool
test_finalizer_null_resource_is_acquired(size_t)
{
return gFinalizerTestResources[0] == 1;
}
bool
test_finalizer_cmp_null_t(void *a, void *b)
{
return a==b;
}
// Resource type: char*
// Acquire resource i
char*
test_finalizer_acq_string_t(int i)
{
gFinalizerTestResources[i] = 1;
if (!gFinalizerTestNames[i]) {
char* buf = new char[10];
snprintf(buf, 10, "%d", i);
gFinalizerTestNames[i] = buf;
return buf;
}
return gFinalizerTestNames[i];
}
// Release resource i
void
test_finalizer_rel_string_t(char *i)
{
int index = atoi(i);
if (index < 0 || index >= (int)gFinalizerTestSize) {
MOZ_NOT_REACHED("Assertion failed");
}
gFinalizerTestResources[index] --;
}
bool
test_finalizer_string_resource_is_acquired(size_t i)
{
return gFinalizerTestResources[i] == 1;
}
bool
test_finalizer_cmp_string_t(char *a, char *b)
{
return !strncmp(a, b, 10);
}
// Resource type: RECT
// Acquire resource i
RECT
test_finalizer_acq_struct_t(int i)
{
gFinalizerTestResources[i] = 1;
RECT result = { i, i, i, i };
return result;
}
// Release resource i
void
test_finalizer_rel_struct_t(RECT i)
{
int index = i.top;
if (index < 0 || index >= (int)gFinalizerTestSize) {
MOZ_NOT_REACHED("Assertion failed");
}
gFinalizerTestResources[index] --;
}
bool
test_finalizer_struct_resource_is_acquired(RECT i)
{
int index = i.top;
if (index < 0 || index >= (int)gFinalizerTestSize) {
MOZ_NOT_REACHED("Assertion failed");
}
return gFinalizerTestResources[index] == 1;
}
bool
test_finalizer_cmp_struct_t(RECT a, RECT b)
{
return a.top == b.top;
}
// Support for checking that we reject NULL finalizer
afun* test_finalizer_rel_null_function()
{
return NULL;
}
void
test_finalizer_rel_size_t_set_errno(size_t i)
{
if (-- gFinalizerTestResources[i] < 0) {
MOZ_NOT_REACHED("Assertion failed");
}
errno = 10;
}
void
reset_errno()
{
errno = 0;
}

View File

@ -1,52 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jsapi.h"
#define EXPORT_CDECL(type) NS_EXPORT type
NS_EXTERN_C
{
EXPORT_CDECL(void) test_finalizer_start(size_t size);
EXPORT_CDECL(void) test_finalizer_stop();
EXPORT_CDECL(bool) test_finalizer_resource_is_acquired(size_t i);
EXPORT_CDECL(size_t) test_finalizer_acq_size_t(size_t i);
EXPORT_CDECL(void) test_finalizer_rel_size_t(size_t i);
EXPORT_CDECL(size_t) test_finalizer_rel_size_t_return_size_t(size_t i);
EXPORT_CDECL(RECT) test_finalizer_rel_size_t_return_struct_t(size_t i);
EXPORT_CDECL(bool) test_finalizer_cmp_size_t(size_t a, size_t b);
EXPORT_CDECL(int32_t) test_finalizer_acq_int32_t(size_t i);
EXPORT_CDECL(void) test_finalizer_rel_int32_t(int32_t i);
EXPORT_CDECL(bool) test_finalizer_cmp_int32_t(int32_t a, int32_t b);
EXPORT_CDECL(int64_t) test_finalizer_acq_int64_t(size_t i);
EXPORT_CDECL(void) test_finalizer_rel_int64_t(int64_t i);
EXPORT_CDECL(bool) test_finalizer_cmp_int64_t(int64_t a, int64_t b);
EXPORT_CDECL(void*) test_finalizer_acq_ptr_t(size_t i);
EXPORT_CDECL(void) test_finalizer_rel_ptr_t(void *i);
EXPORT_CDECL(bool) test_finalizer_cmp_ptr_t(void *a, void *b);
EXPORT_CDECL(char*) test_finalizer_acq_string_t(int i);
EXPORT_CDECL(void) test_finalizer_rel_string_t(char *i);
EXPORT_CDECL(bool) test_finalizer_cmp_string_t(char *a, char *b);
EXPORT_CDECL(void*) test_finalizer_acq_null_t(size_t i);
EXPORT_CDECL(void) test_finalizer_rel_null_t(void *i);
EXPORT_CDECL(bool) test_finalizer_cmp_null_t(void *a, void *b);
EXPORT_CDECL(bool) test_finalizer_null_resource_is_acquired(size_t i);
EXPORT_CDECL(RECT) test_finalizer_acq_struct_t(int i);
EXPORT_CDECL(void) test_finalizer_rel_struct_t(RECT i);
EXPORT_CDECL(bool) test_finalizer_cmp_struct_t(RECT a, RECT b);
typedef void (*afun)(size_t);
EXPORT_CDECL(afun*) test_finalizer_rel_null_function();
EXPORT_CDECL(void) test_finalizer_rel_size_t_set_errno(size_t i);
EXPORT_CDECL(void) reset_errno();
}

View File

@ -23,7 +23,6 @@
* Fredrik Larsson <nossralf@gmail.com>
* Mark Finkle <mark.finkle@gmail.com>, <mfinkle@mozilla.com>
* Dan Witte <dwitte@mozilla.com>
* David Rajchenbach-Teller <dteller@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
@ -40,15 +39,9 @@
* ***** END LICENSE BLOCK ***** */
#include "jsctypes-test.h"
#include "jsapi.h"
#include "nsCRTGlue.h"
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#if defined(XP_WIN)
#define snprintf _snprintf
#endif // defined(XP_WIN)
template <typename T> struct ValueTraits {
static T literal() { return static_cast<T>(109.25); }
@ -410,3 +403,4 @@ test_vector_add_va_cdecl(PRUint8 num_vecs,
}
RECT data_rect = { -1, -2, 3, 4 };

View File

@ -23,7 +23,6 @@
* Fredrik Larsson <nossralf@gmail.com>
* Mark Finkle <mark.finkle@gmail.com>, <mfinkle@mozilla.com>
* Dan Witte <dwitte@mozilla.com>
* David Rajchenbach-Teller <dteller@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

View File

@ -77,18 +77,3 @@ function structural_check_eq_aux(a, b) {
}
);
}
function trigger_gc() {
dump("Triggering garbage-collection");
Components.utils.forceGC();
}
function must_throw(f) {
let has_thrown = false;
try {
f();
} catch (x) {
has_thrown = true;
}
do_check_true(has_thrown);
}

View File

@ -1,445 +0,0 @@
let TEST_SIZE = 100;
function run_test()
{
let library = open_ctypes_test_lib();
let start = library.declare("test_finalizer_start", ctypes.default_abi,
ctypes.void_t,
ctypes.size_t);
let stop = library.declare("test_finalizer_stop", ctypes.default_abi,
ctypes.void_t);
let status = library.declare("test_finalizer_resource_is_acquired",
ctypes.default_abi,
ctypes.bool,
ctypes.size_t);
let released = function(value, witness) {
return witness == undefined;
};
let samples = [];
samples.push(
{
name: "size_t",
acquire: library.declare("test_finalizer_acq_size_t",
ctypes.default_abi,
ctypes.size_t,
ctypes.size_t),
release: library.declare("test_finalizer_rel_size_t",
ctypes.default_abi,
ctypes.void_t,
ctypes.size_t),
compare: library.declare("test_finalizer_cmp_size_t",
ctypes.default_abi,
ctypes.bool,
ctypes.size_t,
ctypes.size_t),
status: status,
released: released
});
samples.push(
{
name: "size_t",
acquire: library.declare("test_finalizer_acq_size_t",
ctypes.default_abi,
ctypes.size_t,
ctypes.size_t),
release: library.declare("test_finalizer_rel_size_t_set_errno",
ctypes.default_abi,
ctypes.void_t,
ctypes.size_t),
compare: library.declare("test_finalizer_cmp_size_t",
ctypes.default_abi,
ctypes.bool,
ctypes.size_t,
ctypes.size_t),
status: status,
released: released
});
samples.push(
{
name: "int32_t",
acquire: library.declare("test_finalizer_acq_int32_t",
ctypes.default_abi,
ctypes.int32_t,
ctypes.size_t),
release: library.declare("test_finalizer_rel_int32_t",
ctypes.default_abi,
ctypes.void_t,
ctypes.int32_t),
compare: library.declare("test_finalizer_cmp_int32_t",
ctypes.default_abi,
ctypes.bool,
ctypes.int32_t,
ctypes.int32_t),
status: status,
released: released
}
);
samples.push(
{
name: "int64_t",
acquire: library.declare("test_finalizer_acq_int64_t",
ctypes.default_abi,
ctypes.int64_t,
ctypes.size_t),
release: library.declare("test_finalizer_rel_int64_t",
ctypes.default_abi,
ctypes.void_t,
ctypes.int64_t),
compare: library.declare("test_finalizer_cmp_int64_t",
ctypes.default_abi,
ctypes.bool,
ctypes.int64_t,
ctypes.int64_t),
status: status,
released: released
}
);
samples.push(
{
name: "ptr",
acquire: library.declare("test_finalizer_acq_ptr_t",
ctypes.default_abi,
ctypes.PointerType(ctypes.void_t),
ctypes.size_t),
release: library.declare("test_finalizer_rel_ptr_t",
ctypes.default_abi,
ctypes.void_t,
ctypes.PointerType(ctypes.void_t)),
compare: library.declare("test_finalizer_cmp_ptr_t",
ctypes.default_abi,
ctypes.bool,
ctypes.void_t.ptr,
ctypes.void_t.ptr),
status: status,
released: released
}
);
samples.push(
{
name: "string",
acquire: library.declare("test_finalizer_acq_string_t",
ctypes.default_abi,
ctypes.char.ptr,
ctypes.int),
release: library.declare("test_finalizer_rel_string_t",
ctypes.default_abi,
ctypes.void_t,
ctypes.char.ptr),
compare: library.declare("test_finalizer_cmp_string_t",
ctypes.default_abi,
ctypes.bool,
ctypes.char.ptr,
ctypes.char.ptr),
status: status,
released: released
}
);
const rect_t = new ctypes.StructType("RECT",
[{ top : ctypes.int32_t },
{ left : ctypes.int32_t },
{ bottom: ctypes.int32_t },
{ right : ctypes.int32_t }]);
samples.push(
{
name: "struct",
acquire: library.declare("test_finalizer_acq_struct_t",
ctypes.default_abi,
rect_t,
ctypes.int),
release: library.declare("test_finalizer_rel_struct_t",
ctypes.default_abi,
ctypes.void_t,
rect_t),
compare: library.declare("test_finalizer_cmp_struct_t",
ctypes.default_abi,
ctypes.bool,
rect_t,
rect_t),
status: status,
released: released
}
);
samples.push(
{
name: "size_t, release returns size_t",
acquire: library.declare("test_finalizer_acq_size_t",
ctypes.default_abi,
ctypes.size_t,
ctypes.size_t),
release: library.declare("test_finalizer_rel_size_t_return_size_t",
ctypes.default_abi,
ctypes.size_t,
ctypes.size_t),
compare: library.declare("test_finalizer_cmp_size_t",
ctypes.default_abi,
ctypes.bool,
ctypes.size_t,
ctypes.size_t),
status: status,
released: function(i, witness) {
return i == witness;
}
}
);
samples.push(
{
name: "size_t, release returns RECT",
acquire: library.declare("test_finalizer_acq_size_t",
ctypes.default_abi,
ctypes.size_t,
ctypes.size_t),
release: library.declare("test_finalizer_rel_size_t_return_struct_t",
ctypes.default_abi,
rect_t,
ctypes.size_t),
compare: library.declare("test_finalizer_cmp_size_t",
ctypes.default_abi,
ctypes.bool,
ctypes.size_t,
ctypes.size_t),
status: status,
released: function(i, witness) {
return witness.top == i
&& witness.bottom == i
&& witness.left == i
&& witness.right == i;
}
}
);
samples.push(
{
name: "using null",
acquire: library.declare("test_finalizer_acq_null_t",
ctypes.default_abi,
ctypes.PointerType(ctypes.void_t),
ctypes.size_t),
release: library.declare("test_finalizer_rel_null_t",
ctypes.default_abi,
ctypes.void_t,
ctypes.PointerType(ctypes.void_t)),
status: library.declare("test_finalizer_null_resource_is_acquired",
ctypes.default_abi,
ctypes.bool,
ctypes.size_t),
compare: library.declare("test_finalizer_cmp_null_t",
ctypes.default_abi,
ctypes.bool,
ctypes.void_t.ptr,
ctypes.void_t.ptr),
released: released
}
);
let tester = new ResourceTester(start, stop);
samples.forEach(
function(sample) {
dump("Executing finalization test for data "+sample.name+"\n");
tester.launch(TEST_SIZE, test_executing_finalizers, sample);
tester.launch(TEST_SIZE, test_do_not_execute_finalizers_on_referenced_stuff, sample);
tester.launch(TEST_SIZE, test_executing_dispose, sample);
tester.launch(TEST_SIZE, test_executing_forget, sample);
tester.launch(TEST_SIZE, test_result_dispose, sample);
}
);
/*
* Following test deactivated: Cycle collection never takes place
* (see bug 727371)
tester.launch(TEST_SIZE, test_cycles, samples[0]);
*/
library.close();
}
// If only I could have Promises to test this :)
// There is only so much we can do at this stage,
// if we want to avoid tests overlapping.
function test_cycles(size, tc) {
// Now, restart this with unreferenced cycles
for (i = 0; i < size/2; ++i) {
let a = {
a: ctypes.CDataFinalizer(tc.acquire(i*2), tc.release),
b: {
b: ctypes.CDataFinalizer(tc.acquire(i*2+1), tc.release)
}
};
a.b.a = a;
}
do_test_pending();
Components.utils.schedulePreciseGC(
function() {
// Check that _something_ has been finalized
do_check_true(count_finalized(size, tc) > 0);
do_test_finished();
}
);
do_timeout(10000, do_throw);
}
function count_finalized(size, tc) {
let finalizedItems = 0;
for (let i = 0; i < size; ++i) {
if (!tc.status(i)) {
++finalizedItems;
}
}
return finalizedItems;
}
/**
* Test:
* - that (some) finalizers are executed;
* - that no finalizer is executed twice (this is done on the C side).
*/
function test_executing_finalizers(size, tc)
{
dump("test_executing_finalizers\n");
// Allocate |size| items without references
for (let i = 0; i < size; ++i) {
ctypes.CDataFinalizer(tc.acquire(i), tc.release);
}
trigger_gc(); // This should trigger some finalizations, hopefully all
// Check that _something_ has been finalized
do_check_true(count_finalized(size, tc) > 0);
}
/**
* Check that
* - |dispose| returns the proper result
*/
function test_result_dispose(size, tc) {
dump("test_result_dispose " + tc.name + "\n");
let ref = [];
// Allocate |size| items with references
for (let i = 0; i < size; ++i) {
ref.push(ctypes.CDataFinalizer(tc.acquire(i), tc.release));
}
do_check_eq(count_finalized(size, tc), 0);
for (i = 0; i < size; ++i) {
let witness = ref[i].dispose();
ref[i] = null;
if (!tc.released(i, witness)) {
do_check_true(tc.released(i, witness));
}
}
}
/**
* Check that
* - |dispose| is executed properly
* - finalizers are not executed after |dispose|
*/
function test_executing_dispose(size, tc)
{
dump("test_executing_dispose\n");
let ref = [];
// Allocate |size| items with references
for (let i = 0; i < size; ++i) {
ref.push(ctypes.CDataFinalizer(tc.acquire(i), tc.release));
}
do_check_eq(count_finalized(size, tc), 0);
// Dispose of everything and make sure that everything has been cleaned up
ref.forEach(
function(v) {
v.dispose();
}
);
do_check_eq(count_finalized(size, tc), size);
// Remove references
ref = [];
// Re-acquireialize data and make sure that everything has been reinialized
for (i = 0; i < size; ++i) {
tc.acquire(i);
}
do_check_eq(count_finalized(size, tc), 0);
// Attempt to trigger finalizations, ensure that they do not take place
trigger_gc();
do_check_eq(count_finalized(size, tc), 0);
}
/**
* Check that
* - |forget| does not dispose
* - |forget| has the right content
* - finalizers are not executed after |forget|
*/
function test_executing_forget(size, tc)
{
dump("test_executing_forget\n");
let ref = [];
// Allocate |size| items with references
for (let i = 0; i < size; ++i) {
let original = tc.acquire(i);
let finalizer = ctypes.CDataFinalizer(original, tc.release);
ref.push(
{
original: original,
finalizer: finalizer
}
);
do_check_true(tc.compare(original, finalizer));
}
do_check_eq(count_finalized(size, tc), 0);
// Forget everything, making sure that we recover the original info
ref.forEach(
function(v) {
let original = v.original;
let recovered = v.finalizer.forget();
// Note: Cannot use do_check_eq on Uint64 et al.
do_check_true(tc.compare(original, recovered));
do_check_eq(original.constructor, recovered.constructor);
}
);
// Also make sure that we have not performed any clean up
do_check_eq(count_finalized(size, tc), 0);
// Remove references
ref = [];
// Attempt to trigger finalizations, ensure that they have no effect
trigger_gc();
do_check_eq(count_finalized(size, tc), 0);
}
/**
* Check that finalizers are not executed
*/
function test_do_not_execute_finalizers_on_referenced_stuff(size, tc)
{
dump("test_do_not_execute_finalizers_on_referenced_stuff\n");
let ref = [];
// Allocate |size| items without references
for (let i = 0; i < size; ++i) {
ref.push(ctypes.CDataFinalizer(tc.acquire(i), tc.release));
}
trigger_gc(); // This might trigger some finalizations, but it should not
// Check that _nothing_ has been finalized
do_check_eq(count_finalized(size, tc), 0);
// Clean up manually, lest leftover data interferes with following tests
ref.forEach(function(v) {
v.dispose();
});
ref = [];
trigger_gc();
}

View File

@ -1,122 +0,0 @@
try {
// We might be running without privileges, in which case it's up to the
// harness to give us the 'ctypes' object.
Components.utils.import("resource://gre/modules/ctypes.jsm");
} catch(e) {
}
let acquire, dispose, reset_errno, dispose_errno;
function run_test()
{
let library = open_ctypes_test_lib();
let start = library.declare("test_finalizer_start", ctypes.default_abi,
ctypes.void_t,
ctypes.size_t);
let stop = library.declare("test_finalizer_stop", ctypes.default_abi,
ctypes.void_t);
let tester = new ResourceTester(start, stop);
acquire = library.declare("test_finalizer_acq_size_t",
ctypes.default_abi,
ctypes.size_t,
ctypes.size_t);
dispose = library.declare("test_finalizer_rel_size_t",
ctypes.default_abi,
ctypes.void_t,
ctypes.size_t);
reset_errno = library.declare("reset_errno",
ctypes.default_abi,
ctypes.void_t);
dispose_errno = library.declare("test_finalizer_rel_size_t_set_errno",
ctypes.default_abi,
ctypes.void_t,
ctypes.size_t);
tester.launch(10, test_to_string);
tester.launch(10, test_to_source);
tester.launch(10, test_to_int);
tester.launch(10, test_errno);
}
/**
* Check that toString succeeds before/after forget/dispose.
*/
function test_to_string()
{
do_print("Starting test_to_string");
let a = ctypes.CDataFinalizer(acquire(0), dispose);
do_check_eq(a.toString(), "0");
a.forget();
do_check_eq(a.toString(), "[CDataFinalizer - empty]");
a = ctypes.CDataFinalizer(acquire(0), dispose);
a.dispose();
do_check_eq(a.toString(), "[CDataFinalizer - empty]");
}
/**
* Check that toSource succeeds before/after forget/dispose.
*/
function test_to_source()
{
do_print("Starting test_to_source");
let value = acquire(0);
let a = ctypes.CDataFinalizer(value, dispose);
do_check_eq(a.toSource(),
"ctypes.CDataFinalizer("
+ ctypes.size_t(value).toSource()
+", "
+dispose.toSource()
+")");
value = null;
a.forget();
do_check_eq(a.toSource(), "ctypes.CDataFinalizer()");
a = ctypes.CDataFinalizer(acquire(0), dispose);
a.dispose();
do_check_eq(a.toSource(), "ctypes.CDataFinalizer()");
}
/**
* Test conversion to int32
*/
function test_to_int()
{
let value = 2;
let wrapped, converted, finalizable;
wrapped = ctypes.int32_t(value);
finalizable= ctypes.CDataFinalizer(acquire(value), dispose);
converted = ctypes.int32_t(finalizable);
structural_check_eq(converted, wrapped);
structural_check_eq(converted, ctypes.int32_t(finalizable.forget()));
wrapped = ctypes.int64_t(value);
converted = ctypes.int64_t(ctypes.CDataFinalizer(acquire(value),
dispose));
structural_check_eq(converted, wrapped);
}
/**
* Test that dispose can change errno but finalization cannot
*/
function test_errno(size)
{
reset_errno();
do_check_eq(ctypes.errno, 0);
let finalizable = ctypes.CDataFinalizer(acquire(3), dispose_errno);
finalizable.dispose();
do_check_eq(ctypes.errno, 10);
reset_errno();
do_check_eq(ctypes.errno, 0);
for (let i = 0; i < size; ++i) {
finalizable = ctypes.CDataFinalizer(acquire(i), dispose_errno);
}
trigger_gc();
do_check_eq(ctypes.errno, 0);
}

View File

@ -1,156 +0,0 @@
try {
// We might be running without privileges, in which case it's up to the
// harness to give us the 'ctypes' object.
Components.utils.import("resource://gre/modules/ctypes.jsm");
} catch(e) {
}
let acquire, dispose, null_dispose, compare;
function run_test()
{
let library = open_ctypes_test_lib();
let start = library.declare("test_finalizer_start", ctypes.default_abi,
ctypes.void_t,
ctypes.size_t);
let stop = library.declare("test_finalizer_stop", ctypes.default_abi,
ctypes.void_t);
let tester = new ResourceTester(start, stop);
acquire = library.declare("test_finalizer_acq_size_t",
ctypes.default_abi,
ctypes.size_t,
ctypes.size_t);
dispose = library.declare("test_finalizer_rel_size_t",
ctypes.default_abi,
ctypes.void_t,
ctypes.size_t);
compare = library.declare("test_finalizer_cmp_size_t",
ctypes.default_abi,
ctypes.bool,
ctypes.size_t,
ctypes.size_t);
let type_afun = ctypes.FunctionType(ctypes.default_abi,
ctypes.void_t,
[ctypes.size_t]).ptr;
let null_dispose_maker =
library.declare("test_finalizer_rel_null_function",
ctypes.default_abi,
type_afun
);
null_dispose = null_dispose_maker();
tester.launch(10, test_double_dispose);
tester.launch(10, test_finalize_bad_construction);
tester.launch(10, test_null_dispose);
tester.launch(10, test_pass_disposed);
}
/**
* Testing construction of finalizers with wrong arguments.
*/
function test_finalize_bad_construction() {
// First argument does not match second
must_throw(function() { ctypes.CDataFinalizer({}, dispose); });
must_throw(function() { ctypes.CDataFinalizer(dispose, dispose); });
// Not enough arguments
must_throw(function() { ctypes.CDataFinalizer(init(0)); });
// Too many arguments
must_throw(function() { ctypes.CDataFinalizer(init(0), dispose, dispose); });
// Second argument is null
must_throw(function() { ctypes.CDataFinalizer(init(0), null); });
// Second argument is undefined
must_throw(function() {
let a;
ctypes.CDataFinalizer(init(0), a);
});
}
/**
* Test that forget/dispose can only take place once.
*/
function test_double_dispose() {
function test_one_combination(i, a, b) {
let v = ctypes.CDataFinalizer(acquire(i), dispose);
a(v);
must_throw(function() { b(v); } );
}
let call_dispose = function(v) {
v.dispose();
};
let call_forget = function(v) {
v.forget();
};
test_one_combination(0, call_dispose, call_dispose);
test_one_combination(1, call_dispose, call_forget);
test_one_combination(2, call_forget, call_dispose);
test_one_combination(3, call_forget, call_forget);
}
/**
* Test that nothing (too) bad happens when the finalizer is NULL
*/
function test_null_dispose()
{
let exception;
exception = false;
try {
let v = ctypes.CDataFinalizer(acquire(0), null_dispose);
} catch (x) {
exception = true;
}
do_check_true(exception);
}
/**
* Test that conversion of a disposed/forgotten CDataFinalizer to a C
* value fails nicely.
*/
function test_pass_disposed()
{
let exception, v;
exception = false;
v = ctypes.CDataFinalizer(acquire(0), dispose);
do_check_true(compare(v, 0));
v.forget();
try {
compare(v, 0);
} catch (x) {
exception = true;
}
do_check_true(exception);
exception = false;
v = ctypes.CDataFinalizer(acquire(0), dispose);
do_check_true(compare(v, 0));
v.dispose();
try {
compare(v, 0);
} catch (x) {
exception = true;
}
do_check_true(exception);
exception = false;
try {
ctypes.int32_t(ctypes.CDataFinalizer(v, dispose));
} catch (x) {
exception = true;
}
do_check_true(exception);
}

View File

@ -4,10 +4,6 @@ tail =
[test_errno.js]
[test_finalizer.js]
[test_finalizer_shouldfail.js]
[test_finalizer_shouldaccept.js]
[test_jsctypes.js]
# Bug 676989: test fails consistently on Android
fail-if = os == "android"