Subj : JS_GetArrayLength() crashing (garbage collection issue) To : netscape.public.mozilla.jseng From : smaraux Date : Thu Feb 24 2005 06:26 am Hello, using sipdermonkey 1.5rc6 : I got a script : function initialize() {mfArrayTest[0] = dummy;} function action(){mfArrayTest[0] = dummy;} I define mfArrayTest[0] as an object with such private data : class dataHolder { public: dataHolder() { pChildrenArray = NULL; } JSObject *pChildrenArray; }; Which is an holder for an array of JSObjects. dummy is a double value. When I call initialize, everything is OK. Then I call JS_GC() (this code is an example of bigger script including much more actions in initialize). but when I called action (the second call to setElement of the array), It hangs on setElement of the array, when I call for JS_GetArrayLength to know if array is big enough. If I don't garbage collect after initalize, everything is OK. I don't see what is garbage collected and make this crash (mfArrayTest and dummy are permanent properties, and I tried with or without adding the new array element created in setElement to a GC Root). I woudl be glad to understand what I did wrong, and I also wonder if I must add array object to a GC root if I want to keep it between calls to initialize() and action(), and if I must add new objects children of array to GC root (well, I guess that yes or they won't be valid in next call ?). here is the complete code : //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- #include "stdio.h" #include "stdlib.h" #include "string.h" #define XP_WIN #include "includes/jsapi.h" class dataHolder { public: dataHolder() { pChildrenArray = NULL; } JSObject *pChildrenArray; }; #define SETUP_JS_CLASS(jsClass, className, classFlags, addProp, delProp, getProp, setProp, enumProp, resolveProp, classConvert, classFinalize) \ jsClass.name = className; \ jsClass.flags = classFlags; \ jsClass.addProperty = addProp; \ jsClass.delProperty = delProp; \ jsClass.getProperty = getProp; \ jsClass.setProperty = setProp; \ jsClass.enumerate = enumProp; \ jsClass.resolve = resolveProp; \ jsClass.convert = classConvert; \ jsClass.finalize = classFinalize; static JSBool arrayConstructor(JSContext *pJSContext, JSObject *pJSObject, uintN argc, jsval *pJSVal_Argv, jsval *pJSReturnVal) { dataHolder *pDataHolder = new dataHolder(); pDataHolder->pChildrenArray = JS_NewArrayObject(pJSContext, (jsint) argc, pJSVal_Argv); JS_AddRoot(pJSContext, pDataHolder->pChildrenArray); JS_SetPrivate(pJSContext, pJSObject, pDataHolder); *pJSReturnVal = OBJECT_TO_JSVAL(pJSObject); return pJSObject == 0 ? JS_FALSE : JS_TRUE; } JSBool array_setElement(JSContext *pJSContext, JSObject *pJSObject, jsval id, jsval *pJSSetVal) { jsval elt_val; JSBool ret; bool val_changed = 1; uint32 u4Index= JSVAL_TO_INT(id); uint32 u4ChildrenArrayLength; dataHolder *pDataHolder = (dataHolder *) JS_GetPrivate(pJSContext, pJSObject); ret = JS_GetArrayLength(pJSContext, pDataHolder->pChildrenArray, &u4ChildrenArrayLength); if (ret==JS_FALSE) return JS_FALSE; if (u4Index>=u4ChildrenArrayLength) { ret = JS_SetArrayLength(pJSContext, pDataHolder->pChildrenArray, u4ChildrenArrayLength+1); if (ret==JS_FALSE) return JS_FALSE; ret = JS_GetArrayLength(pJSContext, pDataHolder->pChildrenArray, &u4ChildrenArrayLength); while (u4ChildrenArrayLengthpChildrenArray, u4ChildrenArrayLength, &a_val); u4ChildrenArrayLength++; } // to allow deletion of previous item //jsval replacedElement; //ret = JS_GetElement(pJSContext, pJSObject, u4Index, &replacedElement); //if (ret == JS_FALSE) return JS_FALSE; //and assign // GC Thing test is done in addRoot function //JS_AddRoot(pJSContext, pJSSetVal); JS_SetElement(pJSContext, pDataHolder->pChildrenArray, u4Index, pJSSetVal); // remove GC protection on old element. //JS_RemoveRoot(pJSContext, JSVAL_TO_OBJECT(replacedElement)); return JS_TRUE; } ret = JS_GetElement(pJSContext, pDataHolder->pChildrenArray, (jsint) u4Index, &elt_val); if (ret==JS_FALSE) return JS_FALSE; JS_SetElement(pJSContext, pDataHolder->pChildrenArray, u4Index, pJSSetVal); return JS_TRUE; } static void array_finalize(JSContext *pJSContext, JSObject *pJSObject) { dataHolder *pDataHolder = (dataHolder *) JS_GetPrivate(pJSContext, pJSObject); if (!pDataHolder) return; delete pDataHolder; } /************************************************************************/ /* Error handling */ /************************************************************************/ static void errorCallBack(JSContext *pJSContext, const char *msg, JSErrorReport *jserr) { printf("JSError : %s\n",msg); } static JSClass globalClass; static JSClass floatArrayClass; int main() { /*set up global JS variables, including global and custom objects */ JSRuntime *rt; JSContext *cx; /* initialize the JS run time, and return result in rt */ rt = JS_NewRuntime(8L * 1024L * 1024L); /* if rt does not have a value, end the program here */ if (!rt) return 1; /* create a context and associate it with the JS run time */ cx = JS_NewContext(rt, 8192); /* if cx does not have a value, end the program here */ if (cx == NULL) return 1; SETUP_JS_CLASS(globalClass, "global", 0, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub); SETUP_JS_CLASS(floatArrayClass , "floatArray", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, array_setElement, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, array_finalize); JSObject* pGlobal = JS_NewObject(cx, &globalClass, 0, 0 ); JS_InitStandardClasses(cx, pGlobal); //JS_DefineFunctions(cx, pGlobal, globalFunctions ); JS_DefineProperty(cx, pGlobal, "FALSE", BOOLEAN_TO_JSVAL(0), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT ); JS_DefineProperty(cx, pGlobal, "TRUE", BOOLEAN_TO_JSVAL(1), 0, 0, JSPROP_READONLY | JSPROP_PERMANENT ); JS_InitClass(cx, pGlobal, 0, &floatArrayClass, &arrayConstructor, 0, 0, 0, 0, 0); JS_SetErrorReporter(cx, errorCallBack); jsval dummyVal = DOUBLE_TO_JSVAL(123456789); JS_DefineProperty(cx, pGlobal, "dummy", dummyVal, 0, NULL, JSPROP_PERMANENT ); dataHolder* jsf = new dataHolder(); jsf->pChildrenArray = JS_NewArrayObject(cx, 0, NULL); if (JS_SetArrayLength(cx, jsf->pChildrenArray, 0) != JS_TRUE) return JSVAL_NULL; JSObject* pJSObject = JS_NewObject(cx, &floatArrayClass, 0, pGlobal); JS_AddRoot(cx, &pJSObject); JS_SetPrivate(cx, pJSObject, jsf); jsval arrayVal = OBJECT_TO_JSVAL(pJSObject); JS_DefineProperty(cx, pGlobal, "arrayTest", arrayVal, 0, NULL, JSPROP_PERMANENT ); jsval rval; char bob[256]; bob[0] = 0; sprintf(bob, "function initialize() {arrayTest[0] = dummy;} function action(){arrayTest[0] = dummy;}"); jsval ret = JS_EvaluateScript(cx, pGlobal, bob, strlen(bob), 0, 0, &rval); if (ret == JS_FALSE) { return -1; } // call initialize jsval fval; if (JS_LookupProperty(cx, pGlobal, "initialize", &fval)) if (! JSVAL_IS_VOID(fval)) JS_CallFunctionValue(cx, pGlobal, fval, 0, NULL, &rval); JS_GC(cx); char bob2[256]; sscanf("%c", bob2); if (JS_LookupProperty(cx, pGlobal, "action", &fval)) if (! JSVAL_IS_VOID(fval)) JS_CallFunctionValue(cx, pGlobal, fval, 0, NULL, &rval); JS_GC(cx); // call another method // destroy JS_DestroyContext(cx); /* Before exiting the application, free the JS run time */ JS_DestroyRuntime(rt); } .