diff src/libmlk-core-js/core/js-state.c @ 355:3b2eed504c61

js: simplify game/state mechanism
author David Demelier <markand@malikania.fr>
date Fri, 22 Oct 2021 10:19:35 +0200
parents 323d13f49233
children 39f5d932941a
line wrap: on
line diff
--- 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;
 }