summaryrefslogtreecommitdiffhomepage
path: root/object.c
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2020-12-23 20:54:05 +0100
committerJo-Philipp Wich <jo@mein.io>2021-02-17 14:10:51 +0100
commit3756806674da909ec6dc10ad25862b592792604e (patch)
treef2af7e47f8444caaff0a5a33599f381889db24e3 /object.c
parent77580a893283f2bde7ab46496bd3a3d7b2fc6784 (diff)
treewide: rewrite ucode interpreter
Replace the former AST walking interpreter implementation with a single pass bytecode compiler and a corresponding virtual machine. The rewrite lays the groundwork for a couple of improvements with will be subsequently implemented: - Ability to precompile ucode sources into binary byte code - Strippable debug information - Reduced runtime memory usage Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'object.c')
-rw-r--r--object.c432
1 files changed, 432 insertions, 0 deletions
diff --git a/object.c b/object.c
new file mode 100644
index 0000000..5841f76
--- /dev/null
+++ b/object.c
@@ -0,0 +1,432 @@
+/*
+ * 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;
+}
+
+
+static uc_ressource_types res_types;
+
+uc_ressource_type *
+uc_ressource_type_add(const char *name, uc_prototype *proto, void (*freefn)(void *))
+{
+ 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;
+}