summaryrefslogtreecommitdiffhomepage
path: root/types.c
diff options
context:
space:
mode:
Diffstat (limited to 'types.c')
-rw-r--r--types.c1809
1 files changed, 1809 insertions, 0 deletions
diff --git a/types.c b/types.c
new file mode 100644
index 0000000..3ed8aaa
--- /dev/null
+++ b/types.c
@@ -0,0 +1,1809 @@
+/*
+ * Copyright (C) 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 <stdarg.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <endian.h>
+#include <errno.h>
+#include <math.h>
+
+#include "types.h"
+#include "util.h"
+#include "vm.h"
+
+uc_type_t
+ucv_type(uc_value_t *uv)
+{
+ uc_type_t type = ((uintptr_t)uv & 3);
+
+ if (type == UC_NULL && uv != NULL)
+ type = uv->type;
+
+ return type;
+}
+
+const char *
+ucv_typename(uc_value_t *uv)
+{
+ switch (ucv_type(uv)) {
+ case UC_NULL: return "null";
+ case UC_INTEGER: return "integer";
+ case UC_BOOLEAN: return "boolean";
+ case UC_STRING: return "string";
+ case UC_DOUBLE: return "double";
+ case UC_ARRAY: return "array";
+ case UC_OBJECT: return "object";
+ case UC_REGEXP: return "regexp";
+ case UC_FUNCTION: return "function";
+ case UC_CFUNCTION: return "cfunction";
+ case UC_CLOSURE: return "closure";
+ case UC_UPVALUE: return "upvalue";
+ case UC_RESSOURCE: return "ressource";
+ }
+
+ return "unknown";
+}
+
+static uc_ressource_type_t *
+ucv_ressource_type_get(size_t type);
+
+static void
+ucv_unref(uc_weakref_t *ref)
+{
+ ref->prev->next = ref->next;
+ ref->next->prev = ref->prev;
+}
+
+static void
+ucv_ref(uc_weakref_t *head, uc_weakref_t *item)
+{
+ item->next = head->next;
+ item->prev = head;
+ head->next->prev = item;
+ head->next = item;
+}
+
+#if 0
+static uc_weakref_t *
+ucv_get_weakref(uc_value_t *uv)
+{
+ switch (ucv_type(uv)) {
+ case UC_ARRAY:
+ return &((uc_array_t *)uv)->ref;
+
+ case UC_OBJECT:
+ return &((uc_object_t *)uv)->ref;
+
+ case UC_CLOSURE:
+ return &((uc_closure_t *)uv)->ref;
+
+ default:
+ return NULL;
+ }
+}
+#endif
+
+static void
+ucv_put_value(uc_value_t *uv, bool retain)
+{
+ if (uv == NULL || (uintptr_t)uv & 3)
+ return;
+
+ assert(uv->refcount > 0);
+
+ if (uv->refcount > 0)
+ uv->refcount--;
+
+ if (uv->refcount == 0)
+ ucv_free(uv, retain);
+}
+
+static void
+ucv_gc_mark(uc_value_t *uv);
+
+static void
+ucv_gc_mark(uc_value_t *uv)
+{
+ uc_function_t *function;
+ uc_closure_t *closure;
+ uc_upvalref_t *upval;
+ uc_object_t *object;
+ uc_array_t *array;
+ struct lh_entry *entry;
+ size_t i;
+
+ switch (ucv_type(uv)) {
+ case UC_ARRAY:
+ array = (uc_array_t *)uv;
+
+ ucv_gc_mark(array->proto);
+
+ for (i = 0; i < array->count; i++)
+ ucv_gc_mark(array->entries[i]);
+
+ if (array->ref.next)
+ ucv_set_mark(uv);
+
+ break;
+
+ case UC_OBJECT:
+ object = (uc_object_t *)uv;
+
+ ucv_gc_mark(object->proto);
+
+ lh_foreach(object->table, entry)
+ ucv_gc_mark((uc_value_t *)lh_entry_v(entry));
+
+ if (object->ref.next)
+ ucv_set_mark(uv);
+
+ break;
+
+ case UC_CLOSURE:
+ closure = (uc_closure_t *)uv;
+ function = closure->function;
+
+ for (i = 0; i < function->nupvals; i++)
+ ucv_gc_mark((uc_value_t *)closure->upvals[i]);
+
+ ucv_gc_mark((uc_value_t *)function);
+
+ if (closure->ref.next)
+ ucv_set_mark(uv);
+
+ break;
+
+ case UC_UPVALUE:
+ upval = (uc_upvalref_t *)uv;
+ ucv_gc_mark(upval->value);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void
+ucv_free(uc_value_t *uv, bool retain)
+{
+ uc_ressource_type_t *restype;
+ uc_ressource_t *ressource;
+ uc_function_t *function;
+ uc_closure_t *closure;
+ uc_upvalref_t *upval;
+ uc_regexp_t *regexp;
+ uc_object_t *object;
+ uc_array_t *array;
+ uc_weakref_t *ref;
+ size_t i;
+
+ if (uv == NULL || (uintptr_t)uv & 3)
+ return;
+
+ if (uv->mark)
+ return;
+
+ uv->mark = true;
+
+ ref = NULL;
+
+ switch (uv->type) {
+ case UC_ARRAY:
+ array = (uc_array_t *)uv;
+ ref = &array->ref;
+ ucv_put_value(array->proto, retain);
+
+ for (i = 0; i < array->count; i++)
+ ucv_put_value(array->entries[i], retain);
+
+ uc_vector_clear(array);
+ break;
+
+ case UC_OBJECT:
+ object = (uc_object_t *)uv;
+ ref = &object->ref;
+ ucv_put_value(object->proto, retain);
+ lh_table_free(object->table);
+ break;
+
+ case UC_REGEXP:
+ regexp = (uc_regexp_t *)uv;
+ regfree(&regexp->regexp);
+ break;
+
+ case UC_FUNCTION:
+ function = (uc_function_t *)uv;
+ uc_chunk_free(&function->chunk);
+ uc_source_put(function->source);
+ break;
+
+ case UC_CLOSURE:
+ closure = (uc_closure_t *)uv;
+ function = closure->function;
+ ref = &closure->ref;
+
+ for (i = 0; i < function->nupvals; i++)
+ ucv_put_value((uc_value_t *)closure->upvals[i], retain);
+
+ ucv_put_value((uc_value_t *)function, retain);
+ break;
+
+ case UC_RESSOURCE:
+ ressource = (uc_ressource_t *)uv;
+ restype = ucv_ressource_type_get(ressource->type);
+
+ if (restype && restype->free)
+ restype->free(ressource->data);
+
+ break;
+
+ case UC_UPVALUE:
+ upval = (uc_upvalref_t *)uv;
+ ucv_put_value(upval->value, retain);
+ break;
+ }
+
+ if (!ref || !retain) {
+ if (ref && ref->prev && ref->next)
+ ucv_unref(ref);
+
+ free(uv);
+ }
+ else {
+ uv->type = UC_NULL;
+ }
+}
+
+void
+ucv_put(uc_value_t *uv)
+{
+ ucv_put_value(uv, false);
+}
+
+uc_value_t *
+ucv_get(uc_value_t *uv)
+{
+ if (uv == NULL || (uintptr_t)uv & 3)
+ return uv;
+
+ assert(uv->refcount < 0x03ffffff);
+
+ uv->refcount++;
+
+ return uv;
+}
+
+uc_value_t *
+ucv_boolean_new(bool val)
+{
+ uintptr_t pv = UC_BOOLEAN | (val << 2);
+
+ return (uc_value_t *)pv;
+}
+
+bool
+ucv_boolean_get(uc_value_t *uv)
+{
+ uintptr_t pv = (uintptr_t)uv;
+
+ if ((pv & 3) == UC_BOOLEAN)
+ return (pv >> 2) & 1;
+
+ return false;
+}
+
+
+uc_value_t *
+ucv_string_new(const char *str)
+{
+ return ucv_string_new_length(str, strlen(str));
+}
+
+uc_value_t *
+ucv_string_new_length(const char *str, size_t length)
+{
+ uc_string_t *ustr;
+ uintptr_t pv;
+ size_t i;
+ char *s;
+
+ if ((length + 1) < sizeof(void *)) {
+ pv = UC_STRING | (length << 2);
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ s = (char *)&pv + 1;
+#else
+ s = (char *)&pv;
+#endif
+
+ for (i = 0; i < length; i++)
+ s[i] = str[i];
+
+ return (uc_value_t *)pv;
+ }
+
+ ustr = xalloc(sizeof(*ustr) + length + 1);
+ ustr->header.type = UC_STRING;
+ ustr->header.refcount = 1;
+ ustr->length = length;
+ memcpy(ustr->str, str, length);
+
+ return &ustr->header;
+}
+
+uc_stringbuf_t *
+ucv_stringbuf_new(void)
+{
+ uc_stringbuf_t *sb = xprintbuf_new();
+ uc_string_t ustr = {
+ .header = {
+ .type = UC_STRING,
+ .refcount = 1
+ }
+ };
+
+ printbuf_memappend_fast(sb, (char *)&ustr, (int)sizeof(ustr));
+
+ return sb;
+}
+
+uc_value_t *
+ucv_stringbuf_finish(uc_stringbuf_t *sb)
+{
+ uc_string_t *ustr = (uc_string_t *)sb->buf;
+
+ ustr->length = printbuf_length(sb) - offsetof(uc_string_t, str);
+
+ free(sb);
+
+ return &ustr->header;
+}
+
+char *
+_ucv_string_get(uc_value_t **uv)
+{
+ uc_string_t *str;
+
+ switch ((uintptr_t)*uv & 3) {
+ case UC_STRING:
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ return (char *)uv + 1;
+#else
+ return (char *)uv;
+#endif
+
+ case UC_NULL:
+ if (*uv != NULL && (*uv)->type == UC_STRING) {
+ str = (uc_string_t *)*uv;
+
+ return str->str;
+ }
+ }
+
+ return NULL;
+}
+
+size_t
+ucv_string_length(uc_value_t *uv)
+{
+ uc_string_t *str = (uc_string_t *)uv;
+ uintptr_t pv = (uintptr_t)uv;
+
+ if ((pv & 3) == UC_STRING)
+ return (pv & 0xff) >> 2;
+ else if (uv != NULL && uv->type == UC_STRING)
+ return str->length;
+
+ return 0;
+}
+
+
+uc_value_t *
+ucv_int64_new(int64_t n)
+{
+ uint64_t uval = (n < 0) ? ((n > INT64_MIN) ? (~n + 1) : INT64_MAX) : n;
+ uint64_t max = (1ULL << ((sizeof(void *) * 8) - 3)) - 1;
+ uc_integer_t *integer;
+ uintptr_t pv;
+
+ if (uval <= max) {
+ pv = UC_INTEGER | ((n < 0) << 2) | (uval << 3);
+
+ return (uc_value_t *)pv;
+ }
+
+ integer = xalloc(sizeof(*integer));
+ integer->header.type = UC_INTEGER;
+ integer->header.refcount = 1;
+ integer->header.u64 = 0;
+ integer->i.s64 = n;
+
+ return &integer->header;
+}
+
+uc_value_t *
+ucv_uint64_new(uint64_t n)
+{
+ uint64_t max = (1ULL << ((sizeof(void *) * 8) - 3)) - 1;
+ uc_integer_t *integer;
+ uintptr_t pv;
+
+ if (n <= max) {
+ pv = UC_INTEGER | (n << 3);
+
+ return (uc_value_t *)pv;
+ }
+
+ integer = xalloc(sizeof(*integer));
+ integer->header.type = UC_INTEGER;
+ integer->header.refcount = 1;
+ integer->header.u64 = 1;
+ integer->i.u64 = n;
+
+ return &integer->header;
+}
+
+uint64_t
+ucv_uint64_get(uc_value_t *uv)
+{
+ uintptr_t pv = (uintptr_t)uv;
+ uc_integer_t *integer;
+
+ errno = 0;
+
+ if ((pv & 3) == UC_INTEGER) {
+ if (((pv >> 2) & 1) == 0)
+ return (uint64_t)(pv >> 3);
+
+ errno = ERANGE;
+
+ return 0;
+ }
+ else if (uv != NULL && uv->type == UC_INTEGER) {
+ integer = (uc_integer_t *)uv;
+
+ if (integer->header.u64)
+ return integer->i.u64;
+
+ if (integer->i.s64 >= 0)
+ return (uint64_t)integer->i.s64;
+
+ errno = ERANGE;
+
+ return 0;
+ }
+
+ errno = EINVAL;
+
+ return 0;
+}
+
+int64_t
+ucv_int64_get(uc_value_t *uv)
+{
+ uintptr_t pv = (uintptr_t)uv;
+ uc_integer_t *integer;
+
+ errno = 0;
+
+ if ((pv & 3) == UC_INTEGER) {
+ if (((pv >> 2) & 1) == 0)
+ return (int64_t)(pv >> 3);
+
+ return -(int64_t)(pv >> 3);
+ }
+ else if (uv != NULL && uv->type == UC_INTEGER) {
+ integer = (uc_integer_t *)uv;
+
+ if (integer->header.u64 && integer->i.u64 <= INT64_MAX)
+ return (int64_t)integer->i.u64;
+
+ if (!integer->header.u64)
+ return integer->i.s64;
+
+ errno = ERANGE;
+
+ return 0;
+ }
+
+ errno = EINVAL;
+
+ return 0;
+}
+
+
+uc_value_t *
+ucv_double_new(double d)
+{
+ uc_double_t *dbl;
+
+ dbl = xalloc(sizeof(*dbl));
+ dbl->header.type = UC_DOUBLE;
+ dbl->header.refcount = 1;
+ dbl->dbl = d;
+
+ return &dbl->header;
+}
+
+double
+ucv_double_get(uc_value_t *uv)
+{
+ uc_double_t *dbl;
+
+ errno = 0;
+
+ if (ucv_type(uv) != UC_DOUBLE) {
+ errno = EINVAL;
+
+ return NAN;
+ }
+
+ dbl = (uc_double_t *)uv;
+
+ return dbl->dbl;
+}
+
+
+uc_value_t *
+ucv_array_new(uc_vm *vm)
+{
+ return ucv_array_new_length(vm, 0);
+}
+
+uc_value_t *
+ucv_array_new_length(uc_vm *vm, size_t length)
+{
+ uc_array_t *array;
+
+ /* XXX */
+ length = 0;
+
+ array = xalloc(sizeof(*array) + length * sizeof(array->entries[0]));
+ array->header.type = UC_ARRAY;
+ array->header.refcount = 1;
+
+ if (length > 0)
+ array->count = length;
+
+ uc_vector_grow(array);
+
+ if (vm)
+ ucv_ref(&vm->values, &array->ref);
+
+ return &array->header;
+}
+
+uc_value_t *
+ucv_array_pop(uc_value_t *uv)
+{
+ uc_array_t *array = (uc_array_t *)uv;
+ uc_value_t *item;
+
+ if (ucv_type(uv) != UC_ARRAY || array->count == 0)
+ return NULL;
+
+ item = ucv_get(array->entries[array->count - 1]);
+
+ ucv_array_delete(uv, array->count - 1, 1);
+
+ return item;
+}
+
+uc_value_t *
+ucv_array_push(uc_value_t *uv, uc_value_t *item)
+{
+ uc_array_t *array = (uc_array_t *)uv;
+
+ if (ucv_type(uv) != UC_ARRAY)
+ return NULL;
+
+ ucv_array_set(uv, array->count, item);
+
+ return item;
+}
+
+uc_value_t *
+ucv_array_shift(uc_value_t *uv)
+{
+ uc_array_t *array = (uc_array_t *)uv;
+ uc_value_t *item;
+
+ if (ucv_type(uv) != UC_ARRAY || array->count == 0)
+ return NULL;
+
+ item = ucv_get(array->entries[0]);
+
+ ucv_array_delete(uv, 0, 1);
+
+ return item;
+}
+
+uc_value_t *
+ucv_array_unshift(uc_value_t *uv, uc_value_t *item)
+{
+ uc_array_t *array = (uc_array_t *)uv;
+ size_t i;
+
+ if (ucv_type(uv) != UC_ARRAY || array->count == 0)
+ return NULL;
+
+ array->count++;
+ uc_vector_grow(array);
+
+ for (i = array->count; i > 1; i--)
+ array->entries[i - 1] = array->entries[i - 2];
+
+ array->entries[0] = item;
+
+ return item;
+}
+
+void
+ucv_array_sort(uc_value_t *uv, int (*cmp)(const void *, const void *))
+{
+ uc_array_t *array = (uc_array_t *)uv;
+
+ if (ucv_type(uv) != UC_ARRAY || array->count <= 1)
+ return;
+
+ qsort(array->entries, array->count, sizeof(array->entries[0]), cmp);
+}
+
+bool
+ucv_array_delete(uc_value_t *uv, size_t offset, size_t count)
+{
+ uc_array_t *array = (uc_array_t *)uv;
+ size_t i;
+
+ if (ucv_type(uv) != UC_ARRAY || array->count == 0)
+ return false;
+
+ if (offset >= array->count)
+ return false;
+
+ if ((offset + count) < offset)
+ return false;
+
+ if ((offset + count) > array->count)
+ count = array->count - offset;
+
+ for (i = 0; i < count; i++)
+ ucv_put(array->entries[offset + i]);
+
+ memmove(&array->entries[offset],
+ &array->entries[offset + count],
+ (array->count - (offset + count)) * sizeof(array->entries[0]));
+
+ array->count -= count;
+
+ uc_vector_grow(array);
+
+ return true;
+}
+
+bool
+ucv_array_set(uc_value_t *uv, size_t index, uc_value_t *item)
+{
+ uc_array_t *array = (uc_array_t *)uv;
+
+ if (ucv_type(uv) != UC_ARRAY)
+ return false;
+
+ if (index >= array->count) {
+ array->count = index + 1;
+ uc_vector_grow(array);
+ }
+ else {
+ ucv_put(array->entries[index]);
+ }
+
+ array->entries[index] = item;
+
+ return true;
+}
+
+uc_value_t *
+ucv_array_get(uc_value_t *uv, size_t index)
+{
+ uc_array_t *array = (uc_array_t *)uv;
+
+ if (ucv_type(uv) != UC_ARRAY)
+ return NULL;
+
+ if (index >= array->count)
+ return NULL;
+
+ return array->entries[index];
+}
+size_t
+ucv_array_length(uc_value_t *uv)
+{
+ uc_array_t *array = (uc_array_t *)uv;
+
+ if (ucv_type(uv) != UC_ARRAY)
+ return 0;
+
+ return array->count;
+}
+
+
+static void
+ucv_free_object_entry(struct lh_entry *entry)
+{
+ free(lh_entry_k(entry));
+ ucv_put(lh_entry_v(entry));
+}
+
+uc_value_t *
+ucv_object_new(uc_vm *vm)
+{
+ struct lh_table *table;
+ uc_object_t *object;
+
+ table = lh_kchar_table_new(16, ucv_free_object_entry);
+
+ if (!table) {
+ fprintf(stderr, "Out of memory\n");
+ abort();
+ }
+
+ object = xalloc(sizeof(*object));
+ object->header.type = UC_OBJECT;
+ object->header.refcount = 1;
+ object->table = table;
+
+ if (vm)
+ ucv_ref(&vm->values, &object->ref);
+
+ return &object->header;
+}
+
+bool
+ucv_object_add(uc_value_t *uv, const char *key, uc_value_t *val)
+{
+ uc_object_t *object = (uc_object_t *)uv;
+ struct lh_entry *existing_entry;
+ uc_value_t *existing_value;
+ unsigned long hash;
+ void *k;
+
+ if (ucv_type(uv) != UC_OBJECT)
+ return false;
+
+ hash = lh_get_hash(object->table, (const void *)key);
+ existing_entry = lh_table_lookup_entry_w_hash(object->table, (const void *)key, hash);
+
+ if (existing_entry == NULL) {
+ k = xstrdup(key);
+
+ if (lh_table_insert_w_hash(object->table, k, val, hash, 0) != 0) {
+ free(k);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ existing_value = (uc_value_t *)lh_entry_v(existing_entry);
+
+ if (existing_value)
+ ucv_put(existing_value);
+
+ existing_entry->v = val;
+
+ return true;
+}
+
+bool
+ucv_object_delete(uc_value_t *uv, const char *key)
+{
+ uc_object_t *object = (uc_object_t *)uv;
+
+ if (ucv_type(uv) != UC_OBJECT)
+ return false;
+
+ return (lh_table_delete(object->table, key) == 0);
+}
+
+uc_value_t *
+ucv_object_get(uc_value_t *uv, const char *key, bool *found)
+{
+ uc_object_t *object = (uc_object_t *)uv;
+ uc_value_t *val = NULL;
+ bool rv;
+
+ if (found != NULL)
+ *found = false;
+
+ if (ucv_type(uv) != UC_OBJECT)
+ return NULL;
+
+ rv = lh_table_lookup_ex(object->table, (const void *)key, (void **)&val);
+
+ if (found != NULL)
+ *found = rv;
+
+ return val;
+}
+
+size_t
+ucv_object_length(uc_value_t *uv)
+{
+ uc_object_t *object = (uc_object_t *)uv;
+
+ if (ucv_type(uv) != UC_OBJECT)
+ return 0;
+
+ return lh_table_length(object->table);
+}
+
+
+uc_value_t *
+ucv_function_new(const char *name, size_t srcpos, uc_source *source)
+{
+ size_t namelen = 0;
+ uc_function_t *fn;
+
+ if (name)
+ namelen = strlen(name);
+
+ fn = xalloc(sizeof(*fn) + namelen + 1);
+ fn->header.type = UC_FUNCTION;
+ fn->header.refcount = 1;
+
+ if (name)
+ strcpy(fn->name, name);
+
+ 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->header;
+}
+
+size_t
+ucv_function_srcpos(uc_value_t *uv, size_t off)
+{
+ uc_function_t *fn = (uc_function_t *)uv;
+ size_t pos;
+
+ if (ucv_type(uv) != UC_FUNCTION)
+ return 0;
+
+ pos = uc_chunk_debug_get_srcpos(&fn->chunk, off);
+
+ return pos ? fn->srcpos + pos : 0;
+}
+
+
+uc_value_t *
+ucv_cfunction_new(const char *name, uc_cfn_ptr_t fptr)
+{
+ uc_cfunction_t *cfn;
+ size_t namelen = 0;
+
+ if (name)
+ namelen = strlen(name);
+
+ cfn = xalloc(sizeof(*cfn) + namelen + 1);
+ cfn->header.type = UC_CFUNCTION;
+ cfn->header.refcount = 1;
+
+ if (name)
+ strcpy(cfn->name, name);
+
+ cfn->cfn = fptr;
+
+ return &cfn->header;
+}
+
+
+uc_value_t *
+ucv_closure_new(uc_vm *vm, uc_function_t *function, bool arrow_fn)
+{
+ uc_closure_t *closure;
+
+ closure = xalloc(sizeof(*closure) + (sizeof(uc_upvalref_t *) * function->nupvals));
+ closure->header.type = UC_CLOSURE;
+ closure->header.refcount = 1;
+ closure->function = function;
+ closure->is_arrow = arrow_fn;
+ closure->upvals = function->nupvals ? (uc_upvalref_t **)((uintptr_t)closure + ALIGN(sizeof(*closure))) : NULL;
+
+ if (vm)
+ ucv_ref(&vm->values, &closure->ref);
+
+ return &closure->header;
+}
+
+
+static uc_ressource_types_t res_types;
+
+uc_ressource_type_t *
+ucv_ressource_type_add(const char *name, uc_value_t *proto, void (*freefn)(void *))
+{
+ uc_ressource_type_t *existing;
+
+ existing = ucv_ressource_type_lookup(name);
+
+ if (existing) {
+ ucv_put(proto);
+
+ 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_t *
+ucv_ressource_type_get(size_t type)
+{
+ return (type > 0 && type <= res_types.count) ? &res_types.entries[type - 1] : NULL;
+}
+
+uc_ressource_type_t *
+ucv_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;
+}
+
+
+uc_value_t *
+ucv_ressource_new(uc_ressource_type_t *type, void *data)
+{
+ uc_ressource_t *res;
+
+ res = xalloc(sizeof(*res));
+ res->header.type = UC_RESSOURCE;
+ res->header.refcount = 1;
+ res->type = type ? (type - res_types.entries) + 1 : 0;
+ res->data = data;
+
+ return &res->header;
+}
+
+void **
+ucv_ressource_dataptr(uc_value_t *uv, const char *name)
+{
+ uc_ressource_t *res = (uc_ressource_t *)uv;
+ uc_ressource_type_t *type;
+
+ if (ucv_type(uv) != UC_RESSOURCE)
+ return NULL;
+
+ if (name) {
+ type = ucv_ressource_type_lookup(name);
+
+ if (!type || type != ucv_ressource_type_get(res->type))
+ return NULL;
+ }
+
+ return &res->data;
+}
+
+
+uc_value_t *
+ucv_regexp_new(const char *pattern, bool icase, bool newline, bool global, char **error)
+{
+ int cflags = REG_EXTENDED, res;
+ uc_regexp_t *re;
+ size_t len;
+
+ re = xalloc(sizeof(*re) + strlen(pattern) + 1);
+ re->header.type = UC_REGEXP;
+ re->header.refcount = 1;
+ re->icase = icase;
+ re->global = global;
+ re->newline = newline;
+ strcpy(re->source, pattern);
+
+ if (icase)
+ cflags |= REG_ICASE;
+
+ if (newline)
+ cflags |= REG_NEWLINE;
+
+ res = regcomp(&re->regexp, pattern, cflags);
+
+ if (res != 0) {
+ if (error) {
+ len = regerror(res, &re->regexp, NULL, 0);
+ *error = xalloc(len);
+
+ regerror(res, &re->regexp, *error, len);
+ }
+
+ free(re);
+
+ 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->header;
+}
+
+
+uc_value_t *
+ucv_upvalref_new(size_t slot)
+{
+ uc_upvalref_t *up;
+
+ up = xalloc(sizeof(*up));
+ up->header.type = UC_UPVALUE;
+ up->header.refcount = 1;
+ up->slot = slot;
+
+ return &up->header;
+}
+
+
+uc_value_t *
+ucv_prototype_get(uc_value_t *uv)
+{
+ uc_ressource_type_t *restype;
+ uc_ressource_t *ressource;
+ uc_object_t *object;
+ uc_array_t *array;
+
+ switch (ucv_type(uv)) {
+ case UC_ARRAY:
+ array = (uc_array_t *)uv;
+
+ return array->proto;
+
+ case UC_OBJECT:
+ object = (uc_object_t *)uv;
+
+ return object->proto;
+
+ case UC_RESSOURCE:
+ ressource = (uc_ressource_t *)uv;
+ restype = ucv_ressource_type_get(ressource->type);
+
+ return restype ? restype->proto : NULL;
+
+ default:
+ return NULL;
+ }
+}
+
+bool
+ucv_prototype_set(uc_value_t *uv, uc_value_t *proto)
+{
+ uc_object_t *object;
+ uc_array_t *array;
+
+ if (ucv_type(proto) != UC_OBJECT)
+ return false;
+
+ switch (ucv_type(uv)) {
+ case UC_ARRAY:
+ array = (uc_array_t *)uv;
+ array->proto = proto;
+
+ return true;
+
+ case UC_OBJECT:
+ object = (uc_object_t *)uv;
+ object->proto = proto;
+
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+uc_value_t *
+ucv_property_get(uc_value_t *uv, const char *key)
+{
+ uc_value_t *val;
+ bool found;
+
+ for (; uv; uv = ucv_prototype_get(uv)) {
+ val = ucv_object_get(uv, key, &found);
+
+ if (found)
+ return val;
+ }
+
+ return NULL;
+}
+
+
+uc_value_t *
+ucv_from_json(uc_vm *vm, json_object *jso)
+{
+ //uc_array_t *arr;
+ uc_value_t *uv, *item;
+ int64_t n;
+ size_t i;
+
+ switch (json_object_get_type(jso)) {
+ case json_type_null:
+ return NULL;
+
+ case json_type_boolean:
+ return ucv_boolean_new(json_object_get_boolean(jso));
+
+ case json_type_double:
+ return ucv_double_new(json_object_get_double(jso));
+
+ case json_type_int:
+ n = json_object_get_int64(jso);
+
+ if (n == INT64_MAX)
+ return ucv_uint64_new(json_object_get_uint64(jso));
+
+ return ucv_int64_new(n);
+
+ case json_type_object:
+ uv = ucv_object_new(vm);
+
+ json_object_object_foreach(jso, key, val) {
+ item = ucv_from_json(vm, val);
+
+ if (!ucv_object_add(uv, key, item))
+ ucv_put(item);
+
+#ifdef __clang_analyzer__
+ /* Clang static analyzer does not understand that the object retains
+ * our item so pretend to free it here to suppress the false positive
+ * memory leak warning. */
+ ucv_put(item);
+#endif
+ }
+
+ return uv;
+
+ case json_type_array:
+ /* XXX
+ arr = (uc_array_t *)ucv_array_new_length(vm, json_object_array_length(jso));
+
+ for (i = 0; i < arr->count; i++)
+ arr->entries[i] = ucv_from_json(vm, json_object_array_get_idx(jso, i));
+
+ return &arr->header;
+ */
+ uv = ucv_array_new(vm);
+
+ for (i = 0; i < json_object_array_length(jso); i++) {
+ item = ucv_from_json(vm, json_object_array_get_idx(jso, i));
+
+ if (!ucv_array_push(uv, item))
+ ucv_put(item);
+
+#ifdef __clang_analyzer__
+ /* Clang static analyzer does not understand that the array retains
+ * our item so pretend to free it here to suppress the false positive
+ * memory leak warning. */
+ ucv_put(item);
+#endif
+ }
+
+ return uv;
+
+ case json_type_string:
+ return ucv_string_new_length(json_object_get_string(jso), json_object_get_string_len(jso));
+ }
+
+ return NULL;
+}
+
+json_object *
+ucv_to_json(uc_value_t *uv)
+{
+ uc_regexp_t *regexp;
+ uc_array_t *array;
+ json_object *jso;
+ size_t i;
+ char *s;
+
+ switch (ucv_type(uv)) {
+ case UC_BOOLEAN:
+ return json_object_new_boolean(ucv_boolean_get(uv));
+
+ case UC_INTEGER:
+ if (ucv_is_u64(uv))
+ return json_object_new_uint64(ucv_uint64_get(uv));
+
+ return json_object_new_int64(ucv_int64_get(uv));
+
+ case UC_DOUBLE:
+ return json_object_new_double(ucv_double_get(uv));
+
+ case UC_STRING:
+ return json_object_new_string_len(ucv_string_get(uv), ucv_string_length(uv));
+
+ case UC_ARRAY:
+ array = (uc_array_t *)uv;
+ jso = json_object_new_array_ext(array->count);
+
+ for (i = 0; i < array->count; i++)
+ json_object_array_put_idx(jso, i, ucv_to_json(array->entries[i]));
+
+ return jso;
+
+ case UC_OBJECT:
+ jso = json_object_new_object();
+
+ ucv_object_foreach(uv, key, val)
+ json_object_object_add(jso, key, ucv_to_json(val));
+
+ return jso;
+
+ case UC_REGEXP:
+ regexp = (uc_regexp_t *)uv;
+ i = asprintf(&s, "/%s/%s%s%s",
+ regexp->source,
+ regexp->global ? "g" : "",
+ regexp->icase ? "i" : "",
+ regexp->newline ? "s" : "");
+
+ if (i <= 0)
+ return NULL;
+
+ jso = json_object_new_string_len(s, i);
+
+ free(s);
+
+ return jso;
+
+ case UC_CLOSURE:
+ case UC_CFUNCTION:
+ case UC_FUNCTION:
+ case UC_RESSOURCE:
+ case UC_UPVALUE:
+ case UC_NULL:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+static void
+ucv_to_string_json_encoded(uc_stringbuf_t *pb, const char *s, size_t len, bool regexp)
+{
+ size_t i;
+
+ if (!regexp)
+ ucv_stringbuf_append(pb, "\"");
+
+ for (i = 0; s != NULL && i < len; i++, s++) {
+ switch (*s) {
+ case '"':
+ ucv_stringbuf_append(pb, "\\\"");
+ break;
+
+ case '\\':
+ ucv_stringbuf_append(pb, "\\\\");
+ break;
+
+ case '\b':
+ ucv_stringbuf_append(pb, "\\b");
+ break;
+
+ case '\f':
+ ucv_stringbuf_append(pb, "\\f");
+ break;
+
+ case '\n':
+ ucv_stringbuf_append(pb, "\\n");
+ break;
+
+ case '\r':
+ ucv_stringbuf_append(pb, "\\r");
+ break;
+
+ case '\t':
+ ucv_stringbuf_append(pb, "\\t");
+ break;
+
+ case '/':
+ if (regexp)
+ ucv_stringbuf_append(pb, "\\");
+
+ ucv_stringbuf_append(pb, "/");
+ break;
+
+ default:
+ if (*s < 0x20)
+ ucv_stringbuf_printf(pb, "\\u%04x", *s);
+ else
+ ucv_stringbuf_addstr(pb, s, 1);
+ break;
+ }
+ }
+
+ if (!regexp)
+ ucv_stringbuf_append(pb, "\"");
+}
+
+static bool
+ucv_call_tostring(uc_vm *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json)
+{
+ uc_value_t *proto = ucv_prototype_get(uv);
+ uc_value_t *tostr = ucv_object_get(proto, "tostring", NULL);
+ uc_value_t *str;
+ size_t l;
+ char *s;
+
+ if (!ucv_is_callable(tostr))
+ return false;
+
+ uc_vm_stack_push(vm, ucv_get(uv));
+ uc_vm_stack_push(vm, ucv_get(tostr));
+
+ if (uc_vm_call(vm, true, 0) != EXCEPTION_NONE)
+ return false;
+
+ str = uc_vm_stack_pop(vm);
+
+ if (ucv_type(str) == UC_STRING) {
+ s = ucv_string_get(str);
+ l = ucv_string_length(str);
+
+ if (json)
+ ucv_to_string_json_encoded(pb, s, l, false);
+ else if (s)
+ ucv_stringbuf_addstr(pb, s, l);
+ }
+ else if (json) {
+ ucv_stringbuf_append(pb, "\"\"");
+ }
+
+ ucv_put(str);
+
+ return true;
+}
+
+void
+_ucv_stringbuf_append(uc_stringbuf_t *pb, const char *str, size_t len)
+{
+ printbuf_memappend_fast(pb, str, (int)len);
+}
+
+void
+ucv_to_stringbuf(uc_vm *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json)
+{
+ uc_ressource_type_t *restype;
+ uc_ressource_t *ressource;
+ uc_cfunction_t *cfunction;
+ uc_function_t *function;
+ uc_closure_t *closure;
+ uc_regexp_t *regexp;
+ uc_value_t *argname;
+ uc_array_t *array;
+ size_t i, l;
+ double d;
+ char *s;
+
+ if (ucv_is_marked(uv)) {
+ ucv_stringbuf_append(pb, "null");
+
+ return;
+ }
+
+ if (vm != NULL && ucv_call_tostring(vm, pb, uv, json))
+ return;
+
+ ucv_set_mark(uv);
+
+ switch (ucv_type(uv)) {
+ case UC_NULL:
+ ucv_stringbuf_append(pb, "null");
+ break;
+
+ case UC_BOOLEAN:
+ if (ucv_boolean_get(uv))
+ ucv_stringbuf_append(pb, "true");
+ else
+ ucv_stringbuf_append(pb, "false");
+ break;
+
+ case UC_INTEGER:
+ if (ucv_is_u64(uv))
+ ucv_stringbuf_printf(pb, "%" PRIu64, ucv_uint64_get(uv));
+ else
+ ucv_stringbuf_printf(pb, "%" PRId64, ucv_int64_get(uv));
+ break;
+
+ case UC_DOUBLE:
+ d = ucv_double_get(uv);
+
+ if (json && isnan(d))
+ ucv_stringbuf_append(pb, "\"NaN\"");
+ else if (json && d == INFINITY)
+ ucv_stringbuf_append(pb, "1e309");
+ else if (json && d == -INFINITY)
+ ucv_stringbuf_append(pb, "-1e309");
+ else if (isnan(d))
+ ucv_stringbuf_append(pb, "NaN");
+ else if (d == INFINITY)
+ ucv_stringbuf_append(pb, "Infinity");
+ else if (d == -INFINITY)
+ ucv_stringbuf_append(pb, "-Infinity");
+ else
+ ucv_stringbuf_printf(pb, "%g", d);
+
+ break;
+
+ case UC_STRING:
+ s = ucv_string_get(uv);
+ l = ucv_string_length(uv);
+
+ if (s) {
+ if (json)
+ ucv_to_string_json_encoded(pb, s, l, false);
+ else
+ ucv_stringbuf_addstr(pb, s, l);
+ }
+
+ break;
+
+ case UC_ARRAY:
+ array = (uc_array_t *)uv;
+
+ ucv_stringbuf_append(pb, "[");
+
+ for (i = 0; i < array->count; i++) {
+ if (i)
+ ucv_stringbuf_append(pb, ", ");
+ else
+ ucv_stringbuf_append(pb, " ");
+
+ ucv_to_stringbuf(vm, pb, array->entries[i], true);
+ }
+
+ ucv_stringbuf_append(pb, " ]");
+ break;
+
+ case UC_OBJECT:
+ ucv_stringbuf_append(pb, "{");
+
+ i = 0;
+ ucv_object_foreach(uv, key, val) {
+ if (i++)
+ ucv_stringbuf_append(pb, ", ");
+ else
+ ucv_stringbuf_append(pb, " ");
+
+ ucv_to_string_json_encoded(pb, key, strlen(key), false);
+ ucv_stringbuf_append(pb, ": ");
+ ucv_to_stringbuf(vm, pb, val, true);
+ }
+
+ ucv_stringbuf_append(pb, " }");
+ break;
+
+ case UC_REGEXP:
+ regexp = (uc_regexp_t *)uv;
+
+ if (json)
+ ucv_stringbuf_append(pb, "\"");
+
+ ucv_stringbuf_append(pb, "/");
+ ucv_to_string_json_encoded(pb, regexp->source, strlen(regexp->source), true);
+ ucv_stringbuf_append(pb, "/");
+
+ if (regexp->global)
+ ucv_stringbuf_append(pb, "g");
+
+ if (regexp->icase)
+ ucv_stringbuf_append(pb, "i");
+
+ if (regexp->newline)
+ ucv_stringbuf_append(pb, "s");
+
+ if (json)
+ ucv_stringbuf_append(pb, "\"");
+
+ break;
+
+ case UC_CLOSURE:
+ closure = (uc_closure_t *)uv;
+ function = closure->function;
+
+ if (json)
+ ucv_stringbuf_append(pb, "\"");
+
+ if (!closure->is_arrow) {
+ ucv_stringbuf_append(pb, "function");
+
+ if (function->name[0]) {
+ ucv_stringbuf_append(pb, " ");
+ ucv_stringbuf_addstr(pb, function->name, strlen(function->name));
+ }
+ }
+
+ ucv_stringbuf_append(pb, "(");
+
+ for (i = 1; i <= function->nargs; i++) {
+ argname = uc_chunk_debug_get_variable(&function->chunk, i - 1, i, false);
+
+ if (i > 1)
+ ucv_stringbuf_append(pb, ", ");
+
+ if (i == function->nargs && function->vararg)
+ ucv_stringbuf_append(pb, "...");
+
+ if (argname) {
+ s = ucv_string_get(argname);
+ l = ucv_string_length(argname);
+
+ if (s)
+ ucv_stringbuf_addstr(pb, s, l);
+
+ ucv_put(argname);
+
+ continue;
+ }
+
+ ucv_stringbuf_printf(pb, "[arg%zu]", i);
+ }
+
+ ucv_stringbuf_printf(pb, ")%s { ... }%s",
+ closure->is_arrow ? " =>" : "",
+ json ? "\"" : "");
+
+ break;
+
+ case UC_CFUNCTION:
+ cfunction = (uc_cfunction_t *)uv;
+
+ ucv_stringbuf_printf(pb, "%sfunction%s%s(...) { [native code] }%s",
+ json ? "\"" : "",
+ cfunction->name[0] ? " " : "",
+ cfunction->name[0] ? cfunction->name : "",
+ json ? "\"" : "");
+
+ break;
+
+ case UC_FUNCTION:
+ ucv_stringbuf_printf(pb, "%s<function %p>%s",
+ json ? "\"" : "",
+ uv,
+ json ? "\"" : "");
+
+ break;
+
+ case UC_RESSOURCE:
+ ressource = (uc_ressource_t *)uv;
+ restype = ucv_ressource_type_get(ressource->type);
+
+ ucv_stringbuf_printf(pb, "%s<%s %p>%s",
+ json ? "\"" : "",
+ restype ? restype->name : "ressource",
+ ressource->data,
+ json ? "\"" : "");
+
+ break;
+
+ case UC_UPVALUE:
+ ucv_stringbuf_printf(pb, "%s<upvalref %p>%s",
+ json ? "\"" : "",
+ uv,
+ json ? "\"" : "");
+
+ break;
+ }
+
+ ucv_clear_mark(uv);
+}
+
+static char *
+ucv_to_string_any(uc_vm *vm, uc_value_t *uv, bool json)
+{
+ uc_stringbuf_t *pb = xprintbuf_new();
+ char *rv;
+
+ ucv_to_stringbuf(vm, pb, uv, json);
+
+ rv = pb->buf;
+
+ free(pb);
+
+ return rv;
+}
+
+char *
+ucv_to_string(uc_vm *vm, uc_value_t *uv)
+{
+ return ucv_to_string_any(vm, uv, false);
+}
+
+char *
+ucv_to_jsonstring(uc_vm *vm, uc_value_t *uv)
+{
+ return ucv_to_string_any(vm, uv, true);
+}
+
+bool
+ucv_equal(uc_value_t *uv1, uc_value_t *uv2)
+{
+ uc_type_t t1 = ucv_type(uv1);
+ uc_type_t t2 = ucv_type(uv2);
+ const char *s1, *s2;
+ uint64_t u1, u2;
+ int64_t n1, n2;
+ bool b1, b2;
+
+ if (t1 != t2)
+ return false;
+
+ if (uv1 == uv2)
+ return true;
+
+ switch (t1) {
+ case UC_NULL:
+ return true;
+
+ case UC_BOOLEAN:
+ return ucv_boolean_get(uv1) == ucv_boolean_get(uv2);
+
+ case UC_DOUBLE:
+ return ucv_double_get(uv1) == ucv_double_get(uv2);
+
+ case UC_INTEGER:
+ n1 = ucv_int64_get(uv1);
+ b1 = (errno == 0);
+
+ n2 = ucv_int64_get(uv2);
+ b2 = (errno == 0);
+
+ if (b1 && b2)
+ return (n1 == n2);
+
+ u1 = ucv_uint64_get(uv1);
+ b1 = (errno == 0);
+
+ u2 = ucv_uint64_get(uv2);
+ b2 = (errno == 0);
+
+ if (b1 && b2)
+ return (u1 == u2);
+
+ return false;
+
+ case UC_STRING:
+ s1 = ucv_string_get(uv1);
+ s2 = ucv_string_get(uv2);
+ u1 = ucv_string_length(uv1);
+ u2 = ucv_string_length(uv2);
+
+ if (s1 == NULL || s2 == NULL || u1 != u2)
+ return false;
+
+ return (memcmp(s1, s2, u1) == 0);
+
+ case UC_ARRAY:
+ u1 = ucv_array_length(uv1);
+ u2 = ucv_array_length(uv2);
+
+ if (u1 != u2)
+ return false;
+
+ for (u1 = 0; u1 < u2; u1++)
+ if (!ucv_equal(ucv_array_get(uv1, u1), ucv_array_get(uv2, u1)))
+ return false;
+
+ return true;
+
+ case UC_OBJECT:
+ u1 = ucv_object_length(uv1);
+ u2 = ucv_object_length(uv2);
+
+ if (u1 != u2)
+ return false;
+
+ ucv_object_foreach(uv1, key, val) {
+ if (!ucv_equal(val, ucv_object_get(uv2, key, NULL)))
+ return false;
+ }
+
+ ucv_object_foreach(uv2, key2, val2) {
+ (void)val2;
+ ucv_object_get(uv1, key2, &b1);
+
+ if (!b1)
+ return false;
+ }
+
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+void
+ucv_gc(uc_vm *vm, bool final)
+{
+ uc_weakref_t *ref, *tmp;
+ uc_value_t *val;
+ size_t i;
+
+ if (!final) {
+ /* mark reachable objects */
+ ucv_gc_mark(vm->globals);
+
+ for (i = 0; i < vm->callframes.count; i++)
+ ucv_gc_mark(vm->callframes.entries[i].ctx);
+
+ for (i = 0; i < vm->stack.count; i++)
+ ucv_gc_mark(vm->stack.entries[i]);
+ }
+
+ /* unref unreachable objects */
+ for (ref = vm->values.next; ref != &vm->values; ref = ref->next) {
+ val = (uc_value_t *)((uintptr_t)ref - offsetof(uc_array_t, ref));
+
+ if (ucv_is_marked(val))
+ ucv_clear_mark(val);
+ else
+ ucv_free(val, true);
+ }
+
+ /* free destroyed objects */
+ for (ref = vm->values.next, tmp = ref->next; ref != &vm->values; ref = tmp, tmp = tmp->next) {
+ val = (uc_value_t *)((uintptr_t)ref - offsetof(uc_array_t, ref));
+
+ if (val->type == UC_NULL) {
+ ucv_unref(ref);
+ free(val);
+ }
+ }
+}
+
+
+#ifdef __GNUC__
+
+__attribute__((destructor))
+static void ucv_ressource_types_free(void)
+{
+ size_t i;
+
+ for (i = 0; i < res_types.count; i++)
+ ucv_put(res_types.entries[i].proto);
+
+ uc_vector_clear(&res_types);
+}
+
+#endif