From 35af4ba4fc21a4b2357c50e6b02a2e3e4b236e88 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Wed, 21 Apr 2021 15:07:16 +0200 Subject: 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 --- types.c | 1755 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1755 insertions(+) create mode 100644 types.c (limited to 'types.c') diff --git a/types.c b/types.c new file mode 100644 index 0000000..de2860f --- /dev/null +++ b/types.c @@ -0,0 +1,1755 @@ +/* + * Copyright (C) 2021 Jo-Philipp Wich + * + * 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 +#include +#include +#include +#include +#include + +#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(®exp->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, 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; + + 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) { + return (lh_table_insert_w_hash(object->table, xstrdup(key), val, hash, 0) == 0); + } + + 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 ? ((void *)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 < res_types.count) ? &res_types.entries[type] : 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 - res_types.entries; + 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; + 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) + ucv_object_add(uv, key, ucv_from_json(vm, val)); + + 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++) + ucv_array_push(uv, ucv_from_json(vm, json_object_array_get_idx(jso, i))); + + 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; 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; + + 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) { + if (json) + ucv_to_string_json_encoded(pb, ucv_string_get(str), ucv_string_length(str), false); + else + ucv_stringbuf_addstr(pb, ucv_string_get(str), ucv_string_length(str)); + } + 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, 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; + double d; + + 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: + if (json) + ucv_to_string_json_encoded(pb, ucv_string_get(uv), ucv_string_length(uv), false); + else + ucv_stringbuf_addstr(pb, ucv_string_get(uv), ucv_string_length(uv)); + 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) + ucv_stringbuf_addstr(pb, ucv_string_get(argname), ucv_string_length(argname)); + else + ucv_stringbuf_printf(pb, "[arg%zu]", i); + + ucv_put(argname); + } + + 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 ? " " : "", + cfunction->name ? cfunction->name : "", + json ? "\"" : ""); + + break; + + case UC_FUNCTION: + ucv_stringbuf_printf(pb, "%s%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%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); + 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: + u1 = ucv_string_length(uv1); + u2 = ucv_string_length(uv2); + + if (u1 != u2) + return false; + + return (memcmp(ucv_string_get(uv1), ucv_string_get(uv2), 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) { + 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 = (void *)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 = (void *)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 -- cgit v1.2.3 From c51934a9ab7d416b5ef25c28efd2cf693c4c1f3a Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Mon, 26 Apr 2021 11:39:46 +0200 Subject: types: fix potential leak of key in ucv_object_add() Signed-off-by: Jo-Philipp Wich --- types.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'types.c') diff --git a/types.c b/types.c index de2860f..85023c6 100644 --- a/types.c +++ b/types.c @@ -779,6 +779,7 @@ ucv_object_add(uc_value_t *uv, const char *key, uc_value_t *val) struct lh_entry *existing_entry; uc_value_t *existing_value; unsigned long hash; + void *k; if (ucv_type(uv) != UC_OBJECT) return false; @@ -787,7 +788,15 @@ ucv_object_add(uc_value_t *uv, const char *key, uc_value_t *val) existing_entry = lh_table_lookup_entry_w_hash(object->table, (const void *)key, hash); if (existing_entry == NULL) { - return (lh_table_insert_w_hash(object->table, xstrdup(key), val, hash, 0) == 0); + 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); -- cgit v1.2.3 From 9c5106a6b76527af3b164f81cea8e2dede46ea4d Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Mon, 26 Apr 2021 11:52:00 +0200 Subject: types: fix potential memory leaks and null pointer accesses Signed-off-by: Jo-Philipp Wich --- types.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 21 deletions(-) (limited to 'types.c') diff --git a/types.c b/types.c index 85023c6..1247268 100644 --- a/types.c +++ b/types.c @@ -1152,7 +1152,7 @@ uc_value_t * ucv_from_json(uc_vm *vm, json_object *jso) { //uc_array_t *arr; - uc_value_t *uv; + uc_value_t *uv, *item; int64_t n; size_t i; @@ -1177,8 +1177,19 @@ ucv_from_json(uc_vm *vm, json_object *jso) case json_type_object: uv = ucv_object_new(vm); - json_object_object_foreach(jso, key, val) - ucv_object_add(uv, key, ucv_from_json(vm, val)); + 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; @@ -1193,8 +1204,19 @@ ucv_from_json(uc_vm *vm, json_object *jso) */ uv = ucv_array_new(vm); - for (i = 0; i < json_object_array_length(jso); i++) - ucv_array_push(uv, ucv_from_json(vm, json_object_array_get_idx(jso, i))); + 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; @@ -1284,7 +1306,7 @@ ucv_to_string_json_encoded(uc_stringbuf_t *pb, const char *s, size_t len, bool r if (!regexp) ucv_stringbuf_append(pb, "\""); - for (i = 0; i < len; i++, s++) { + for (i = 0; s != NULL && i < len; i++, s++) { switch (*s) { case '"': ucv_stringbuf_append(pb, "\\\""); @@ -1340,6 +1362,8 @@ 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; @@ -1353,10 +1377,13 @@ ucv_call_tostring(uc_vm *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json) 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, ucv_string_get(str), ucv_string_length(str), false); - else - ucv_stringbuf_addstr(pb, ucv_string_get(str), ucv_string_length(str)); + 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, "\"\""); @@ -1384,8 +1411,9 @@ ucv_to_stringbuf(uc_vm *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json) uc_regexp_t *regexp; uc_value_t *argname; uc_array_t *array; - size_t i; + size_t i, l; double d; + char *s; if (ucv_is_marked(uv)) { ucv_stringbuf_append(pb, "null"); @@ -1438,10 +1466,16 @@ ucv_to_stringbuf(uc_vm *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json) break; case UC_STRING: - if (json) - ucv_to_string_json_encoded(pb, ucv_string_get(uv), ucv_string_length(uv), false); - else - ucv_stringbuf_addstr(pb, ucv_string_get(uv), ucv_string_length(uv)); + 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: @@ -1530,12 +1564,19 @@ ucv_to_stringbuf(uc_vm *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json) if (i == function->nargs && function->vararg) ucv_stringbuf_append(pb, "..."); - if (argname) - ucv_stringbuf_addstr(pb, ucv_string_get(argname), ucv_string_length(argname)); - else - ucv_stringbuf_printf(pb, "[arg%zu]", i); + if (argname) { + s = ucv_string_get(argname); + l = ucv_string_length(argname); + + if (s) + ucv_stringbuf_addstr(pb, s, l); + + ucv_put(argname); - ucv_put(argname); + continue; + } + + ucv_stringbuf_printf(pb, "[arg%zu]", i); } ucv_stringbuf_printf(pb, ")%s { ... }%s", @@ -1619,6 +1660,7 @@ 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; @@ -1661,13 +1703,15 @@ ucv_equal(uc_value_t *uv1, uc_value_t *uv2) 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 (u1 != u2) + if (s1 == NULL || s2 == NULL || u1 != u2) return false; - return (memcmp(ucv_string_get(uv1), ucv_string_get(uv2), u1) == 0); + return (memcmp(s1, s2, u1) == 0); case UC_ARRAY: u1 = ucv_array_length(uv1); -- cgit v1.2.3 From 28825acae8ee95543d4dc4f2c38e9711d4d4a420 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Mon, 26 Apr 2021 13:46:26 +0200 Subject: types: support creating ressource values without associated type Signed-off-by: Jo-Philipp Wich --- types.c | 4 ++-- vm.c | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'types.c') diff --git a/types.c b/types.c index 1247268..4e17025 100644 --- a/types.c +++ b/types.c @@ -963,7 +963,7 @@ ucv_ressource_type_add(const char *name, uc_value_t *proto, void (*freefn)(void static uc_ressource_type_t * ucv_ressource_type_get(size_t type) { - return (type < res_types.count) ? &res_types.entries[type] : NULL; + return (type > 0 && type <= res_types.count) ? &res_types.entries[type - 1] : NULL; } uc_ressource_type_t * @@ -987,7 +987,7 @@ ucv_ressource_new(uc_ressource_type_t *type, void *data) res = xalloc(sizeof(*res)); res->header.type = UC_RESSOURCE; res->header.refcount = 1; - res->type = type - res_types.entries; + res->type = type ? (type - res_types.entries) + 1 : 0; res->data = data; return &res->header; diff --git a/vm.c b/vm.c index 10e3286..88c537c 100644 --- a/vm.c +++ b/vm.c @@ -1807,10 +1807,8 @@ uc_vm_insn_next(uc_vm *vm, enum insn_type insn) abort(); } - if (k == NULL) { + if (k == NULL) k = ucv_ressource_new(NULL, NULL); - ((uc_ressource_t *)k)->type = UINT64_MAX; - } iterk = (uc_ressource_t *)k; -- cgit v1.2.3 From d5dd183f3622002fbc4ae175045a3ebce4eeeb05 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Mon, 26 Apr 2021 14:16:25 +0200 Subject: treewide: address various sign-compare warnings Signed-off-by: Jo-Philipp Wich --- lib.c | 4 ++-- types.c | 4 ++-- value.c | 8 ++++---- vm.c | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'types.c') diff --git a/lib.c b/lib.c index 5733646..80c1386 100644 --- a/lib.c +++ b/lib.c @@ -888,7 +888,7 @@ uc_splice(uc_vm *vm, size_t nargs) if (remlen < 0) remlen = 0; } - else if ((uint64_t)remlen > arrlen - ofs) { + else if ((uint64_t)remlen > arrlen - (uint64_t)ofs) { remlen = arrlen - ofs; } @@ -1025,7 +1025,7 @@ uc_substr(uc_vm *vm, size_t nargs) if (sublen < 0) sublen = 0; } - else if ((uint64_t)sublen > len - ofs) { + else if ((uint64_t)sublen > len - (uint64_t)ofs) { sublen = len - ofs; } diff --git a/types.c b/types.c index 4e17025..e786fb1 100644 --- a/types.c +++ b/types.c @@ -356,7 +356,7 @@ ucv_stringbuf_new(void) } }; - printbuf_memappend_fast(sb, (char *)&ustr, sizeof(ustr)); + printbuf_memappend_fast(sb, (char *)&ustr, (int)sizeof(ustr)); return sb; } @@ -1397,7 +1397,7 @@ ucv_call_tostring(uc_vm *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json) void _ucv_stringbuf_append(uc_stringbuf_t *pb, const char *str, size_t len) { - printbuf_memappend_fast(pb, str, len); + printbuf_memappend_fast(pb, str, (int)len); } void diff --git a/value.c b/value.c index 5fafefa..e53e317 100644 --- a/value.c +++ b/value.c @@ -348,7 +348,7 @@ add_num(uc_value_list *list, int64_t n) list->index[list->isize++] = (TAG_TYPE)(TAG_NUM | TAG_SET_NV(n)); } else { - if (list->dsize + sz > TAG_MASK) { + if ((TAG_TYPE)list->dsize + sz > TAG_MASK) { fprintf(stderr, "Constant data too large\n"); abort(); } @@ -400,7 +400,7 @@ add_dbl(uc_value_list *list, double d) { size_t sz = TAG_ALIGN(sizeof(d)); - if (list->dsize + sz > TAG_MASK) { + if ((TAG_TYPE)list->dsize + sz > TAG_MASK) { fprintf(stderr, "Constant data too large\n"); abort(); } @@ -450,7 +450,7 @@ add_str(uc_value_list *list, const char *s, size_t slen) sz = TAG_ALIGN(sizeof(uint32_t) + slen); - if (list->dsize + sz > TAG_MASK) { + if ((TAG_TYPE)list->dsize + sz > TAG_MASK) { fprintf(stderr, "Constant data too large\n"); abort(); } @@ -528,7 +528,7 @@ add_ptr(uc_value_list *list, void *ptr) { size_t sz = TAG_ALIGN(sizeof(ptr)); - if (list->dsize + sz > TAG_MASK) { + if ((TAG_TYPE)list->dsize + sz > TAG_MASK) { fprintf(stderr, "Constant data too large\n"); abort(); } diff --git a/vm.c b/vm.c index 88c537c..f7d2e09 100644 --- a/vm.c +++ b/vm.c @@ -1836,7 +1836,7 @@ uc_vm_insn_next(uc_vm *vm, enum insn_type insn) n = (uintptr_t)iterk->data; if (n < ucv_array_length(v)) { - iterk->data = (void *)(n + 1); + iterk->data = (void *)(uintptr_t)(n + 1); if (insn == I_NEXTKV) uc_vm_stack_push(vm, ucv_uint64_new(n)); -- cgit v1.2.3 From 64eec7f90e945696572ee076b75d1f35e8f2248a Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Tue, 27 Apr 2021 00:35:20 +0200 Subject: treewide: ISO C / pedantic compliance - Shuffle typedefs to avoid need for non-compliant forward declarations - Fix non-compliant empty struct initializers - Remove use of braced expressions - Remove use of anonymous unions - Avoid `void *` pointer arithmetic - Fix several warnings reported by gcc -pedantic mode and clang 11 compilation Signed-off-by: Jo-Philipp Wich --- CMakeLists.txt | 5 +- chunk.h | 28 +--------- compiler.c | 33 ++++++----- lexer.c | 77 +++++++++++++------------- lexer.h | 6 -- lib.c | 52 +++++++++++------ lib/uci.c | 14 ++--- main.c | 9 ++- source.h | 10 +--- tests/cram/test_basic.t | 5 +- types.c | 11 ++-- types.h | 144 +++++++++++++++++++++++++++++++++++++++++++----- util.h | 2 + value.c | 2 +- value.h | 15 +---- vm.c | 37 +++++++------ vm.h | 49 +--------------- 17 files changed, 278 insertions(+), 221 deletions(-) (limited to 'types.c') diff --git a/CMakeLists.txt b/CMakeLists.txt index c6bfec9..0d5fe54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,8 +107,9 @@ IF(UNIT_TESTING) LIST(APPEND CMAKE_CTEST_ARGUMENTS "--output-on-failure") IF(CMAKE_C_COMPILER_ID STREQUAL "Clang") - ADD_EXECUTABLE(ucode-san ${UCODE_SOURCES}) - TARGET_LINK_LIBRARIES(ucode-san ${json}) + ADD_EXECUTABLE(ucode-san ${UCODE_SOURCES}) + SET_PROPERTY(TARGET ucode-san PROPERTY ENABLE_EXPORTS 1) + TARGET_LINK_LIBRARIES(ucode-san ${json}) TARGET_COMPILE_OPTIONS(ucode-san PRIVATE -g -fno-omit-frame-pointer -fsanitize=undefined,address,leak -fno-sanitize-recover=all) TARGET_LINK_OPTIONS(ucode-san PRIVATE -fsanitize=undefined,address,leak) ENDIF() diff --git a/chunk.h b/chunk.h index cad00f7..2a8fa3a 100644 --- a/chunk.h +++ b/chunk.h @@ -22,38 +22,12 @@ #include "value.h" #include "util.h" - - -typedef struct { - size_t from, to, target, slot; -} uc_ehrange; - -typedef struct { - size_t from, to, slot, nameidx; -} uc_varrange; - -uc_declare_vector(uc_ehranges, uc_ehrange); -uc_declare_vector(uc_variables, uc_varrange); -uc_declare_vector(uc_offsetinfo, uint8_t); - -typedef struct { - size_t count; - uint8_t *entries; - uc_value_list constants; - uc_ehranges ehranges; - struct { - uc_variables variables; - uc_value_list varnames; - uc_offsetinfo offsets; - } debuginfo; -} uc_chunk; +#include "types.h" void uc_chunk_init(uc_chunk *chunk); void uc_chunk_free(uc_chunk *chunk); size_t uc_chunk_add(uc_chunk *chunk, uint8_t byte, size_t line); -typedef struct uc_value_t uc_value_t; - ssize_t uc_chunk_add_constant(uc_chunk *chunk, uc_value_t *value); uc_value_t *uc_chunk_get_constant(uc_chunk *chunk, size_t idx); void uc_chunk_pop(uc_chunk *chunk); diff --git a/compiler.c b/compiler.c index 04ff8ee..fd5b3af 100644 --- a/compiler.c +++ b/compiler.c @@ -468,7 +468,12 @@ uc_compiler_emit_s32(uc_compiler *compiler, size_t srcpos, int32_t n) { uc_chunk *chunk = uc_compiler_current_chunk(compiler); size_t lineoff = uc_compiler_set_srcpos(compiler, srcpos); - uint32_t v = n + 0x7fffffff; + uint32_t v; + + if (n <= 0) + v = n + 0x7fffffff; + else + v = (uint32_t)n + 0x7fffffff; uc_chunk_add(chunk, v / 0x1000000, lineoff); uc_chunk_add(chunk, (v / 0x10000) % 0x100, 0); @@ -555,9 +560,9 @@ uc_compiler_get_jmpaddr(uc_compiler *compiler, size_t off) assert(off + 4 < chunk->count); return ( - chunk->entries[off + 1] * 0x1000000 + - chunk->entries[off + 2] * 0x10000 + - chunk->entries[off + 3] * 0x100 + + chunk->entries[off + 1] * 0x1000000UL + + chunk->entries[off + 2] * 0x10000UL + + chunk->entries[off + 3] * 0x100UL + chunk->entries[off + 4] ) - 0x7fffffff; } @@ -1065,7 +1070,7 @@ static bool uc_compiler_compile_arrowfn(uc_compiler *compiler, uc_value_t *args, bool restarg) { bool array = (ucv_type(args) == UC_ARRAY); - uc_compiler fncompiler = {}; + uc_compiler fncompiler = { 0 }; size_t i, pos, load_off; uc_function_t *fn; ssize_t slot; @@ -1283,7 +1288,7 @@ static void uc_compiler_compile_call(uc_compiler *compiler, bool assignable) { uc_chunk *chunk = uc_compiler_current_chunk(compiler); - uc_jmplist spreads = {}; + uc_jmplist spreads = { 0 }; enum insn_type type; size_t i, nargs = 0; @@ -1422,7 +1427,7 @@ uc_compiler_compile_delimitted_block(uc_compiler *compiler, uc_tokentype_t endty static void uc_compiler_compile_function(uc_compiler *compiler, bool assignable) { - uc_compiler fncompiler = {}; + uc_compiler fncompiler = { 0 }; uc_value_t *name = NULL; ssize_t slot = -1, pos; uc_tokentype_t type; @@ -1891,7 +1896,7 @@ uc_compiler_compile_if(uc_compiler *compiler) uc_chunk *chunk = uc_compiler_current_chunk(compiler); size_t jmpz_off, jmp_off, i; bool expect_endif = false; - uc_jmplist elifs = {}; + uc_jmplist elifs = { 0 }; uc_tokentype_t type; /* parse & compile condition expression */ @@ -1996,7 +2001,7 @@ uc_compiler_compile_while(uc_compiler *compiler) { uc_chunk *chunk = uc_compiler_current_chunk(compiler); size_t cond_off, jmpz_off, end_off; - uc_patchlist p = {}; + uc_patchlist p = { 0 }; p.parent = compiler->patchlist; compiler->patchlist = &p; @@ -2044,7 +2049,7 @@ uc_compiler_compile_for_in(uc_compiler *compiler, bool local, uc_token *kvar, uc { uc_chunk *chunk = uc_compiler_current_chunk(compiler); size_t skip_jmp, test_jmp, key_slot, val_slot; - uc_patchlist p = {}; + uc_patchlist p = { 0 }; p.parent = compiler->patchlist; compiler->patchlist = &p; @@ -2154,7 +2159,7 @@ uc_compiler_compile_for_count(uc_compiler *compiler, bool local, uc_token *var) { uc_chunk *chunk = uc_compiler_current_chunk(compiler); size_t test_off = 0, incr_off, skip_off, cond_off = 0; - uc_patchlist p = {}; + uc_patchlist p = { 0 }; p.parent = compiler->patchlist; compiler->patchlist = &p; @@ -2254,7 +2259,7 @@ uc_compiler_compile_for_count(uc_compiler *compiler, bool local, uc_token *var) static void uc_compiler_compile_for(uc_compiler *compiler) { - uc_token keyvar = {}, valvar = {}; + uc_token keyvar = { 0 }, valvar = { 0 }; bool local; uc_compiler_parse_consume(compiler, TK_LPAREN); @@ -2309,8 +2314,8 @@ uc_compiler_compile_switch(uc_compiler *compiler) size_t i, test_jmp, skip_jmp, next_jmp, value_slot, default_off = 0; uc_chunk *chunk = uc_compiler_current_chunk(compiler); uc_locals *locals = &compiler->locals; - uc_jmplist cases = {}; - uc_patchlist p = {}; + uc_jmplist cases = { 0 }; + uc_patchlist p = { 0 }; p.parent = compiler->patchlist; compiler->patchlist = &p; diff --git a/lexer.c b/lexer.c index 74e0c4d..aaba314 100644 --- a/lexer.c +++ b/lexer.c @@ -38,7 +38,7 @@ struct keyword { union { double d; bool b; - }; + } u; }; struct token { @@ -46,7 +46,7 @@ struct token { union { uint32_t patn; char pat[4]; - }; + } u; unsigned plen; uc_token *(*parse)(uc_lexer *, bool); }; @@ -135,33 +135,33 @@ static const struct token tokens[] = { }; static const struct keyword reserved_words[] = { - { TK_ENDFUNC, "endfunction", 11, {} }, + { TK_ENDFUNC, "endfunction", 11, { 0 } }, { TK_DOUBLE, "Infinity", 8, { .d = INFINITY } }, - { TK_CONTINUE, "continue", 8, {} }, - { TK_ENDWHILE, "endwhile", 8, {} }, - { TK_FUNC, "function", 8, {} }, - { TK_DEFAULT, "default", 7, {} }, - { TK_RETURN, "return", 6, {} }, - { TK_ENDFOR, "endfor", 6, {} }, - { TK_SWITCH, "switch", 6, {} }, - { TK_LOCAL, "local", 5, {} }, - { TK_ENDIF, "endif", 5, {} }, - { TK_WHILE, "while", 5, {} }, - { TK_BREAK, "break", 5, {} }, - { TK_CATCH, "catch", 5, {} }, + { TK_CONTINUE, "continue", 8, { 0 } }, + { TK_ENDWHILE, "endwhile", 8, { 0 } }, + { TK_FUNC, "function", 8, { 0 } }, + { TK_DEFAULT, "default", 7, { 0 } }, + { TK_RETURN, "return", 6, { 0 } }, + { TK_ENDFOR, "endfor", 6, { 0 } }, + { TK_SWITCH, "switch", 6, { 0 } }, + { TK_LOCAL, "local", 5, { 0 } }, + { TK_ENDIF, "endif", 5, { 0 } }, + { TK_WHILE, "while", 5, { 0 } }, + { TK_BREAK, "break", 5, { 0 } }, + { TK_CATCH, "catch", 5, { 0 } }, { TK_BOOL, "false", 5, { .b = false } }, { TK_BOOL, "true", 4, { .b = true } }, - { TK_ELIF, "elif", 4, {} }, - { TK_ELSE, "else", 4, {} }, - { TK_THIS, "this", 4, {} }, - { TK_NULL, "null", 4, {} }, - { TK_CASE, "case", 4, {} }, + { TK_ELIF, "elif", 4, { 0 } }, + { TK_ELSE, "else", 4, { 0 } }, + { TK_THIS, "this", 4, { 0 } }, + { TK_NULL, "null", 4, { 0 } }, + { TK_CASE, "case", 4, { 0 } }, { TK_DOUBLE, "NaN", 3, { .d = NAN } }, - { TK_TRY, "try", 3, {} }, - { TK_FOR, "for", 3, {} }, - { TK_LOCAL, "let", 3, {} }, - { TK_IF, "if", 2, {} }, - { TK_IN, "in", 2, {} }, + { TK_TRY, "try", 3, { 0 } }, + { TK_FOR, "for", 3, { 0 } }, + { TK_LOCAL, "let", 3, { 0 } }, + { TK_IF, "if", 2, { 0 } }, + { TK_IN, "in", 2, { 0 } }, }; @@ -362,7 +362,7 @@ parse_comment(uc_lexer *lex, bool no_regexp) if (!buf_remaining(lex)) return emit_op(lex, lex->lastoff, TK_ERROR, ucv_string_new("Unterminated comment")); - if (!strcmp(tok->pat, "//")) { + if (!strcmp(tok->u.pat, "//")) { end = "\n"; elen = 1; } @@ -400,7 +400,7 @@ static uc_token * parse_string(uc_lexer *lex, bool no_regexp) { const struct token *tok = lex->tok; - char q = tok->pat[0]; + char q = tok->u.pat[0]; char *ptr, *c; uc_token *rv; int code; @@ -438,7 +438,7 @@ parse_string(uc_lexer *lex, bool no_regexp) default: lex->is_escape = false; - c = strchr("a\ab\be\ef\fn\nr\rt\tv\v", *ptr); + c = strchr("a\ab\be\033f\fn\nr\rt\tv\v", *ptr); if (c && *c >= 'a') { lookbehind_append(lex, c + 1, 1); @@ -725,7 +725,7 @@ parse_label(uc_lexer *lex, bool no_regexp) size_t i; if (!lex->lookbehind && tok->plen) - lookbehind_append(lex, tok->pat, tok->plen); + lookbehind_append(lex, tok->u.pat, tok->plen); if (!buf_remaining(lex) || (lex->bufstart[0] != '_' && !isalnum(lex->bufstart[0]))) { for (i = 0, word = &reserved_words[0]; i < ARRAY_SIZE(reserved_words); i++, word = &reserved_words[i]) { @@ -734,11 +734,11 @@ parse_label(uc_lexer *lex, bool no_regexp) switch (word->type) { case TK_DOUBLE: - rv = emit_op(lex, lex->source->off - word->plen, word->type, ucv_double_new(word->d)); + rv = emit_op(lex, lex->source->off - word->plen, word->type, ucv_double_new(word->u.d)); break; case TK_BOOL: - rv = emit_op(lex, lex->source->off - word->plen, word->type, ucv_boolean_new(word->b)); + rv = emit_op(lex, lex->source->off - word->plen, word->type, ucv_boolean_new(word->u.b)); break; default: @@ -803,7 +803,7 @@ parse_number(uc_lexer *lex, bool no_regexp) if (*e == '.' || *e == 'e' || *e == 'E') { d = strtod(lex->lookbehind, &e); - if (tok->pat[0] == '-') + if (tok->u.pat[0] == '-') d = -d; if (e > lex->lookbehind && *e == 0) @@ -812,7 +812,7 @@ parse_number(uc_lexer *lex, bool no_regexp) rv = emit_op(lex, lex->source->off - (lex->lookbehindlen - (e - lex->lookbehind) - 1), TK_ERROR, ucv_string_new("Invalid number literal")); } else if (*e == 0) { - if (tok->pat[0] == '-') + if (tok->u.pat[0] == '-') n = (errno == ERANGE) ? INT64_MIN : -n; rv = emit_op(lex, lex->source->off - (e - lex->lookbehind), TK_NUMBER, ucv_int64_new(n)); @@ -857,7 +857,8 @@ lex_step(uc_lexer *lex, FILE *fp, bool no_regexp) rem = lex->bufend - lex->bufstart; - memcpy(lex->buf, lex->bufstart, rem); + if (rem) + memcpy(lex->buf, lex->bufstart, rem); rlen = fread(lex->buf + rem, 1, lex->buflen - rem, fp); @@ -1042,8 +1043,8 @@ lex_step(uc_lexer *lex, FILE *fp, bool no_regexp) c = buf_remaining(lex) ? lex->bufstart[0] : 0; - if (tok->plen ? ((search.n & masks[tok->plen]) == tok->patn) - : (c >= tok->pat[0] && c <= tok->pat[1])) { + if (tok->plen ? ((search.n & masks[tok->plen]) == tok->u.patn) + : (c >= tok->u.pat[0] && c <= tok->u.pat[1])) { lex->lastoff = lex->source->off; /* token has a parse method, switch state */ @@ -1067,7 +1068,7 @@ lex_step(uc_lexer *lex, FILE *fp, bool no_regexp) else if ((lex->block == STATEMENTS && tok->type == TK_RSTM) || (lex->block == EXPRESSION && tok->type == TK_REXP)) { /* strip whitespace after block */ - if (tok->pat[0] == '-') + if (tok->u.pat[0] == '-') lex->modifier = MINUS; /* strip newline after statement block */ @@ -1207,7 +1208,7 @@ uc_get_tokenname(unsigned type) if (tokens[i].type != type) continue; - snprintf(buf, sizeof(buf), "'%s'", tokens[i].pat); + snprintf(buf, sizeof(buf), "'%s'", tokens[i].u.pat); return buf; } diff --git a/lexer.h b/lexer.h index f023fb6..069e9e0 100644 --- a/lexer.h +++ b/lexer.h @@ -126,12 +126,6 @@ typedef struct { size_t pos; } uc_token; -typedef struct { - bool lstrip_blocks; - bool trim_blocks; - bool strict_declarations; -} uc_parse_config; - typedef struct { uc_lex_state_t state; uc_parse_config *config; diff --git a/lib.c b/lib.c index 80c1386..5d6235d 100644 --- a/lib.c +++ b/lib.c @@ -35,6 +35,7 @@ #include "compiler.h" #include "vm.h" #include "lib.h" +#include "source.h" static void format_context_line(uc_stringbuf_t *buf, const char *line, size_t off, bool compact) @@ -454,9 +455,11 @@ static uc_value_t * uc_delete(uc_vm *vm, size_t nargs) { uc_value_t *obj = uc_get_arg(0); + uc_value_t *key = NULL; uc_value_t *rv = NULL; - const char *key; + bool freeable; size_t i; + char *k; if (ucv_type(obj) != UC_OBJECT) return NULL; @@ -464,10 +467,14 @@ uc_delete(uc_vm *vm, size_t nargs) for (i = 1; i < nargs; i++) { ucv_put(rv); - key = ucv_string_get(uc_get_arg(i)); - rv = ucv_get(ucv_object_get(obj, key ? key : "null", NULL)); + key = uc_get_arg(i); + k = uc_cast_string(vm, &key, &freeable); + rv = ucv_get(ucv_object_get(obj, k, NULL)); + + ucv_object_delete(obj, k); - ucv_object_delete(obj, key ? key : "null"); + if (freeable) + free(k); } return rv; @@ -482,7 +489,7 @@ uc_die(uc_vm *vm, size_t nargs) s = msg ? uc_cast_string(vm, &msg, &freeable) : "Died"; - uc_vm_raise_exception(vm, EXCEPTION_USER, s); + uc_vm_raise_exception(vm, EXCEPTION_USER, "%s", s); if (freeable) free(s); @@ -494,13 +501,19 @@ static uc_value_t * uc_exists(uc_vm *vm, size_t nargs) { uc_value_t *obj = uc_get_arg(0); - const char *key = ucv_string_get(uc_get_arg(1)); - bool found; + uc_value_t *key = uc_get_arg(1); + bool found, freeable; + char *k; if (ucv_type(obj) != UC_OBJECT) return false; - ucv_object_get(obj, key ? key : "null", &found); + k = uc_cast_string(vm, &key, &freeable); + + ucv_object_get(obj, k, &found); + + if (freeable) + free(k); return ucv_boolean_new(found); } @@ -516,8 +529,9 @@ uc_exit(uc_vm *vm, size_t nargs) static uc_value_t * uc_getenv(uc_vm *vm, size_t nargs) { - const char *key = ucv_string_get(uc_get_arg(0)); - char *val = key ? getenv(key) : NULL; + uc_value_t *key = uc_get_arg(0); + char *k = ucv_string_get(key); + char *val = k ? getenv(k) : NULL; return val ? ucv_string_new(val) : NULL; } @@ -562,16 +576,18 @@ uc_filter(uc_vm *vm, size_t nargs) static uc_value_t * uc_hex(uc_vm *vm, size_t nargs) { - const char *val = ucv_string_get(uc_get_arg(0)); + uc_value_t *val = uc_get_arg(0); + char *e, *v; int64_t n; - char *e; - if (!val || !isxdigit(*val)) + v = ucv_string_get(val); + + if (!v || !isxdigit(*v)) return ucv_double_new(NAN); - n = strtoll(val, &e, 16); + n = strtoll(v, &e, 16); - if (e == val || *e) + if (e == v || *e) return ucv_double_new(NAN); return ucv_int64_new(n); @@ -622,8 +638,10 @@ uc_keys(uc_vm *vm, size_t nargs) arr = ucv_array_new(vm); - ucv_object_foreach(obj, key, val) + ucv_object_foreach(obj, key, val) { + (void)val; ucv_array_push(arr, ucv_string_new(key)); + } return arr; } @@ -1423,7 +1441,7 @@ uc_require_so(uc_vm *vm, const char *path, uc_value_t **res) return true; } - init = dlsym(dlh, "uc_module_entry"); + *(void **)(&init) = dlsym(dlh, "uc_module_entry"); if (!init) { uc_vm_raise_exception(vm, EXCEPTION_RUNTIME, diff --git a/lib/uci.c b/lib/uci.c index fba7dad..00a451c 100644 --- a/lib/uci.c +++ b/lib/uci.c @@ -255,7 +255,7 @@ uc_uci_get_any(uc_vm *vm, size_t nargs, bool all) uc_value_t *conf = uc_get_arg(0); uc_value_t *sect = uc_get_arg(1); uc_value_t *opt = uc_get_arg(2); - struct uci_ptr ptr = {}; + struct uci_ptr ptr = { 0 }; int rv; if (!c || !*c) @@ -330,7 +330,7 @@ uc_uci_get_first(uc_vm *vm, size_t nargs) struct uci_package *p = NULL; struct uci_section *sc; struct uci_element *e; - struct uci_ptr ptr = {}; + struct uci_ptr ptr = { 0 }; int rv; if (ucv_type(conf) != UC_STRING || @@ -465,7 +465,7 @@ uc_uci_set(uc_vm *vm, size_t nargs) uc_value_t *conf = uc_get_arg(0); uc_value_t *sect = uc_get_arg(1); uc_value_t *opt = NULL, *val = NULL; - struct uci_ptr ptr = {}; + struct uci_ptr ptr = { 0 }; bool is_list = false; size_t i; int rv; @@ -570,7 +570,7 @@ uc_uci_delete(uc_vm *vm, size_t nargs) uc_value_t *conf = uc_get_arg(0); uc_value_t *sect = uc_get_arg(1); uc_value_t *opt = uc_get_arg(2); - struct uci_ptr ptr = {}; + struct uci_ptr ptr = { 0 }; int rv; if (ucv_type(conf) != UC_STRING || @@ -605,7 +605,7 @@ uc_uci_rename(uc_vm *vm, size_t nargs) uc_value_t *conf = uc_get_arg(0); uc_value_t *sect = uc_get_arg(1); uc_value_t *opt = NULL, *val = NULL; - struct uci_ptr ptr = {}; + struct uci_ptr ptr = { 0 }; int rv; if (ucv_type(conf) != UC_STRING || @@ -665,7 +665,7 @@ uc_uci_reorder(uc_vm *vm, size_t nargs) uc_value_t *conf = uc_get_arg(0); uc_value_t *sect = uc_get_arg(1); uc_value_t *val = uc_get_arg(2); - struct uci_ptr ptr = {}; + struct uci_ptr ptr = { 0 }; int64_t n; int rv; @@ -705,7 +705,7 @@ uc_uci_pkg_command(uc_vm *vm, size_t nargs, enum pkg_cmd cmd) uc_value_t *conf = uc_get_arg(0); struct uci_element *e, *tmp; struct uci_package *p; - struct uci_ptr ptr = {}; + struct uci_ptr ptr = { 0 }; int rv, res = UCI_OK; if (cmd != CMD_REVERT && conf) diff --git a/main.c b/main.c index 618d5e4..d291447 100644 --- a/main.c +++ b/main.c @@ -98,7 +98,7 @@ parse(uc_parse_config *config, uc_source *src, { uc_value_t *globals = NULL; uc_function_t *entry; - uc_vm vm = {}; + uc_vm vm = { 0 }; int c, c2, rc = 0; char *err; @@ -285,7 +285,12 @@ main(int argc, char **argv) if (source) fprintf(stderr, "Options -i and -s are exclusive\n"); - source = uc_source_new_buffer("[-s argument]", xstrdup(optarg), strlen(optarg)); + c = xstrdup(optarg); + source = uc_source_new_buffer("[-s argument]", c, strlen(c)); + + if (!source) + free(c); + break; case 'S': diff --git a/source.h b/source.h index 4ca01b3..6557a48 100644 --- a/source.h +++ b/source.h @@ -22,17 +22,9 @@ #include #include "util.h" +#include "types.h" -uc_declare_vector(uc_lineinfo, uint8_t); - -typedef struct { - char *filename, *buffer; - FILE *fp; - size_t usecount, off; - uc_lineinfo lineinfo; -} uc_source; - uc_source *uc_source_new_file(const char *path); uc_source *uc_source_new_buffer(const char *name, char *buf, size_t len); diff --git a/tests/cram/test_basic.t b/tests/cram/test_basic.t index 982d58a..3d4cd9e 100644 --- a/tests/cram/test_basic.t +++ b/tests/cram/test_basic.t @@ -35,7 +35,7 @@ check that ucode provides proper error messages: One of -i or -s is required [1] - $ ucode -m foo -s '' + $ ucode -m foo -s ' ' Runtime error: No module named 'foo' could be found At start of program @@ -53,6 +53,7 @@ check that ucode can load fs module: One of -i or -s is required [1] - $ ucode -m fs -s '' + $ ucode -m fs -s ' ' + (no-eol) $ touch moo; ucode -m fs -i moo diff --git a/types.c b/types.c index e786fb1..3ed8aaa 100644 --- a/types.c +++ b/types.c @@ -927,7 +927,7 @@ ucv_closure_new(uc_vm *vm, uc_function_t *function, bool arrow_fn) closure->header.refcount = 1; closure->function = function; closure->is_arrow = arrow_fn; - closure->upvals = function->nupvals ? ((void *)closure + ALIGN(sizeof(*closure))) : NULL; + closure->upvals = function->nupvals ? (uc_upvalref_t **)((uintptr_t)closure + ALIGN(sizeof(*closure))) : NULL; if (vm) ucv_ref(&vm->values, &closure->ref); @@ -1590,8 +1590,8 @@ ucv_to_stringbuf(uc_vm *vm, uc_stringbuf_t *pb, uc_value_t *uv, bool json) ucv_stringbuf_printf(pb, "%sfunction%s%s(...) { [native code] }%s", json ? "\"" : "", - cfunction->name ? " " : "", - cfunction->name ? cfunction->name : "", + cfunction->name[0] ? " " : "", + cfunction->name[0] ? cfunction->name : "", json ? "\"" : ""); break; @@ -1739,6 +1739,7 @@ ucv_equal(uc_value_t *uv1, uc_value_t *uv2) } ucv_object_foreach(uv2, key2, val2) { + (void)val2; ucv_object_get(uv1, key2, &b1); if (!b1) @@ -1772,7 +1773,7 @@ ucv_gc(uc_vm *vm, bool final) /* unref unreachable objects */ for (ref = vm->values.next; ref != &vm->values; ref = ref->next) { - val = (void *)ref - offsetof(uc_array_t, ref); + val = (uc_value_t *)((uintptr_t)ref - offsetof(uc_array_t, ref)); if (ucv_is_marked(val)) ucv_clear_mark(val); @@ -1782,7 +1783,7 @@ ucv_gc(uc_vm *vm, bool final) /* free destroyed objects */ for (ref = vm->values.next, tmp = ref->next; ref != &vm->values; ref = tmp, tmp = tmp->next) { - val = (void *)ref - offsetof(uc_array_t, ref); + val = (uc_value_t *)((uintptr_t)ref - offsetof(uc_array_t, ref)); if (val->type == UC_NULL) { ucv_unref(ref); diff --git a/types.h b/types.h index a8962da..9dab246 100644 --- a/types.h +++ b/types.h @@ -22,8 +22,10 @@ #include #include -#include "source.h" -#include "chunk.h" +#include "util.h" + + +/* Value types and generic value header */ typedef enum uc_type_t { UC_NULL, @@ -48,6 +50,58 @@ typedef struct uc_value_t { uint32_t refcount:26; } uc_value_t; + +/* Constant list defintions */ + +typedef struct { + size_t isize; + size_t dsize; + uint64_t *index; + char *data; +} uc_value_list; + + +/* Source buffer defintions */ + +uc_declare_vector(uc_lineinfo, uint8_t); + +typedef struct { + char *filename, *buffer; + FILE *fp; + size_t usecount, off; + uc_lineinfo lineinfo; +} uc_source; + + +/* Bytecode chunk defintions */ + +typedef struct { + size_t from, to, target, slot; +} uc_ehrange; + +typedef struct { + size_t from, to, slot, nameidx; +} uc_varrange; + +uc_declare_vector(uc_ehranges, uc_ehrange); +uc_declare_vector(uc_variables, uc_varrange); +uc_declare_vector(uc_offsetinfo, uint8_t); + +typedef struct { + size_t count; + uint8_t *entries; + uc_value_list constants; + uc_ehranges ehranges; + struct { + uc_variables variables; + uc_value_list varnames; + uc_offsetinfo offsets; + } debuginfo; +} uc_chunk; + + +/* Value type structures */ + typedef struct uc_weakref_t { struct uc_weakref_t *prev; struct uc_weakref_t *next; @@ -144,6 +198,69 @@ typedef struct { uc_declare_vector(uc_ressource_types_t, uc_ressource_type_t); + +/* Parser definitions */ + +typedef struct { + bool lstrip_blocks; + bool trim_blocks; + bool strict_declarations; +} uc_parse_config; + + +/* VM definitions */ + +typedef enum { + EXCEPTION_NONE, + EXCEPTION_SYNTAX, + EXCEPTION_RUNTIME, + EXCEPTION_TYPE, + EXCEPTION_REFERENCE, + EXCEPTION_USER +} uc_exception_type_t; + +typedef struct { + uc_exception_type_t type; + uc_value_t *stacktrace; + char *message; +} uc_exception; + +typedef struct { + uint8_t *ip; + uc_closure_t *closure; + uc_cfunction_t *cfunction; + size_t stackframe; + uc_value_t *ctx; + bool mcall; +} uc_callframe; + +uc_declare_vector(uc_callframes, uc_callframe); +uc_declare_vector(uc_stack, uc_value_t *); + +struct uc_vm { + uc_stack stack; + uc_exception exception; + uc_callframes callframes; + uc_upvalref_t *open_upvals; + uc_parse_config *config; + uc_value_t *globals; + uc_source *sources; + uc_weakref_t values; + union { + uint32_t u32; + int32_t s32; + uint16_t u16; + int16_t s16; + uint8_t u8; + int8_t s8; + } arg; + size_t spread_values; + uint8_t trace; +}; + + +/* Value API */ + typedef struct printbuf uc_stringbuf_t; void ucv_free(uc_value_t *, bool); @@ -162,7 +279,7 @@ uc_value_t *ucv_string_new_length(const char *, size_t); size_t ucv_string_length(uc_value_t *); char *_ucv_string_get(uc_value_t **); -#define ucv_string_get(uv) ({ uc_value_t * volatile p = (uv); _ucv_string_get((uc_value_t **)&p); }) +#define ucv_string_get(uv) _ucv_string_get((uc_value_t **)&uv) uc_stringbuf_t *ucv_stringbuf_new(void); uc_value_t *ucv_stringbuf_finish(uc_stringbuf_t *); @@ -200,16 +317,17 @@ bool ucv_object_add(uc_value_t *, const char *, uc_value_t *); bool ucv_object_delete(uc_value_t *, const char *); size_t ucv_object_length(uc_value_t *); -#define ucv_object_foreach(obj, key, val) \ - char *key; \ - uc_value_t *val __attribute__((__unused__)); \ - for (struct lh_entry *entry ## key = (ucv_type(obj) == UC_OBJECT) ? ((uc_object_t *)obj)->table->head : NULL, *entry_next ## key = NULL; \ - ({ if (entry ## key) { \ - key = (char *)entry ## key->k; \ - val = (uc_value_t *)entry ## key->v; \ - entry_next ## key = entry ## key->next; \ - } ; entry ## key; }); \ - entry ## key = entry_next ## key) +#define ucv_object_foreach(obj, key, val) \ + char *key = NULL; \ + uc_value_t *val = NULL; \ + struct lh_entry *entry##key; \ + struct lh_entry *entry_next##key = NULL; \ + for (entry##key = (ucv_type(obj) == UC_OBJECT) ? ((uc_object_t *)obj)->table->head : NULL; \ + (entry##key ? (key = (char *)lh_entry_k(entry##key), \ + val = (uc_value_t *)lh_entry_v(entry##key), \ + entry_next##key = entry##key->next, entry##key) \ + : 0); \ + entry##key = entry_next##key) uc_value_t *ucv_function_new(const char *, size_t, uc_source *); size_t ucv_function_srcpos(uc_value_t *, size_t); diff --git a/util.h b/util.h index a62ccc4..c4ea087 100644 --- a/util.h +++ b/util.h @@ -218,6 +218,7 @@ static inline struct json_tokener *xjs_new_tokener(void) { return tok; } +__attribute__((format(printf, 2, 0))) static inline int xasprintf(char **strp, const char *fmt, ...) { va_list ap; int len; @@ -234,6 +235,7 @@ static inline int xasprintf(char **strp, const char *fmt, ...) { return len; } +__attribute__((format(printf, 2, 0))) static inline int xvasprintf(char **strp, const char *fmt, va_list ap) { int len = vasprintf(strp, fmt, ap); diff --git a/value.c b/value.c index e53e317..0827038 100644 --- a/value.c +++ b/value.c @@ -260,7 +260,7 @@ uc_cmp(int how, uc_value_t *v1, uc_value_t *v2) } else { if (t1 == t2 && !ucv_is_scalar(v1)) { - delta = (void *)v1 - (void *)v2; + delta = (intptr_t)v1 - (intptr_t)v2; } else { t1 = uc_cast_number(v1, &n1, &d1); diff --git a/value.h b/value.h index 770279e..a3abfc6 100644 --- a/value.h +++ b/value.h @@ -29,6 +29,8 @@ #include +#include "types.h" + typedef enum { TAG_INVAL = 0, TAG_NUM = 1, @@ -39,24 +41,11 @@ typedef enum { TAG_PTR = 6 } uc_value_type_t; -typedef struct { - size_t isize; - size_t dsize; - uint64_t *index; - char *data; -} uc_value_list; - -typedef struct uc_value_t uc_value_t; - bool uc_cmp(int how, uc_value_t *v1, uc_value_t *v2); bool uc_val_is_truish(uc_value_t *val); -typedef enum uc_type_t uc_type_t; -typedef struct uc_value_t uc_value_t; uc_type_t uc_cast_number(uc_value_t *v, int64_t *n, double *d); -typedef struct uc_vm uc_vm; - uc_value_t *uc_getval(uc_vm *, uc_value_t *scope, uc_value_t *key); uc_value_t *uc_setval(uc_vm *, uc_value_t *scope, uc_value_t *key, uc_value_t *val); diff --git a/vm.c b/vm.c index 7d60805..ee73265 100644 --- a/vm.c +++ b/vm.c @@ -232,9 +232,9 @@ uc_vm_decode_insn(uc_vm *vm, uc_callframe *frame, uc_chunk *chunk) case -4: vm->arg.s32 = ( - frame->ip[0] * 0x1000000 + - frame->ip[1] * 0x10000 + - frame->ip[2] * 0x100 + + frame->ip[0] * 0x1000000UL + + frame->ip[1] * 0x10000UL + + frame->ip[2] * 0x100UL + frame->ip[3] ) - 0x7fffffff; frame->ip += 4; @@ -247,9 +247,9 @@ uc_vm_decode_insn(uc_vm *vm, uc_callframe *frame, uc_chunk *chunk) case 4: vm->arg.u32 = ( - frame->ip[0] * 0x1000000 + - frame->ip[1] * 0x10000 + - frame->ip[2] * 0x100 + + frame->ip[0] * 0x1000000UL + + frame->ip[1] * 0x10000UL + + frame->ip[2] * 0x100UL + frame->ip[3] ); frame->ip += 4; @@ -307,7 +307,7 @@ uc_vm_frame_dump(uc_vm *vm, uc_callframe *frame) ref = closure->upvals[i]; v = uc_chunk_debug_get_variable(chunk, 0, i, true); s = ucv_to_string(NULL, v); - fprintf(stderr, " [%zu] <%p> %s ", i, ref, s); + fprintf(stderr, " [%zu] <%p> %s ", i, (void *)ref, s); free(s); if (ref->closed) { @@ -588,13 +588,13 @@ uc_dump_insn(uc_vm *vm, uint8_t *pos, enum insn_type insn) case -2: fprintf(stderr, " {%c0x%hx}", vm->arg.s16 < 0 ? '-' : '+', - vm->arg.s16 < 0 ? -(unsigned)vm->arg.s16 : (unsigned)vm->arg.s16); + (uint16_t)(vm->arg.s16 < 0 ? -vm->arg.s16 : vm->arg.s16)); break; case -4: fprintf(stderr, " {%c0x%x}", vm->arg.s32 < 0 ? '-' : '+', - vm->arg.s32 < 0 ? -(unsigned)vm->arg.s32 : (unsigned)vm->arg.s32); + (uint32_t)(vm->arg.s32 < 0 ? -vm->arg.s32 : vm->arg.s32)); break; case 1: @@ -1035,7 +1035,7 @@ uc_vm_capture_upval(uc_vm *vm, size_t slot) if (curr && curr->slot == slot) { if (vm->trace) { s = ucv_to_string(NULL, vm->stack.entries[slot]); - fprintf(stderr, " {+%zu} <%p> %s\n", slot, curr, s); + fprintf(stderr, " {+%zu} <%p> %s\n", slot, (void *)curr, s); free(s); } @@ -1047,7 +1047,7 @@ uc_vm_capture_upval(uc_vm *vm, size_t slot) if (vm->trace) { s = ucv_to_string(NULL, vm->stack.entries[slot]); - fprintf(stderr, " {*%zu} <%p> %s\n", slot, created, s); + fprintf(stderr, " {*%zu} <%p> %s\n", slot, (void *)created, s); free(s); } @@ -1072,7 +1072,7 @@ uc_vm_close_upvals(uc_vm *vm, size_t slot) if (vm->trace) { s = ucv_to_string(NULL, ref->value); - fprintf(stderr, " {!%zu} <%p> %s\n", ref->slot, ref, s); + fprintf(stderr, " {!%zu} <%p> %s\n", ref->slot, (void *)ref, s); free(s); } @@ -1528,11 +1528,13 @@ static void uc_vm_insn_sobj(uc_vm *vm, enum insn_type insn) { uc_value_t *obj = uc_vm_stack_peek(vm, vm->arg.u32); + uc_value_t *val; size_t idx; for (idx = 0; idx < vm->arg.u32; idx += 2) { + val = uc_vm_stack_peek(vm, vm->arg.u32 - idx - 1); ucv_object_add(obj, - ucv_string_get(uc_vm_stack_peek(vm, vm->arg.u32 - idx - 1)), + ucv_string_get(val), ucv_get(uc_vm_stack_peek(vm, vm->arg.u32 - idx - 2))); } @@ -1972,6 +1974,8 @@ uc_vm_callframe_pop(uc_vm *vm) static void uc_vm_output_exception(uc_vm *vm) { + uc_value_t *ctx; + if (vm->exception.type == EXCEPTION_USER) fprintf(stderr, "%s\n", vm->exception.message); else @@ -1979,10 +1983,9 @@ uc_vm_output_exception(uc_vm *vm) exception_type_strings[vm->exception.type] ? exception_type_strings[vm->exception.type] : "Error", vm->exception.message); - fprintf(stderr, "%s\n\n", - ucv_string_get( - ucv_object_get( - ucv_array_get(vm->exception.stacktrace, 0), "context", NULL))); + ctx = ucv_object_get(ucv_array_get(vm->exception.stacktrace, 0), "context", NULL); + + fprintf(stderr, "%s\n\n", ucv_string_get(ctx)); } static uc_vm_status_t diff --git a/vm.h b/vm.h index e4de4f9..0f7b805 100644 --- a/vm.h +++ b/vm.h @@ -23,6 +23,7 @@ #include "chunk.h" #include "util.h" #include "lexer.h" +#include "types.h" #define __insns \ __insn(NOOP) \ @@ -103,54 +104,6 @@ typedef struct { int8_t operand_bytes; } uc_insn_definition; -typedef enum { - EXCEPTION_NONE, - EXCEPTION_SYNTAX, - EXCEPTION_RUNTIME, - EXCEPTION_TYPE, - EXCEPTION_REFERENCE, - EXCEPTION_USER -} uc_exception_type_t; - -typedef struct { - uc_exception_type_t type; - uc_value_t *stacktrace; - char *message; -} uc_exception; - -typedef struct { - uint8_t *ip; - uc_closure_t *closure; - uc_cfunction_t *cfunction; - size_t stackframe; - uc_value_t *ctx; - bool mcall; -} uc_callframe; - -uc_declare_vector(uc_callframes, uc_callframe); -uc_declare_vector(uc_stack, uc_value_t *); - -typedef struct uc_vm { - uc_stack stack; - uc_exception exception; - uc_callframes callframes; - uc_upvalref_t *open_upvals; - uc_parse_config *config; - uc_value_t *globals; - uc_source *sources; - uc_weakref_t values; - union { - uint32_t u32; - int32_t s32; - uint16_t u16; - int16_t s16; - uint8_t u8; - int8_t s8; - } arg; - size_t spread_values; - uint8_t trace; -} uc_vm; - typedef enum { STATUS_OK, ERROR_COMPILE, -- cgit v1.2.3