#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 [ [...]]) * 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 {...})\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, 1, (Janet[]) {janet_wrap_table(tgt)}, &ret, nullptr); if (e == JANET_SIGNAL_ERROR) { _errMsg("failed to unpack the janet environment", (char*)janet_unwrap_string(ret)); goto fail; } /* janet_table_merge_table(tgt, janet_unwrap_table(ret)); */ 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 = ""; 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 }