diff options
author | Jo-Philipp Wich <jo@mein.io> | 2021-04-21 15:07:16 +0200 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2021-04-25 20:48:40 +0200 |
commit | 35af4ba4fc21a4b2357c50e6b02a2e3e4b236e88 (patch) | |
tree | 445f9fdf2e96e490cd681dca36d5cc9912474ed3 /object.c | |
parent | f2c4b79feaffd7b2fdb4041f47c9cd0f4cc3bc6e (diff) |
treewide: rework internal data type system
Instead of relying on json_object values internally, use custom types to
represent the different ucode value types which brings a number of
advantages compared to the previous approach:
- Due to the use of tagged pointers, small integer, string and bool
values can be stored directly in the pointer addresses, vastly
reducing required heap memory
- Ability to create circular data structures such as
`let o; o = { test: o };`
- Ability to register custom `tostring()` function through prototypes
- Initial mark/sweep GC implementation to tear down circular object
graphs on VM deinit
The change also paves the way for possible future extensions such as
constant variables and meta methods for custom ressource types.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'object.c')
-rw-r--r-- | object.c | 493 |
1 files changed, 0 insertions, 493 deletions
diff --git a/object.c b/object.c deleted file mode 100644 index 7748744..0000000 --- a/object.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * Copyright (C) 2020-2021 Jo-Philipp Wich <jo@mein.io> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <string.h> -#include <assert.h> - -#include "object.h" - -static void * -uc_object_new(uc_objtype_t type, size_t size, json_object_to_json_string_fn *tostring, json_object_delete_fn *gc) -{ - uc_objhdr *hdr = xalloc(size); - - hdr->type = type; - hdr->jso = xjs_new_object(); - - json_object_set_serializer(hdr->jso, tostring, hdr, gc); - - return hdr; -} - -static int -uc_upvalref_tostring(json_object *jso, struct printbuf *pb, int level, int flags) -{ - return sprintbuf(pb, "<upvalref %p>", jso); -} - -static void -uc_upvalref_gc(json_object *jso, void *userdata) -{ - uc_upvalref *up = userdata; - - uc_value_put(up->value); - free(up); -} - -uc_upvalref * -uc_upvalref_new(size_t slot) -{ - uc_upvalref *up; - - up = uc_object_new(UC_OBJ_UPVAL, sizeof(*up), uc_upvalref_tostring, uc_upvalref_gc); - up->slot = slot; - - return up; -} - -static int -uc_function_tostring(json_object *jso, struct printbuf *pb, int level, int flags) -{ - return sprintbuf(pb, "<function %p>", jso); -} - -static void -uc_function_gc(json_object *jso, void *userdata) -{ - uc_function *fn = userdata; - - uc_chunk_free(&fn->chunk); - uc_source_put(fn->source); - - free(fn); -} - -uc_function * -uc_function_new(const char *name, size_t srcpos, uc_source *source) -{ - size_t namelen = 0; - uc_function *fn; - - if (name) - namelen = strlen(name) + 1; - - fn = uc_object_new(UC_OBJ_FUNCTION, ALIGN(sizeof(*fn)) + namelen, uc_function_tostring, uc_function_gc); - fn->name = name ? strcpy((char *)fn + ALIGN(sizeof(*fn)), name) : NULL; - fn->nargs = 0; - fn->nupvals = 0; - fn->srcpos = srcpos; - fn->source = uc_source_get(source); - fn->vararg = false; - - uc_chunk_init(&fn->chunk); - - return fn; -} - -size_t -uc_function_get_srcpos(uc_function *fn, size_t off) -{ - size_t pos = uc_chunk_debug_get_srcpos(&fn->chunk, off); - - return pos ? fn->srcpos + pos : 0; -} - -static int -uc_closure_tostring(json_object *jso, struct printbuf *pb, int level, int flags) -{ - bool strict = (level > 0) || (flags & JSON_C_TO_STRING_STRICT); - uc_closure *closure = json_object_get_userdata(jso); - uc_function *function = closure->function; - json_object *argname; - size_t i; - - sprintbuf(pb, "%s%s", - strict ? "\"" : "", - closure->is_arrow ? "" : "function"); - - if (function->name) - sprintbuf(pb, " %s", function->name); - - sprintbuf(pb, "("); - - for (i = 1; i <= function->nargs; i++) { - argname = uc_chunk_debug_get_variable(&function->chunk, i - 1, i, false); - - if (i > 1) - sprintbuf(pb, ", "); - - if (i == function->nargs && function->vararg) - sprintbuf(pb, "..."); - - if (argname) - sprintbuf(pb, "%s", json_object_get_string(argname)); - else - sprintbuf(pb, "[arg%zu]", i); - - uc_value_put(argname); - } - - return sprintbuf(pb, ")%s { ... }%s", - closure->is_arrow ? " =>" : "", - strict ? "\"" : ""); -} - -static void -uc_closure_gc(json_object *jso, void *userdata) -{ - uc_closure *closure = userdata; - uc_function *function = closure->function; - size_t i; - - for (i = 0; i < function->nupvals; i++) - uc_value_put(closure->upvals[i]->header.jso); - - uc_value_put(function->header.jso); - - free(closure); -} - -uc_closure * -uc_closure_new(uc_function *function, bool arrow_fn) -{ - uc_closure *closure; - - closure = uc_object_new(UC_OBJ_CLOSURE, - ALIGN(sizeof(*closure)) + (sizeof(uc_upvalref *) * function->nupvals), - uc_closure_tostring, uc_closure_gc); - - closure->function = function; - closure->is_arrow = arrow_fn; - closure->upvals = function->nupvals ? ((void *)closure + ALIGN(sizeof(*closure))) : NULL; - - return closure; -} - -static int -uc_cfunction_tostring(json_object *jso, struct printbuf *pb, int level, int flags) -{ - bool strict = (level > 0) || (flags & JSON_C_TO_STRING_STRICT); - uc_cfunction *cfn = json_object_get_userdata(jso); - - return sprintbuf(pb, "%sfunction%s%s(...) { [native code] }%s", - strict ? "\"" : "", - cfn->name ? " " : "", - cfn->name ? cfn->name : "", - strict ? "\"" : ""); -} - -static void -uc_cfunction_gc(json_object *jso, void *userdata) -{ - free(userdata); -} - -uc_cfunction * -uc_cfunction_new(const char *name, uc_cfn_ptr fptr) -{ - size_t namelen = 0; - uc_cfunction *cfn; - - if (name) - namelen = strlen(name) + 1; - - cfn = uc_object_new(UC_OBJ_CFUNCTION, ALIGN(sizeof(*cfn)) + namelen, uc_cfunction_tostring, uc_cfunction_gc); - cfn->name = name ? strcpy((char *)cfn + ALIGN(sizeof(*cfn)), name) : NULL; - cfn->cfn = fptr; - - return cfn; -} - -static int -uc_regexp_tostring(json_object *jso, struct printbuf *pb, int level, int flags) -{ - bool strict = (level > 0) || (flags & JSON_C_TO_STRING_STRICT); - uc_regexp *re = json_object_get_userdata(jso); - json_object *s; - const char *p; - size_t len; - - sprintbuf(pb, "%s/", strict ? "\"" : ""); - - s = xjs_new_string(re->pattern); - - if (strict) - for (p = json_object_to_json_string(s) + 1, len = strlen(p) - 1; len > 0; len--, p++) - sprintbuf(pb, "%c", *p); - else - sprintbuf(pb, "%s", json_object_get_string(s)); - - uc_value_put(s); - - return sprintbuf(pb, "/%s%s%s%s", - re->global ? "g" : "", - re->icase ? "i" : "", - re->newline ? "s" : "", - strict ? "\"" : ""); -} - -static void -uc_regexp_gc(json_object *jso, void *userdata) -{ - uc_regexp *re = userdata; - - regfree(&re->re); - free(re); -} - -uc_regexp * -uc_regexp_new(const char *pattern, bool icase, bool newline, bool global, char **err) -{ - int cflags = REG_EXTENDED, res; - uc_regexp *re; - size_t len; - - re = uc_object_new(UC_OBJ_REGEXP, ALIGN(sizeof(*re)) + strlen(pattern) + 1, uc_regexp_tostring, uc_regexp_gc); - re->icase = icase; - re->global = global; - re->newline = newline; - re->pattern = strcpy((char *)re + ALIGN(sizeof(*re)), pattern); - - if (icase) - cflags |= REG_ICASE; - - if (newline) - cflags |= REG_NEWLINE; - - res = regcomp(&re->re, pattern, cflags); - - if (res != 0) { - if (err) { - len = regerror(res, &re->re, NULL, 0); - *err = xalloc(len); - - regerror(res, &re->re, *err, len); - } - - uc_value_put(re->header.jso); - - return NULL; - } - - json_object_object_add(re->header.jso, "source", xjs_new_string(pattern)); - json_object_object_add(re->header.jso, "i", xjs_new_boolean(icase)); - json_object_object_add(re->header.jso, "g", xjs_new_boolean(global)); - json_object_object_add(re->header.jso, "s", xjs_new_boolean(newline)); - - return re; -} - -static void -uc_prototype_gc(json_object *jso, void *userdata) -{ - uc_prototype *proto = userdata; - - if (proto->parent) - uc_value_put(proto->parent->header.jso); - - free(proto); -} - -uc_prototype * -uc_prototype_new(uc_prototype *parent) -{ - uc_prototype *proto; - - proto = uc_object_new(UC_OBJ_PROTOTYPE, sizeof(*proto), NULL, uc_prototype_gc); - - if (parent) { - proto->parent = parent; - uc_value_get(parent->header.jso); - } - - return proto; -} - -json_object * -uc_prototype_lookup(uc_prototype *proto, const char *key) -{ - json_object *val; - - for (; proto; proto = proto->parent) - if (json_object_object_get_ex(proto->header.jso, key, &val)) - return val; - - return NULL; -} - -uc_prototype * -uc_protoref_new(json_object *value, uc_prototype *proto) -{ - uc_prototype *ref; - - if (!json_object_is_type(value, json_type_object) && - !json_object_is_type(value, json_type_array)) - return NULL; - - ref = xalloc(sizeof(*ref)); - ref->header.type = UC_OBJ_PROTOTYPE; - ref->header.jso = value; - - if (proto) { - ref->parent = proto; - uc_value_get(proto->header.jso); - } - - json_object_set_serializer(ref->header.jso, NULL, ref, uc_prototype_gc); - - return ref; -} - - -static uc_ressource_types res_types; - -uc_ressource_type * -uc_ressource_type_add(const char *name, uc_prototype *proto, void (*freefn)(void *)) -{ - uc_ressource_type *existing; - - existing = uc_ressource_type_lookup(name); - - if (existing) { - uc_value_put(proto->header.jso); - - return existing; - } - - uc_vector_grow(&res_types); - - res_types.entries[res_types.count].name = name; - res_types.entries[res_types.count].proto = proto; - res_types.entries[res_types.count].free = freefn; - - return &res_types.entries[res_types.count++]; -} - -static uc_ressource_type * -uc_ressource_type_get(size_t type) -{ - return (type < res_types.count) ? &res_types.entries[type] : NULL; -} - -uc_ressource_type * -uc_ressource_type_lookup(const char *name) -{ - size_t i; - - for (i = 0; i < res_types.count; i++) - if (!strcmp(res_types.entries[i].name, name)) - return &res_types.entries[i]; - - return NULL; -} - -static int -uc_ressource_tostring(json_object *jso, struct printbuf *pb, int level, int flags) -{ - bool strict = (level > 0) || (flags & JSON_C_TO_STRING_STRICT); - uc_ressource *res = json_object_get_userdata(jso); - uc_ressource_type *type = uc_ressource_type_get(res->type); - - return sprintbuf(pb, "%s<%s %p>%s", - strict ? "\"" : "", - type ? type->name : "ressource", - res->data, - strict ? "\"" : ""); -} - -static void -uc_ressource_gc(json_object *jso, void *userdata) -{ - uc_ressource *res = userdata; - uc_ressource_type *type = uc_ressource_type_get(res->type); - - if (type && type->free) - type->free(res->data); - - free(res); -} - -uc_ressource * -uc_ressource_new(json_object *jso, uc_ressource_type *type, void *data) -{ - uc_ressource *res; - - if (!jso) - return NULL; - - res = xalloc(sizeof(*res)); - res->header.type = UC_OBJ_RESSOURCE; - res->header.jso = jso; - - res->type = type - res_types.entries; - res->data = data; - - json_object_set_serializer(res->header.jso, uc_ressource_tostring, res, uc_ressource_gc); - - return res; -} - -void ** -uc_ressource_dataptr(json_object *jso, const char *name) -{ - uc_ressource_type *type; - uc_ressource *res; - - if (!uc_object_is_type(jso, UC_OBJ_RESSOURCE)) - return NULL; - - res = uc_object_as_ressource(jso); - - if (name) { - type = uc_ressource_type_lookup(name); - - if (!type || type != uc_ressource_type_get(res->type)) - return NULL; - } - - return &res->data; -} - -uc_prototype * -uc_ressource_prototype(json_object *jso) -{ - uc_ressource_type *type; - uc_ressource *res; - - if (!uc_object_is_type(jso, UC_OBJ_RESSOURCE)) - return NULL; - - res = uc_object_as_ressource(jso); - type = uc_ressource_type_get(res->type); - - return type ? type->proto : NULL; -} - - -#ifdef __GNUC__ - -__attribute__((destructor)) -static void uc_ressource_types_free(void) -{ - size_t i; - - for (i = 0; i < res_types.count; i++) - uc_value_put(res_types.entries[i].proto->header.jso); - - uc_vector_clear(&res_types); -} - -#endif |