# HG changeset patch # User David Demelier # Date 1634890775 -7200 # Node ID 3b2eed504c617ebd840832f2f33cf1c75971ae41 # Parent 2dbe1cf6b544ab45b0b83914e2a7d3103c483606 js: simplify game/state mechanism diff -r 2dbe1cf6b544 -r 3b2eed504c61 src/libmlk-core-js/core/js-game.c --- a/src/libmlk-core-js/core/js-game.c Thu Oct 21 13:45:36 2021 +0200 +++ b/src/libmlk-core-js/core/js-game.c Fri Oct 22 10:19:35 2021 +0200 @@ -23,8 +23,6 @@ #include "js-game.h" #include "js-state.h" -#define TABLE DUK_HIDDEN_SYMBOL("Mlk.Game.table") - /* * TODO: determine if it's worth it to add handle, update and draw functions. */ @@ -34,18 +32,13 @@ { struct state *state = js_state_require(ctx, 0); - if (game.state == &game.states[GAME_STATE_MAX]) + if (game.state == &game.states[GAME_STATE_MAX]) { + state_finish(state); duk_error(ctx, DUK_ERR_RANGE_ERROR, "too many states"); + } game_push(state); - /* Store the state to avoid destruction. */ - duk_push_global_stash(ctx); - duk_get_prop_string(ctx, -1, TABLE); - duk_dup(ctx, 0); - duk_put_prop_index(ctx, -2, duk_get_length(ctx, -2)); - duk_pop_n(ctx, 2); - return 0; } @@ -55,11 +48,7 @@ if (game.state == &game.states[0]) return 0; - /* Remove the stored reference. */ - duk_push_global_stash(ctx); - duk_get_prop_string(ctx, -1, TABLE); - duk_del_prop_index(ctx, -1, duk_get_length(ctx, -1) - 1); - duk_pop_n(ctx, 2); + game_pop(); return 0; } @@ -100,8 +89,4 @@ duk_push_object(ctx); duk_put_function_list(ctx, -1, functions); duk_put_global_string(ctx, "Game"); - duk_push_global_stash(ctx); - duk_push_array(ctx); - duk_put_prop_string(ctx, -2, TABLE); - duk_pop(ctx); } diff -r 2dbe1cf6b544 -r 3b2eed504c61 src/libmlk-core-js/core/js-state.c --- a/src/libmlk-core-js/core/js-state.c Thu Oct 21 13:45:36 2021 +0200 +++ b/src/libmlk-core-js/core/js-state.c Fri Oct 22 10:19:35 2021 +0200 @@ -26,120 +26,132 @@ #include "js-state.h" #define SIGNATURE DUK_HIDDEN_SYMBOL("Mlk.State") -#define FUNCTIONS DUK_HIDDEN_SYMBOL("Mlk.State.functions") - -#define INVOKE(state, function, block) \ -do { \ - struct statedata *data = state->data; \ - \ - duk_push_heapptr(data->ctx, data->heapptr); \ - duk_get_prop_index(data->ctx, -1, function); \ - duk_remove(data->ctx, -2); \ - \ - if (duk_is_callable(data->ctx, -1)) { \ - block \ - duk_pop(data->ctx); \ - } else { \ - duk_pop(data->ctx); \ - } \ -} while (0) +#define SELF DUK_HIDDEN_SYMBOL("Mlk.State.self") -#define SET(ctx, function) \ -do { \ - struct statedata *data = self(ctx); \ - \ - duk_push_heapptr(ctx, data->heapptr); \ - duk_dup(ctx, 0); \ - duk_put_prop_index(ctx, -2, function); \ - duk_pop(ctx); \ - \ - return 0; \ -} while (0) - -enum statefunc { - FUNC_START, - FUNC_HANDLE, - FUNC_UPDATE, - FUNC_DRAW, - FUNC_SUSPEND, - FUNC_RESUME, - FUNC_END +struct self { + duk_context *ctx; + void *selfptr; + struct state state; + unsigned int refc; }; -struct statedata { - duk_context *ctx; - void *heapptr; - struct state state; -}; +static inline int +callable(struct self *s, const char *prop, duk_context **ctx) +{ + int callable; + + duk_push_heapptr(s->ctx, s->selfptr); + duk_get_prop_string(s->ctx, -1, prop); + duk_remove(s->ctx, -2); + + if (duk_is_callable(s->ctx, -1)) { + *ctx = s->ctx; + callable = 1; + } else { + *ctx = NULL; + callable = 0; + duk_pop(s->ctx); + } + + return callable; +} static void start(struct state *state) { - INVOKE(state, FUNC_START, { - duk_call(data->ctx, 0); - }); + duk_context *ctx; + + if (callable(state->data, "start", &ctx)) { + duk_call(ctx, 0); + duk_pop(ctx); + } } static void handle(struct state *state, const union event *ev) { - INVOKE(state, FUNC_HANDLE, { - js_event_push(data->ctx, ev); - duk_call(data->ctx, 1); - }); + duk_context *ctx; + + if (callable(state->data, "handle", &ctx)) { + js_event_push(ctx, ev); + duk_call(ctx, 1); + duk_pop(ctx); + } } static void update(struct state *state, unsigned int ticks) { - INVOKE(state, FUNC_UPDATE, { - duk_push_uint(data->ctx, ticks); - duk_call(data->ctx, 1); - }); + duk_context *ctx; + + if (callable(state->data, "update", &ctx)) { + duk_push_uint(ctx, ticks); + duk_call(ctx, 1); + duk_pop(ctx); + } } static void draw(struct state *state) { - INVOKE(state, FUNC_DRAW, { - duk_call(data->ctx, 0); - }); + duk_context *ctx; + + if (callable(state->data, "draw", &ctx)) { + duk_call(ctx, 0); + duk_pop(ctx); + } } static void suspend(struct state *state) { - INVOKE(state, FUNC_SUSPEND, { - duk_call(data->ctx, 0); - }); + duk_context *ctx; + + if (callable(state->data, "suspend", &ctx)) { + duk_call(ctx, 0); + duk_pop(ctx); + } } static void resume(struct state *state) { - INVOKE(state, FUNC_RESUME, { - duk_call(data->ctx, 0); - }); + duk_context *ctx; + + if (callable(state->data, "resume", &ctx)) { + duk_call(ctx, 0); + duk_pop(ctx); + } } static void end(struct state *state) { - INVOKE(state, FUNC_END, { - duk_call(data->ctx, 0); - }); + duk_context *ctx; + + if (callable(state->data, "end", &ctx)) { + duk_call(ctx, 0); + duk_pop(ctx); + } } static void finish(struct state *state) { - free(state->data); + struct self *self = state->data; + + if (!--self->refc) { + duk_push_heapptr(self->ctx, self->selfptr); + duk_del_prop_string(self->ctx, -1, SIGNATURE); + duk_pop(self->ctx); + free(self); + } } -static inline struct statedata * +static inline struct self * self(duk_context *ctx) { - struct statedata *data = NULL; + struct self *data = NULL; duk_push_this(ctx); duk_get_prop_string(ctx, -1, SIGNATURE); @@ -153,91 +165,43 @@ } static duk_ret_t -State_setStart(duk_context *ctx) -{ - SET(ctx, FUNC_START); -} - -static duk_ret_t -State_setHandle(duk_context *ctx) +State_constructor(duk_context *ctx) { - SET(ctx, FUNC_HANDLE); -} - -static duk_ret_t -State_setUpdate(duk_context *ctx) -{ - SET(ctx, FUNC_UPDATE); -} + struct self *self; -static duk_ret_t -State_setDraw(duk_context *ctx) -{ - SET(ctx, FUNC_DRAW); -} + self = alloc_new0(sizeof (*self)); + self->ctx = ctx; + self->refc = 1; + self->state.data = self; + self->state.start = start; + self->state.handle = handle; + self->state.update = update; + self->state.draw = draw; + self->state.suspend = suspend; + self->state.resume = resume; + self->state.end = end; + self->state.finish = finish; -static duk_ret_t -State_setSuspend(duk_context *ctx) -{ - SET(ctx, FUNC_SUSPEND); -} + duk_push_this(ctx); + self->selfptr = duk_get_heapptr(ctx, -1); + duk_dup(ctx, -1); + duk_put_prop_string(ctx, -2, SELF); + duk_push_pointer(ctx, self); + duk_put_prop_string(ctx, -2, SIGNATURE); + duk_pop(ctx); -static duk_ret_t -State_setResume(duk_context *ctx) -{ - SET(ctx, FUNC_RESUME); + return 0; } static duk_ret_t -State_setEnd(duk_context *ctx) +State_destructor(duk_context *ctx) { - SET(ctx, FUNC_END); -} - -static const struct { - const char *property; - duk_ret_t (*setter)(duk_context *ctx); -} properties[] = { - { "start", State_setStart }, - { "handle", State_setHandle }, - { "update", State_setUpdate }, - { "draw", State_setDraw }, - { "suspend", State_setSuspend }, - { "resume", State_setResume }, - { "end", State_setEnd }, - { NULL, NULL } -}; - -static duk_ret_t -State_constructor(duk_context *ctx) -{ - struct statedata *data; + struct self *self; - data = alloc_new0(sizeof (*data)); - data->ctx = ctx; - data->state.data = data; - data->state.start = start; - data->state.handle = handle; - data->state.update = update; - data->state.draw = draw; - data->state.suspend = suspend; - data->state.resume = resume; - data->state.end = end; - data->state.finish = finish; + duk_get_prop_string(ctx, 0, SIGNATURE); - duk_push_this(ctx); - duk_push_pointer(ctx, data); - duk_put_prop_string(ctx, -2, SIGNATURE); - duk_push_array(ctx); - data->heapptr = duk_get_heapptr(ctx, -1); - duk_put_prop_string(ctx, -2, FUNCTIONS); - - /* Push every properties. */ - for (size_t i = 0; properties[i].property; ++i) { - duk_push_string(ctx, properties[i].property); - duk_push_c_function(ctx, properties[i].setter, 1); - duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_SETTER); - } + if ((self = duk_to_pointer(ctx, -1))) + state_finish(&self->state); duk_pop(ctx); @@ -250,22 +214,28 @@ assert(ctx); duk_push_c_function(ctx, State_constructor, 1); + duk_push_object(ctx); + duk_push_c_function(ctx, State_destructor, 1); + duk_set_finalizer(ctx, -2); + duk_put_prop_string(ctx, -2, "prototype"); duk_put_global_string(ctx, "State"); } struct state * js_state_require(duk_context *ctx, duk_idx_t idx) { - struct statedata *data = NULL; + struct self *self = NULL; if (duk_is_object(ctx, idx)) { duk_get_prop_string(ctx, idx, SIGNATURE); - data = duk_to_pointer(ctx, -1); + self = duk_to_pointer(ctx, -1); duk_pop(ctx); } - if (!data) + if (!self) duk_error(ctx, DUK_ERR_TYPE_ERROR, "not a State object"); - return &data->state; + self->refc++; + + return &self->state; } diff -r 2dbe1cf6b544 -r 3b2eed504c61 src/mlk-run/main.c --- a/src/mlk-run/main.c Thu Oct 21 13:45:36 2021 +0200 +++ b/src/mlk-run/main.c Fri Oct 22 10:19:35 2021 +0200 @@ -105,12 +105,8 @@ vfs_file_finish(&main); - if (duk_peval_string(ctx, code)) { - duk_get_prop_string(ctx, -1, "lineNumber"); - printf("%d\n", duk_to_int(ctx, -1)); - duk_pop(ctx); + if (duk_peval_string(ctx, code)) panicf("%s", duk_safe_to_string(ctx, -1)); - } free(code); }