#include "vm.h"
#include "util-jn.h"
#include "util-gd.h"
#include "rsrc.h"
#define _safe_wrap(gdt, ct) ({ \
_Static_assert(sizeof(gdt) == sizeof(ct)); \
*(typeof(ct)*)v; \
})
void
gdjn_dejanetize_typed
( GDExtensionTypePtr v,
GDExtensionVariantType const t,
Janet val
) {
switch (t) {
case GDEXTENSION_VARIANT_TYPE_BOOL:
assert(janet_type(val) == JANET_BOOLEAN);
*(bool*)v = janet_unwrap_boolean(val);
break;
case GDEXTENSION_VARIANT_TYPE_INT:
switch (janet_type(val)) {
case JANET_NUMBER:
*(int64_t*)v = janet_unwrap_integer(val); break;
case JANET_INT_S64:
*(int64_t*)v = janet_unwrap_s64(val); break;
case JANET_INT_U64:
*(int64_t*)v = janet_unwrap_u64(val); break;
default: assert(false);
}
break;
case GDEXTENSION_VARIANT_TYPE_STRING:
case GDEXTENSION_VARIANT_TYPE_STRING_NAME: {
JanetString str;
switch (janet_type(val)) {
case JANET_STRING: str = janet_unwrap_string(val); break;
case JANET_KEYWORD: str = janet_unwrap_keyword(val); break;
case JANET_SYMBOL: str = janet_unwrap_symbol(val); break;
default: assert(false);
}
size_t len = janet_string_length(str);
if (t == GDEXTENSION_VARIANT_TYPE_STRING_NAME) {
_t(stringName).newWithUtf8CharsAndLen(v, (char*)str, len);
} else {
_t(string).newWithUtf8CharsAndLen(v, (char*)str, len);
}
break;
}
case GDEXTENSION_VARIANT_TYPE_ARRAY: {
}
case GDEXTENSION_VARIANT_TYPE_DICTIONARY: {
}
default: {
assert(false);
}
}
}
Janet
gdjn_janetize_typed
( GDExtensionTypePtr const v,
GDExtensionVariantType const t
) {
switch (t) {
case GDEXTENSION_VARIANT_TYPE_NIL:
return janet_wrap_nil();
case GDEXTENSION_VARIANT_TYPE_BOOL:
return janet_wrap_boolean(_safe_wrap(gd_bool, int8_t));
case GDEXTENSION_VARIANT_TYPE_INT:
return janet_wrap_s64(_safe_wrap(gd_int, int64_t));
case GDEXTENSION_VARIANT_TYPE_FLOAT:
_Static_assert(
sizeof(gd_float) == sizeof(double) ||
sizeof(gd_float) == sizeof(float)
);
return janet_wrap_number(
(sizeof(gd_float) == sizeof(double)) ? *(double*)v :
(sizeof(gd_float) == sizeof(float)) ? *(float*)v :0);
case GDEXTENSION_VARIANT_TYPE_STRING: {
auto str = gdu_string_pdup((gd_string*)v);
auto j = janet_stringv((void*)str.v, str.sz);
_free(str.v);
return j;
};
case GDEXTENSION_VARIANT_TYPE_STRING_NAME: {
/* we can reasonably assume syms will be small enough
* to fit on the stack and avoid a pointless malloc */
auto str = _gdu_stringName_stackp((gd_stringName*)v);
auto j = janet_keywordv((void*)str.v, str.sz);
return j;
};
case GDEXTENSION_VARIANT_TYPE_ARRAY: {
auto sz = gd_array_size(v);
auto ja = janet_array(sz);
for (size_t i = 0; i < sz; ++i) {
auto val = _t(array).operatorIndexConst(v, i);
auto j = gdjn_janetize(val);
janet_array_push(ja, j);
}
return janet_wrap_array(ja);
};
default: assert(false);
}
}
typedef struct jn_closure {
Janet (*fn)(void* data, int32_t argc, Janet* argv);
void (*gc)(void* data);
char alignas(max_align_t) data [];
} jn_closure;
typedef struct jn_hnd_dict {
JanetAbstractHead header;
GDExtensionVariantType key, val;
gd_dictionary dict;
} jn_hnd_dict;
typedef struct jn_hnd_array {
JanetAbstractHead header;
GDExtensionVariantType ty;
gd_array array;
} jn_hnd_array;
static int
api_del_closure(void* ptr, size_t sz) {
jn_closure* c = ptr;
if (c -> gc != nullptr) {
(*c -> gc)(c -> data);
}
return 0;
}
static Janet
api_call_closure
( void* ptr,
int32_t argc,
Janet* argv
) {
jn_closure* c = ptr;
return (c -> fn)(c -> data, argc, argv);
}
static int
api_del_dict(void* ptr, size_t sz) {
jn_hnd_dict* dict = ptr;
_t(dictionary).dtor(&dict -> dict);
printf("drop dict\n");
return 0;
}
static int
api_del_array(void* ptr, size_t sz) {
jn_hnd_array* array = ptr;
_t(array).dtor(&array -> array);
printf("drop array\n");
return 0;
}
const JanetAbstractType jn_closure_def = {
.name = "closure",
.call = api_call_closure,
.gc = api_del_closure,
};
const JanetAbstractType jn_hnd_dict_def = {
.name = "prim/type/dict",
.gc = api_del_dict,
};
const JanetAbstractType jn_hnd_array_def = {
.name = "prim/type/array",
.gc = api_del_array,
};
static Janet
api_new_array(int32_t argc, Janet* argv) {
auto a = (jn_hnd_array*)janet_abstract(&jn_hnd_array_def, sizeof(jn_hnd_array));
_t(array).empty(&a -> array, nullptr);
printf("create array\n");
return janet_wrap_abstract(a);
}
static Janet
api_new_dict(int32_t argc, Janet* argv) {
auto a = (jn_hnd_dict*)janet_abstract(&jn_hnd_dict_def, sizeof(jn_hnd_dict));
_t(dictionary).empty(&a -> dict, nullptr);
printf("create dict\n");
return janet_wrap_abstract(a);
}
/* (prim/class-load [<ident> [<ident2>...]])
* low-level class loader. run at compile time to
* import a godot class */
static Janet
api_class_load(int32_t argc, Janet* argv) {
return janet_wrap_nil(); /* FIXME */
}
static const JanetReg reg_core [] = {
{"class-load", api_class_load,
"(prim/class-load ident)\n\n"
"low-level loading function for Godot classes"},
{"type/array", api_new_array,
"(prim/type/array [...])\n\n"
"create a handle to a new godot array object"},
{"type/dict", api_new_dict,
"(prim/type/dict <key-type> <val-type> {...})\n\n"
"create a handle to a new godot dictionary object"},
{}
};
JanetTable*
gdjn_vm_api_spawnEnv (JanetTable* api) {
/* create a clean new environment that can be used
* and discarded by a script without contaminating
* the global environment(s)
* yes this is ooky */
auto env = jnu_table_extend(api, 8);
auto sym_mc = janet_csymbolv("module/cache");
auto cleancache = jnu_table_extend(
janet_unwrap_table(janet_table_get(api, sym_mc)), 8);
janet_table_put(env, janet_csymbolv("module/cache"), janet_wrap_table(cleancache));
return env;
}
gdjn_vm_bind
gdjn_vm_meta
( JanetTable* bind
) {
gdjn_vm_bind b = {};
if (gdjn_vm_metaFlag(bind, "private")) goto fail;
if (!gdjn_vm_metaKey(bind, "value", &b.val)) goto fail;
if (gdjn_vm_metaFlag(bind, "macro"))
b.kind = gdjn_vm_bind_mac;
else {
if (gdjn_vm_metaKey(bind, "method", &b.meta))
/* TODO assert callability */
b.kind = gdjn_vm_bind_method;
else if (gdjn_vm_metaFlag(bind, "class"))
b.kind = gdjn_vm_bind_class;
else if (gdjn_vm_metaKey(bind, "type", &b.meta)) {
if (gdjn_vm_metaFlag(bind, "prop")) {
b.kind = gdjn_vm_bind_prop;
} else /* constant */ {
b.kind = gdjn_vm_bind_const;
goto succeed;
}
if (gdjn_vm_metaFlag(bind, "share"))
b.kind |= gdjn_vm_bind_flag_static;
} else {
switch (janet_type(b.val)) {
case JANET_ABSTRACT:
if ((janet_abstract_type(&b.val)) != &jn_closure_def) {
b.kind = gdjn_vm_bind_const; break;
}
case JANET_FUNCTION:
case JANET_CFUNCTION:
b.kind = gdjn_vm_bind_method_static;
break;
default: goto fail;
}
}
}
/* found a valid export, return it */
succeed: return b;
/* this binding is not marked correctly for gdexport */
fail: return (gdjn_vm_bind){gdjn_vm_bind_none};
}
gdjn_vm_bind
gdjn_vm_resv
( JanetTable* env,
Janet key
) {
auto gchnd = janet_gclock();
Janet def = janet_table_get(env, key);
if (janet_type(def) == JANET_NIL)
return (gdjn_vm_bind){};
auto m = gdjn_vm_meta(janet_unwrap_table(def));
janet_gcunlock(gchnd);
return m;
}
void gdjn_vm_api_installCommon (JanetTable* tgt) {
/* install primitives */
janet_cfuns(tgt, "prim", reg_core);
auto idmap = janet_env_lookup(tgt);
int gc = janet_gclock();
/* unpack API image */
Janet apiEnv = janet_unmarshal(
gdjn_rsrc_api_jimage,
sizeof gdjn_rsrc_api_jimage,
0,
idmap,
nullptr);
printf("apienv type is %s\n",
janet_type_names[janet_type(apiEnv)]);
JanetTable* apiTbl = janet_unwrap_table(apiEnv);
/* call the init function to precache base modules */
Janet initDef = janet_table_get(apiTbl, janet_csymbolv("init"));
if (janet_type(initDef) == JANET_NIL) {
_err("no init fn in api envtbl");
goto fail;
}
auto initFn = janet_unwrap_function(
janet_table_get(janet_unwrap_table(initDef),
janet_ckeywordv("value")));
Janet ret;
auto e = janet_pcall(initFn, 0, nullptr, &ret, nullptr);
if (e == JANET_SIGNAL_ERROR) {
_err("failed to unpack the janet environment");
goto fail;
}
printf("environment load complete\n");
fail:
janet_gcunlock(gc);
/* janet_collect(); */
}
JanetTable* gdjn_vm_api_build_compTime(void) {
auto core = janet_core_env(nullptr);
auto api = jnu_table_extend(core,32);
gdjn_vm_api_installCommon(api);
return api;
}
JanetTable* gdjn_vm_api_build_core(void) {
auto core = janet_core_env(nullptr);
auto api = jnu_table_extend(core,32);
gdjn_vm_api_installCommon(api);
return api;
}
JanetTable*
gdjn_vm_compile
( pstr const body,
JanetTable* api,
const char* ctx
) {
if (!ctx) ctx = "<anon>";
if (!api) api = gdjn_ctx -> jn.api;
auto cls = jnu_table_extend(api, 8);
janet_gcroot(janet_wrap_table(cls));
/* printf("janet: doing bytes %d %s\n", (int)me -> src.sz, me -> src.v); */
int e = janet_dobytes(cls,
(uint8_t const*)body.v, body.sz,
ctx, nullptr);
/* we discard the return value; the environment is what
* we're really interested in here */
/* printf("janet: bytes done, got %d\n",e); */
if (e != 0) {
_err("janet module could not be loaded");
/* TODO capture parse error */
janet_gcunroot(janet_wrap_table(cls));
cls = nullptr;
}
return cls;
}
pstr
gdjn_vm_image
( JanetTable* env,
JanetTable* binds
) {
return (pstr){}; //TODO
}