From 7fba44ccce553c6e5d54faaa3e614604a96c55fe Mon Sep 17 00:00:00 2001 From: bakpakin Date: Sun, 25 Jul 2021 13:03:01 -0500 Subject: [PATCH] Add macro mechanism for defining C source information for functions. This wil let us track source code for C functions more easily. --- src/core/asm.c | 66 ++++++++--------- src/core/util.c | 176 ++++++++++++++++++++++++++++++++------------ src/core/util.h | 8 ++ src/include/janet.h | 57 ++++++++++++++ 4 files changed, 223 insertions(+), 84 deletions(-) diff --git a/src/core/asm.c b/src/core/asm.c index f453388f..9d8e37c0 100644 --- a/src/core/asm.c +++ b/src/core/asm.c @@ -942,8 +942,12 @@ Janet janet_disasm(JanetFuncDef *def) { return janet_wrap_struct(janet_table_to_struct(ret)); } -/* C Function for assembly */ -static Janet cfun_asm(int32_t argc, Janet *argv) { +JANET_CORE_FN(cfun_asm, + "(asm assembly)", + "Returns a new function that is the compiled result of the assembly.\n" + "The syntax for the assembly can be found on the Janet website, and should correspond\n" + "to the return value of disasm. Will throw an\n" + "error on invalid assembly.") { janet_fixarity(argc, 1); JanetAssembleResult res; res = janet_asm(argv[0], 0); @@ -953,7 +957,24 @@ static Janet cfun_asm(int32_t argc, Janet *argv) { return janet_wrap_function(janet_thunk(res.funcdef)); } -static Janet cfun_disasm(int32_t argc, Janet *argv) { +JANET_CORE_FN(cfun_disasm, + "(disasm func &opt field)", + "Returns assembly that could be used to compile the given function. " + "func must be a function, not a c function. Will throw on error on a badly " + "typed argument. If given a field name, will only return that part of the function assembly. " + "Possible fields are:\n\n" + "* :arity - number of required and optional arguments.\n" + "* :min-arity - minimum number of arguments function can be called with.\n" + "* :max-arity - maximum number of arguments function can be called with.\n" + "* :vararg - true if function can take a variable number of arguments.\n" + "* :bytecode - array of parsed bytecode instructions. Each instruction is a tuple.\n" + "* :source - name of source file that this function was compiled from.\n" + "* :name - name of function.\n" + "* :slotcount - how many virtual registers, or slots, this function uses. Corresponds to stack space used by function.\n" + "* :constants - an array of constants referenced by this function.\n" + "* :sourcemap - a mapping of each bytecode instruction to a line and column in the source file.\n" + "* :environments - an internal mapping of which enclosing functions are referenced for bindings.\n" + "* :defs - other function definitions that this function may instantiate.\n") { janet_arity(argc, 1, 2); JanetFunction *f = janet_getfunction(argv, 0); if (argc == 2) { @@ -976,41 +997,14 @@ static Janet cfun_disasm(int32_t argc, Janet *argv) { } } -static const JanetReg asm_cfuns[] = { - { - "asm", cfun_asm, - JDOC("(asm assembly)\n\n" - "Returns a new function that is the compiled result of the assembly.\n" - "The syntax for the assembly can be found on the Janet website, and should correspond\n" - "to the return value of disasm. Will throw an\n" - "error on invalid assembly.") - }, - { - "disasm", cfun_disasm, - JDOC("(disasm func &opt field)\n\n" - "Returns assembly that could be used to compile the given function.\n" - "func must be a function, not a c function. Will throw on error on a badly\n" - "typed argument. If given a field name, will only return that part of the function assembly.\n" - "Possible fields are:\n\n" - "* :arity - number of required and optional arguments.\n\n" - "* :min-arity - minimum number of arguments function can be called with.\n\n" - "* :max-arity - maximum number of arguments function can be called with.\n\n" - "* :vararg - true if function can take a variable number of arguments.\n\n" - "* :bytecode - array of parsed bytecode instructions. Each instruction is a tuple.\n\n" - "* :source - name of source file that this function was compiled from.\n\n" - "* :name - name of function.\n\n" - "* :slotcount - how many virtual registers, or slots, this function uses. Corresponds to stack space used by function.\n\n" - "* :constants - an array of constants referenced by this function.\n\n" - "* :sourcemap - a mapping of each bytecode instruction to a line and column in the source file.\n\n" - "* :environments - an internal mapping of which enclosing functions are referenced for bindings.\n\n" - "* :defs - other function definitions that this function may instantiate.\n") - }, - {NULL, NULL, NULL} -}; - /* Load the library */ void janet_lib_asm(JanetTable *env) { - janet_core_cfuns(env, NULL, asm_cfuns); + JanetRegExt asm_cfuns[] = { + JANET_CORE_REG("asm", cfun_asm), + JANET_CORE_REG("disasm", cfun_disasm), + JANET_REG_END + }; + janet_core_cfuns_ext(env, NULL, asm_cfuns); } #endif diff --git a/src/core/util.c b/src/core/util.c index adcebcf2..2188c155 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -369,82 +369,148 @@ void janet_register(const char *name, JanetCFunction cfun) { janet_table_put(janet_vm.registry, key, value); } +/* Add sourcemapping and documentation to a binding table */ +static void janet_add_meta(JanetTable *table, const char *doc, const char *source_file, int32_t source_line) { + if (doc) { + janet_table_put(table, janet_ckeywordv("doc"), janet_cstringv(doc)); + } + if (source_file && source_line) { + Janet triple[3]; + triple[0] = janet_cstringv(source_file); + triple[1] = janet_wrap_integer(source_line); + triple[2] = janet_wrap_integer(1); + Janet value = janet_wrap_tuple(janet_tuple_n(triple, 3)); + janet_table_put(table, janet_ckeywordv("source-map"), value); + } +} + /* Add a def to an environment */ -void janet_def(JanetTable *env, const char *name, Janet val, const char *doc) { +void janet_def_sm(JanetTable *env, const char *name, Janet val, const char *doc, const char *source_file, int32_t source_line) { JanetTable *subt = janet_table(2); janet_table_put(subt, janet_ckeywordv("value"), val); - if (doc) - janet_table_put(subt, janet_ckeywordv("doc"), janet_cstringv(doc)); + janet_add_meta(subt, doc, source_file, source_line); janet_table_put(env, janet_csymbolv(name), janet_wrap_table(subt)); } +void janet_def(JanetTable *env, const char *name, Janet value, const char *doc) { + janet_def_sm(env, name, value, doc, NULL, 0); +} /* Add a var to the environment */ -void janet_var(JanetTable *env, const char *name, Janet val, const char *doc) { +void janet_var_sm(JanetTable *env, const char *name, Janet val, const char *doc, const char *source_file, int32_t source_line) { JanetArray *array = janet_array(1); JanetTable *subt = janet_table(2); janet_array_push(array, val); janet_table_put(subt, janet_ckeywordv("ref"), janet_wrap_array(array)); - if (doc) - janet_table_put(subt, janet_ckeywordv("doc"), janet_cstringv(doc)); + janet_add_meta(subt, doc, source_file, source_line); janet_table_put(env, janet_csymbolv(name), janet_wrap_table(subt)); } +void janet_var(JanetTable *env, const char *name, Janet val, const char *doc) { + janet_var_sm(env, name, val, doc, NULL, 0); +} /* Load many cfunctions at once */ -static void _janet_cfuns_prefix(JanetTable *env, const char *regprefix, const JanetReg *cfuns, int defprefix) { - uint8_t *longname_buffer = NULL; - size_t prefixlen = 0; - size_t bufsize = 0; +typedef struct { + uint8_t *longname_buffer; + size_t prefixlen; + size_t bufsize; +} JanetNameBuffer; + +static void cfuns_namebuf_init(JanetNameBuffer *nb, const char *regprefix) { + nb->longname_buffer = NULL; + nb->prefixlen = 0; + nb->bufsize = 0; if (NULL != regprefix) { - prefixlen = strlen(regprefix); - bufsize = prefixlen + 256; - longname_buffer = janet_malloc(bufsize); - if (NULL == longname_buffer) { + nb->prefixlen = strlen(regprefix); + nb->bufsize = nb->prefixlen + 256; + nb->longname_buffer = janet_malloc(nb->bufsize); + if (NULL == nb->longname_buffer) { JANET_OUT_OF_MEMORY; } - safe_memcpy(longname_buffer, regprefix, prefixlen); - longname_buffer[prefixlen] = '/'; - prefixlen++; + safe_memcpy(nb->longname_buffer, regprefix, nb->prefixlen); + nb->longname_buffer[nb->prefixlen] = '/'; + nb->prefixlen++; } - while (cfuns->name) { - Janet name; - if (NULL != regprefix) { - int32_t nmlen = 0; - while (cfuns->name[nmlen]) nmlen++; - int32_t totallen = (int32_t) prefixlen + nmlen; - if ((size_t) totallen > bufsize) { - bufsize = (size_t)(totallen) + 128; - longname_buffer = janet_realloc(longname_buffer, bufsize); - if (NULL == longname_buffer) { - JANET_OUT_OF_MEMORY; - } +} + +static Janet cfuns_namebuf_getname(JanetNameBuffer *nb, const char *suffix) { + if (nb->prefixlen) { + int32_t nmlen = 0; + while (suffix[nmlen]) nmlen++; + int32_t totallen = (int32_t) nb->prefixlen + nmlen; + if ((size_t) totallen > nb->bufsize) { + nb->bufsize = (size_t)(totallen) + 128; + nb->longname_buffer = janet_realloc(nb->longname_buffer, nb->bufsize); + if (NULL == nb->longname_buffer) { + JANET_OUT_OF_MEMORY; } - safe_memcpy(longname_buffer + prefixlen, cfuns->name, nmlen); - name = janet_wrap_symbol(janet_symbol(longname_buffer, totallen)); - } else { - name = janet_csymbolv(cfuns->name); } - Janet fun = janet_wrap_cfunction(cfuns->cfun); - if (defprefix) { - JanetTable *subt = janet_table(2); - janet_table_put(subt, janet_ckeywordv("value"), fun); - if (cfuns->documentation) - janet_table_put(subt, janet_ckeywordv("doc"), janet_cstringv(cfuns->documentation)); - janet_table_put(env, name, janet_wrap_table(subt)); - } else { - janet_def(env, cfuns->name, fun, cfuns->documentation); - } - janet_table_put(janet_vm.registry, fun, name); - cfuns++; + safe_memcpy(nb->longname_buffer + nb->prefixlen, suffix, nmlen); + return janet_wrap_symbol(janet_symbol(nb->longname_buffer, totallen)); + } else { + return janet_csymbolv(suffix); } - (janet_free)(longname_buffer); +} + +static void cfuns_namebuf_deinit(JanetNameBuffer *nb) { + janet_free(nb->longname_buffer); } void janet_cfuns_prefix(JanetTable *env, const char *regprefix, const JanetReg *cfuns) { - _janet_cfuns_prefix(env, regprefix, cfuns, 1); + JanetNameBuffer nb; + cfuns_namebuf_init(&nb, regprefix); + while (cfuns->name) { + Janet name = cfuns_namebuf_getname(&nb, cfuns->name); + Janet fun = janet_wrap_cfunction(cfuns->cfun); + janet_def(env, cfuns->name, fun, cfuns->documentation); + janet_table_put(janet_vm.registry, fun, name); + cfuns++; + } + cfuns_namebuf_deinit(&nb); } void janet_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns) { - _janet_cfuns_prefix(env, regprefix, cfuns, 0); + JanetNameBuffer nb; + cfuns_namebuf_init(&nb, regprefix); + while (cfuns->name) { + Janet name = cfuns_namebuf_getname(&nb, cfuns->name); + Janet fun = janet_wrap_cfunction(cfuns->cfun); + JanetTable *subt = janet_table(2); + janet_table_put(subt, janet_ckeywordv("value"), fun); + janet_add_meta(subt, cfuns->documentation, NULL, 0); + janet_table_put(env, name, janet_wrap_table(subt)); + janet_table_put(janet_vm.registry, fun, name); + cfuns++; + } + cfuns_namebuf_deinit(&nb); +} + +void janet_cfuns_ext(JanetTable *env, const char *regprefix, const JanetRegExt *cfuns) { + JanetNameBuffer nb; + cfuns_namebuf_init(&nb, regprefix); + while (cfuns->name) { + Janet name = cfuns_namebuf_getname(&nb, cfuns->name); + Janet fun = janet_wrap_cfunction(cfuns->cfun); + janet_def_sm(env, cfuns->name, fun, cfuns->documentation, cfuns->source_file, cfuns->source_line); + janet_table_put(janet_vm.registry, fun, name); + cfuns++; + } + cfuns_namebuf_deinit(&nb); +} + +void janet_cfuns_ext_prefix(JanetTable *env, const char *regprefix, const JanetRegExt *cfuns) { + JanetNameBuffer nb; + cfuns_namebuf_init(&nb, regprefix); + while (cfuns->name) { + Janet name = cfuns_namebuf_getname(&nb, cfuns->name); + Janet fun = janet_wrap_cfunction(cfuns->cfun); + JanetTable *subt = janet_table(2); + janet_table_put(subt, janet_ckeywordv("value"), fun); + janet_add_meta(subt, cfuns->documentation, cfuns->source_file, cfuns->source_line); + janet_table_put(env, name, janet_wrap_table(subt)); + janet_table_put(janet_vm.registry, fun, name); + cfuns++; + } + cfuns_namebuf_deinit(&nb); } /* Abstract type introspection */ @@ -485,6 +551,20 @@ void janet_core_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cf cfuns++; } } + +void janet_core_def_sm(JanetTable *env, const char *name, Janet x, const void *p, const void *sf, int32_t sl) { + (void) sf, sl; + janet_core_def(env, name, x, p); +} + +void janet_core_cfuns_ext(JanetTable *env, const char *regprefix, const JanetRegExt *cfuns) { + (void) regprefix; + while (cfuns->name) { + Janet fun = janet_wrap_cfunction(cfuns->cfun); + janet_core_def(env, cfuns->name, fun, cfuns->documentation); + cfuns++; + } +} #endif JanetBinding janet_resolve_ext(JanetTable *env, const uint8_t *sym) { diff --git a/src/core/util.h b/src/core/util.h index e596ceed..90bf690c 100644 --- a/src/core/util.h +++ b/src/core/util.h @@ -90,11 +90,19 @@ Janet janet_next_impl(Janet ds, Janet key, int is_interpreter); /* Inside the janet core, defining globals is different * at bootstrap time and normal runtime */ #ifdef JANET_BOOTSTRAP +#define JANET_CORE_REG JANET_REG +#define JANET_CORE_FN JANET_FN #define janet_core_def janet_def #define janet_core_cfuns janet_cfuns +#define janet_core_def_sm janet_def_sm +#define janet_core_cfuns_ext janet_cfuns_ext #else +#define JANET_CORE_REG JANET_REG_ +#define JANET_CORE_FN JANET_FN_ void janet_core_def(JanetTable *env, const char *name, Janet x, const void *p); void janet_core_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cfuns); +void janet_core_def_sm(JanetTable *env, const char *name, Janet x, const void *p, const void *sf, int32_t sl); +void janet_core_cfuns_ext(JanetTable *env, const char *regprefix, const JanetRegExt *cfuns); #endif /* Clock gettime */ diff --git a/src/include/janet.h b/src/include/janet.h index 09b46530..13a235f8 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -403,6 +403,7 @@ typedef struct JanetKV JanetKV; typedef struct JanetStackFrame JanetStackFrame; typedef struct JanetAbstractType JanetAbstractType; typedef struct JanetReg JanetReg; +typedef struct JanetRegExt JanetRegExt; typedef struct JanetMethod JanetMethod; typedef struct JanetSourceMapping JanetSourceMapping; typedef struct JanetView JanetView; @@ -1093,6 +1094,14 @@ struct JanetReg { const char *documentation; }; +struct JanetRegExt { + const char *name; + JanetCFunction cfun; + const char *documentation; + const char *source_file; + int32_t source_line; +}; + struct JanetMethod { const char *name; JanetCFunction cfun; @@ -1750,6 +1759,54 @@ JANET_API Janet janet_resolve_core(const char *name); /* Shorthand for janet C function declarations */ #define JANET_CFUN(name) Janet name (int32_t argc, Janet *argv) +/* Declare a C function with documentation and source mapping */ +#define JANET_REG_END {NULL, NULL, NULL, NULL, 0} + +/* no docstrings or sourcemaps */ +#define JANET_REG_(JNAME, CNAME) {JNAME, CNAME, NULL, NULL, 0} +#define JANET_FN_(CNAME, USAGE, DOCSTRING) \ + static Janet CNAME (int32_t argc, Janet *argv) + +/* sourcemaps only */ +#define JANET_REG_S(JNAME, CNAME) {JNAME, CNAME, NULL, __FILE__, CNAME##_sourceline_} +#define JANET_FN_S(CNAME, USAGE, DOCSTRING) \ + static int32_t CNAME##_sourceline_ = __LINE__; \ + static Janet CNAME (int32_t argc, Janet *argv) + +/* docstring only */ +#define JANET_REG_D(JNAME, CNAME) {JNAME, CNAME, CNAME##_docstring_, NULL, 0} +#define JANET_FN_D(CNAME, USAGE, DOCSTRING) \ + static const char CNAME##_docstring_[] = USAGE "\n\n" DOCSTRING; \ + static Janet CNAME (int32_t argc, Janet *argv) + +/* sourcemaps and docstrings */ +#define JANET_REG_SD(JNAME, CNAME) {JNAME, CNAME, CNAME##_docstring_, __FILE__, CNAME##_sourceline_} +#define JANET_FN_SD(CNAME, USAGE, DOCSTRING) \ + static int32_t CNAME##_sourceline_ = __LINE__; \ + static const char CNAME##_docstring_[] = USAGE "\n\n" DOCSTRING; \ + static Janet CNAME (int32_t argc, Janet *argv) + +/* Choose defaults for source mapping and docstring based on config defs */ +#if defined(JANET_NO_SOURCEMAPS) && defined(JANET_NO_DOCSTRINGS) +#define JANET_REG JANET_REG_ +#define JANET_FN JANET_FN_ +#elif defined(JANET_NO_SOURCEMAPS) && !defined(JANET_NO_DOCSTRINGS) +#define JANET_REG JANET_REG_D +#define JANET_FN JANET_FN_D +#elif !defined(JANET_NO_SOURCEMAPS) && defined(JANET_NO_DOCSTRINGS) +#define JANET_REG JANET_REG_S +#define JANET_FN JANET_FN_S +#elif !defined(JANET_NO_SOURCEMAPS) && !defined(JANET_NO_DOCSTRINGS) +#define JANET_REG JANET_REG_SD +#define JANET_FN JANET_FN_SD +#endif + +/* Define things with source mapping information */ +JANET_API void janet_cfuns_ext(JanetTable *env, const char *regprefix, const JanetRegExt *cfuns); +JANET_API void janet_cfuns_ext_prefix(JanetTable *env, const char *regprefix, const JanetRegExt *cfuns); +JANET_API void janet_def_sm(JanetTable *env, const char *name, Janet val, const char *documentation, const char *source_file, int32_t source_line); +JANET_API void janet_var_sm(JanetTable *env, const char *name, Janet val, const char *documentation, const char *source_file, int32_t source_line); + /* Allow setting entry name for static libraries */ #ifdef __cplusplus #define JANET_MODULE_PREFIX extern "C"