You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

203 lines
5.2 KiB

/*
* Testcase for object finalizer manipulation.
*/
/*===
*** test_basic (duk_safe_call)
top: 1
finalizer name: basic_finalizer
top: 1
before set top 0
basic_finalizer, arg: target object
after set top 0
==> rc=0, result='undefined'
*** test_recursive_finalizer (duk_safe_call)
top: 1
finalizer.bar=321
c_finalizer.quux=234
before set top 0
finalizing obj, obj.foo: 123
c_finalizer, argument bar: 321
after set top 0
before explicit gc
after explicit gc
==> rc=0, result='undefined'
*** test_get_nonobject (duk_safe_call)
read finalizer: undefined
==> rc=0, result='undefined'
*** test_set_nonobject (duk_safe_call)
==> rc=1, result='TypeError: invalid base value'
*** test_finalizer_loop (duk_safe_call)
before pop
after pop
before forced gc
finalizer called
after forced gc
==> rc=0, result='undefined'
===*/
static duk_ret_t basic_finalizer(duk_context *ctx) {
printf("basic_finalizer, arg: %s\n", duk_safe_to_string(ctx, -1));
return 0;
}
static duk_ret_t test_basic(duk_context *ctx) {
/* Object to be finalized, special toString() */
duk_push_object(ctx);
duk_eval_string(ctx, "(function() { return 'target object'; })");
duk_put_prop_string(ctx, -2, "toString");
/* [ target ] */
/* Set finalizer */
duk_push_c_function(ctx, basic_finalizer, 1);
duk_push_string(ctx, "basic_finalizer");
duk_put_prop_string(ctx, -2, "myName");
duk_set_finalizer(ctx, -2);
/* [ target ] */
printf("top: %ld\n", (long) duk_get_top(ctx));
/* Get finalizer and check it is correct */
duk_get_finalizer(ctx, -1);
duk_get_prop_string(ctx, -1, "myName");
printf("finalizer name: %s\n", duk_to_string(ctx, -1));
duk_pop_2(ctx);
printf("top: %ld\n", (long) duk_get_top(ctx));
/* [ target ] */
printf("before set top 0\n");
duk_set_top(ctx, 0);
printf("after set top 0\n");
/* [ ] */
return 0;
}
static duk_ret_t c_finalizer(duk_context *ctx) {
duk_get_prop_string(ctx, 0, "bar");
printf("c_finalizer, argument bar: %s\n", duk_safe_to_string(ctx, -1));
return 0;
}
static duk_ret_t test_recursive_finalizer(duk_context *ctx) {
/* Object to be finalized */
duk_push_object(ctx);
duk_push_int(ctx, 123);
duk_put_prop_string(ctx, -2, "foo");
/* Ecmascript finalizer */
duk_eval_string(ctx, "(function (obj) { print('finalizing obj, obj.foo:', obj.foo); })");
duk_push_int(ctx, 321);
duk_put_prop_string(ctx, -2, "bar");
/* [ target finalizer ] */
/* Break the function <-> prototype reference loop so that the
* Ecmascript finalizer is not in a reference loop and gets
* collected by refcounting.
*
* (Note that 'prototype' is not configurable so we can't
* delete it.)
*/
duk_push_string(ctx, "dummy");
duk_put_prop_string(ctx, -2, "prototype");
/* Add a Duktape/C finalizer for the Ecmascript finalizer to
* exercise both Duktape/C finalizers and recursive finalization
*/
duk_push_c_function(ctx, c_finalizer, 1);
duk_push_int(ctx, 234);
duk_put_prop_string(ctx, -2, "quux");
duk_set_finalizer(ctx, -2);
/* [ target(foo:123) finalizer(bar:321) ] */
/* Set Ecmascript finalizer to original object */
duk_set_finalizer(ctx, -2);
/* [ target(foo:123) ] */
printf("top: %ld\n", (long) duk_get_top(ctx));
/* Read back the finalizer and the finalizer's finalizer */
duk_get_finalizer(ctx, -1); /* target's finalizer */
duk_get_finalizer(ctx, -1); /* finalizer's finalizer */
/* [ target(foo:123) finalizer(bar:321) c_finalizer(quux:234) ] */
duk_get_prop_string(ctx, -2, "bar");
printf("finalizer.bar=%s\n", duk_safe_to_string(ctx, -1));
duk_pop(ctx);
duk_get_prop_string(ctx, -1, "quux");
printf("c_finalizer.quux=%s\n", duk_safe_to_string(ctx, -1));
duk_pop(ctx);
/* [ target(foo:123) finalizer(bar:321) c_finalizer(quux:234) ] */
printf("before set top 0\n");
duk_set_top(ctx, 0); /* causes finalization */
printf("after set top 0\n");
/* [ ] */
/* Explicit GC (just in case e.g. a reference loop prevented collection) */
printf("before explicit gc\n");
duk_gc(ctx, 0);
printf("after explicit gc\n");
return 0;
}
static duk_ret_t test_get_nonobject(duk_context *ctx) {
duk_push_int(ctx, 123);
duk_get_finalizer(ctx, -1);
printf("read finalizer: %s\n", duk_safe_to_string(ctx, -1));
return 0;
}
static duk_ret_t test_set_nonobject(duk_context *ctx) {
duk_push_int(ctx, 123);
duk_push_int(ctx, 321);
duk_set_finalizer(ctx, -2);
printf("never here\n");
return 0;
}
static duk_ret_t test_finalizer_loop(duk_context *ctx) {
/* Setup a finalizer loop: the finalizer of a finalizer is the
* finalizer itself. The finalizer won't be called recursively.
*/
duk_eval_string(ctx, "(function (obj) { print('finalizer called'); })");
duk_dup(ctx, -1);
duk_set_finalizer(ctx, -2);
printf("before pop\n");
duk_pop(ctx);
printf("after pop\n");
/* The finalizer participates in two circular references so it won't
* be collected until mark-and-sweep happens. The first circular
* reference is the function<->prototype loop. The second circular
* reference is the finalizer reference which points to the object
* itself.
*/
printf("before forced gc\n");
duk_gc(ctx, 0);
printf("after forced gc\n");
return 0;
}
void test(duk_context *ctx) {
TEST_SAFE_CALL(test_basic);
TEST_SAFE_CALL(test_recursive_finalizer);
TEST_SAFE_CALL(test_get_nonobject);
TEST_SAFE_CALL(test_set_nonobject);
TEST_SAFE_CALL(test_finalizer_loop);
}