зеркало из https://github.com/mozilla/gecko-dev.git
Bug 745233 - Back out 33e485d0e23b, 26eb08593f89, ce72679ffb95, 10b783e85c1e (bug 720771, bug 742384) because of Linux64 PGO crashes
This commit is contained in:
Родитель
ad61c52398
Коммит
e938aaf083
|
@ -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,14 +1273,6 @@ 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;
|
||||
}
|
||||
if (JSVAL_IS_BOOLEAN(val)) {
|
||||
|
@ -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
|
||||
*******************************************************************************/
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -53,7 +53,6 @@ NO_DIST_INSTALL = 1
|
|||
|
||||
CPPSRCS = jsctypes-test.cpp \
|
||||
jsctypes-test-errno.cpp \
|
||||
jsctypes-test-finalizer.cpp \
|
||||
$(NULL)
|
||||
|
||||
LOCAL_INCLUDES = \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -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 };
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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"
|
||||
|
|
Загрузка…
Ссылка в новой задаче