Add macro mechanism for defining C source information for functions.

This wil let us track source code for C functions more easily.
This commit is contained in:
bakpakin 2021-07-25 13:03:01 -05:00
parent 6f1695ecd4
commit 7fba44ccce
4 changed files with 223 additions and 84 deletions

View File

@ -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

View File

@ -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) {

View File

@ -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 */

View File

@ -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"