diff --git a/source/qjs_chipmunk.c b/source/qjs_chipmunk.c index ac239f70..94d38ec6 100644 --- a/source/qjs_chipmunk.c +++ b/source/qjs_chipmunk.c @@ -28,31 +28,115 @@ static JSValue js_groove; static JSValue js_slide; static JSValue js_ratchet; +static JSClassID js_cpSpace_class_id; +static JSClassID js_cpBody_class_id; +static JSClassID js_cpShape_class_id; +static JSClassID js_cpConstraint_class_id; + +static void BodyFreeWrap(cpSpace *space, cpBody *body, void *unused) { + cpSpaceRemoveBody(space, body); +} + +static void PostBodyFree(cpBody *body, cpSpace *space) { + cpSpaceAddPostStepCallback(space, BodyFreeWrap, body, NULL); +} + +static void ShapeFreeWrap(cpSpace *space, cpShape *shape, void *unused) { + cpSpaceRemoveShape(space, shape); +} + +static void PostShapeFree(cpShape *shape, cpSpace *space) { + cpSpaceAddPostStepCallback(space, ShapeFreeWrap, shape, NULL); +} + +static void ConstraintFreeWrap(cpSpace *space, cpConstraint *constraint, void *unused) { + cpSpaceRemoveConstraint(space, constraint); +} + +static void PostConstraintFree(cpConstraint *constraint, cpSpace *space) { + cpSpaceAddPostStepCallback(space, ConstraintFreeWrap, constraint, NULL); +} + +static void js_cpSpace_finalizer(JSRuntime *rt, JSValue val) +{ + printf("freeing space\n"); + cpSpace *space = JS_GetOpaque(val, js_cpSpace_class_id); + cpSpaceEachShape(space, PostShapeFree, NULL); + cpSpaceEachConstraint(space, PostConstraintFree, NULL); + cpSpaceEachBody(space, PostBodyFree, NULL); + + JSValue *cb = (JSValue*)cpShapeGetUserData(space); + JS_FreeValueRT(rt, *cb); + free(cb); + cpSpaceFree(space); +} + +static void js_cpBody_finalizer(JSRuntime *rt, JSValue val) +{ + printf("freeing body\n"); + cpBody *body = JS_GetOpaque(val, js_cpBody_class_id); + + cpSpace *space = cpBodyGetSpace(body); + if (space) + cpSpaceRemoveBody(space,body); + + cpBodyEachShape(body, PostShapeFree, space); + cpBodyEachConstraint(body, PostConstraintFree, space); + + JSValue *cb = (JSValue *)cpBodyGetUserData(body); + JS_FreeValueRT(rt, *cb); + free(cb); + + cpBodyFree(body); +} + +static void js_cpShape_finalizer(JSRuntime *rt, JSValue val) +{ + printf("freeing shape\n"); + cpShape *shape = JS_GetOpaque(val, js_cpShape_class_id); + + cpSpace *space = cpShapeGetSpace(shape); + if (space) + cpSpaceRemoveShape(space, shape); + + JSValue *cb = (JSValue*)cpShapeGetUserData(shape); + JS_FreeValueRT(rt, *cb); + free(cb); + + cpShapeFree(shape); +} + +static void js_cpConstraint_finalizer(JSRuntime *rt, JSValue val) +{ + printf("freeing constraint\n"); + cpConstraint *joint = JS_GetOpaque(val, js_cpConstraint_class_id); + + cpSpace *space = cpConstraintGetSpace(joint); + if (space) + cpSpaceRemoveConstraint(space,joint); + + JSValue *cb = (JSValue*)cpConstraintGetUserData(joint); + JS_FreeValueRT(rt, *cb); + free(cb); + + cpConstraintFree(joint); +} + #define CPCLASS(TYPE) \ -static JSClassID js_##TYPE##_class_id; \ -static void js_##TYPE##_finalizer(JSRuntime *rt, JSValue val) { \ - TYPE *cp = JS_GetOpaque(val, js_##TYPE##_class_id); \ - if(cp) { \ - /* Free the stored JSValue pointer (user data). */ \ - JSValue* cb = (JSValue*)TYPE##GetUserData(cp); \ - if (cb) { \ - JS_FreeValueRT(rt, *cb); \ - free(cb); \ - } \ - /* Then free/destroy the Chipmunk object if needed. */ \ - /* For shapes, bodies, constraints, the destroy call is done externally if needed. */ \ - } \ -} \ static inline TYPE *js2##TYPE(JSContext *js, JSValue v) { \ return JS_GetOpaque(v, js_##TYPE##_class_id); \ } \ static inline JSValue TYPE##2js(JSContext *js, TYPE *cp) { \ - /* Return the already-associated JSValue. */ \ return JS_DupValue(js, *(JSValue*)TYPE##GetUserData(cp)); \ } \ +static inline void js_##TYPE##_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func) { \ + TYPE *cp = JS_GetOpaque(val, js_##TYPE##_class_id); \ + JS_MarkValue(rt, *(JSValue*)TYPE##GetUserData(cp), mark_func); \ +} \ static JSClassDef js_##TYPE##_class = { \ #TYPE, \ - .finalizer = js_##TYPE##_finalizer \ + .finalizer = js_##TYPE##_finalizer, \ + .gc_mark = js_##TYPE##_mark, \ }; \ CPCLASS(cpShape) @@ -60,8 +144,6 @@ CPCLASS(cpSpace) CPCLASS(cpBody) CPCLASS(cpConstraint) -// Helper conversions - static inline cpVect js2cpvect(JSContext *js, JSValue v) { cpVect ret; JSValue x = JS_GetPropertyStr(js, v, "x"); @@ -132,7 +214,7 @@ static JSValue js_make_cpSpace(JSContext *js, JSValueConst this_val, int argc, J // Link the JS object to the Chipmunk space JSValue *cb = malloc(sizeof(*cb)); - *cb = obj; + *cb = JS_DupValue(js,obj); cpSpaceSetUserData(space, cb); return obj; @@ -231,23 +313,20 @@ static JSValue js_space_step (JSContext *js, JSValueConst this_val, int argc, JS static JSValue js_cpSpace_add_body (JSContext *js, JSValueConst this_val, int argc, JSValue *argv) { cpSpace *space = js2cpSpace(js, this_val); - if (!space) return JS_EXCEPTION; + if (!space) return JS_ThrowReferenceError(js, "Not a space."); - // Create a dynamic body (so mass, moment, etc. are settable). - cpBody *body = cpBodyNew(0, 0); + cpBody *body = cpBodyNew(1,1); cpBodySetType(body, CP_BODY_TYPE_DYNAMIC); - // Make a JS object for the body JSValue obj = JS_NewObjectClass(js, js_cpBody_class_id); JS_SetOpaque(obj, body); - // Store backref JSValue *cb = malloc(sizeof(*cb)); - *cb = obj; + *cb = JS_DupValue(js,obj); cpBodySetUserData(body, cb); - // Add to space cpSpaceAddBody(space, body); + printf("finished adding body at %p\n", body); return obj; } @@ -398,7 +477,7 @@ static JSValue js_body_add_circle_shape(JSContext *js, JSValueConst this_val, in JS_SetPrototype(js, obj, circle_proto); JSValue *cb = malloc(sizeof(*cb)); - *cb = obj; + *cb = JS_DupValue(js,obj); cpShapeSetUserData(shape, cb); // We do not call cpSpaceAddShape here. Typically you'd do that explicitly: space.addShape(...) @@ -417,7 +496,7 @@ static JSValue js_body_add_segment_shape(JSContext *js, JSValueConst this_val, i JS_SetPrototype(js, obj, segment_proto); JSValue *cb = malloc(sizeof(*cb)); - *cb = obj; + *cb = JS_DupValue(js,obj); cpShapeSetUserData(shape, cb); return obj; @@ -436,7 +515,7 @@ static JSValue js_body_add_poly_shape(JSContext *js, JSValueConst this_val, int JS_SetPrototype(js, obj, poly_proto); JSValue *cb = malloc(sizeof(*cb)); - *cb = obj; + *cb = JS_DupValue(js,obj); cpShapeSetUserData(shape, cb); return obj; @@ -702,7 +781,7 @@ static JSValue prep_constraint(JSContext *js, cpConstraint *c) // Allocate space for the back-reference JSValue *cb = malloc(sizeof(JSValue)); - *cb = ret; + *cb = JS_DupValue(js,ret); cpConstraintSetUserData(c, cb); return ret; @@ -980,7 +1059,7 @@ static JSValue js_joint_motor (JSContext *js, JSValueConst this_val, int argc, J // Space methods static const JSCFunctionListEntry js_cpSpace_funcs[] = { - JS_CFUNC_DEF("add_body", 0, js_cpSpace_add_body), + JS_CFUNC_DEF("body", 0, js_cpSpace_add_body), JS_CFUNC_DEF("step", 1, js_space_step), JS_CGETSET_DEF("gravity", js_cpSpace_get_Gravity, js_cpSpace_set_Gravity), JS_CGETSET_DEF("iterations", js_cpSpace_get_Iterations, js_cpSpace_set_Iterations),