#include "gravity_hash.h"
#include "gravity_core.h"
#include "gravity_value.h"
#include "gravity_utils.h"
#include "gravity_memory.h"
#include "gravity_macros.h"
#include "gravity_opcodes.h"
#include "gravity_vmmacros.h"
static void gravity_function_special_serialize (gravity_function_t *f, const char *key, json_t *json);
static gravity_map_t *gravity_map_deserialize (gravity_vm *vm, json_value *json);
static void gravity_hash_serialize (gravity_hash_t *table, gravity_value_t key, gravity_value_t value, void *data) {
#pragma unused(table)
json_t *json = (json_t *)data;
if (VALUE_ISA_FUNCTION(value)) {
gravity_function_t *f = VALUE_AS_FUNCTION(value);
if (f->tag == EXEC_TYPE_SPECIAL) gravity_function_special_serialize(f, VALUE_AS_CSTRING(key), json);
else {
gravity_string_t *s = VALUE_AS_STRING(key);
bool is_super_function = ((s->len > 5) && (string_casencmp(s->s, CLASS_INTERNAL_INIT_NAME, 5) == 0));
const char *saved = f->identifier;
if (is_super_function) f->identifier = s->s;
gravity_function_serialize(f, json);
if (is_super_function) f->identifier = saved;
}
}
else if (VALUE_ISA_CLASS(value)) {
gravity_class_serialize(VALUE_AS_CLASS(value), json);
}
else
assert(0);
}
void gravity_hash_keyvaluefree (gravity_hash_t *table, gravity_value_t key, gravity_value_t value, void *data) {
#pragma unused(table)
gravity_vm *vm = (gravity_vm *)data;
gravity_value_free(vm, key);
gravity_value_free(vm, value);
}
void gravity_hash_keyfree (gravity_hash_t *table, gravity_value_t key, gravity_value_t value, void *data) {
#pragma unused(table, value)
gravity_vm *vm = (gravity_vm *)data;
gravity_value_free(vm, key);
}
void gravity_hash_valuefree (gravity_hash_t *table, gravity_value_t key, gravity_value_t value, void *data) {
#pragma unused(table, key)
gravity_vm *vm = (gravity_vm *)data;
gravity_value_free(vm, value);
}
static void gravity_hash_internalsize (gravity_hash_t *table, gravity_value_t key, gravity_value_t value, void *data1, void *data2) {
#pragma unused(table)
uint32_t *size = (uint32_t *)data1;
gravity_vm *vm = (gravity_vm *)data2;
*size = gravity_value_size(vm, key);
*size += gravity_value_size(vm, value);
}
static void gravity_hash_gray (gravity_hash_t *table, gravity_value_t key, gravity_value_t value, void *data1) {
#pragma unused(table)
gravity_vm *vm = (gravity_vm *)data1;
gravity_gray_value(vm, key);
gravity_gray_value(vm, value);
}
gravity_module_t *gravity_module_new (gravity_vm *vm, const char *identifier) {
gravity_module_t *m = (gravity_module_t *)mem_alloc(sizeof(gravity_module_t));
assert(m);
m->isa = gravity_class_module;
m->identifier = string_dup(identifier);
m->htable = gravity_hash_create(0, gravity_value_hash, gravity_value_equals, gravity_hash_keyvaluefree, (void*)vm);
gravity_vm_transfer(vm, (gravity_object_t*)m);
return m;
}
void gravity_module_free (gravity_vm *vm, gravity_module_t *m) {
#pragma unused(vm)
if (m->identifier) mem_free(m->identifier);
gravity_hash_free(m->htable);
mem_free(m);
}
uint32_t gravity_module_size (gravity_vm *vm, gravity_module_t *m) {
uint32_t hash_size = 0;
gravity_hash_iterate2(m->htable, gravity_hash_internalsize, (void*)&hash_size, (void*)vm);
return (sizeof(gravity_module_t)) + string_size(m->identifier) + hash_size + gravity_hash_memsize(m->htable);
}
void gravity_module_blacken (gravity_vm *vm, gravity_module_t *m) {
gravity_vm_memupdate(vm, gravity_module_size(vm, m));
gravity_hash_iterate(m->htable, gravity_hash_gray, (void*)vm);
}
void gravity_class_bind (gravity_class_t *c, const char *key, gravity_value_t value) {
if (VALUE_ISA_CLASS(value)) {
gravity_class_t *obj = VALUE_AS_CLASS(value);
obj->has_outer = true;
}
gravity_hash_insert(c->htable, VALUE_FROM_CSTRING(NULL, key), value);
}
gravity_class_t *gravity_class_getsuper (gravity_class_t *c) {
return c->superclass;
}
bool gravity_class_grow (gravity_class_t *c, uint32_t n) {
if (c->ivars) mem_free(c->ivars);
if (c->nivars + n >= MAX_IVARS) return false;
c->nivars += n;
c->ivars = (gravity_value_t *)mem_alloc(c->nivars * sizeof(gravity_value_t));
for (uint32_t i=0; i<c->nivars; ++i) c->ivars[i] = VALUE_FROM_NULL;
return true;
}
bool gravity_class_setsuper (gravity_class_t *baseclass, gravity_class_t *superclass) {
if (!superclass) return true;
baseclass->superclass = superclass;
gravity_class_t *supermeta = (superclass) ? gravity_class_get_meta(superclass) : NULL;
uint32_t n1 = (supermeta) ? supermeta->nivars : 0;
if (n1) if (!gravity_class_grow (gravity_class_get_meta(baseclass), n1)) return false;
uint32_t n2 = (superclass) ? superclass->nivars : 0;
if (n2) if (!gravity_class_grow (baseclass, n2)) return false;
return true;
}
gravity_class_t *gravity_class_new_single (gravity_vm *vm, const char *identifier, uint32_t nivar) {
gravity_class_t *c = (gravity_class_t *)mem_alloc(sizeof(gravity_class_t));
assert(c);
c->isa = gravity_class_class;
c->identifier = string_dup(identifier);
c->superclass = NULL;
c->nivars = nivar;
c->htable = gravity_hash_create(0, gravity_value_hash, gravity_value_equals, gravity_hash_keyfree, NULL);
if (nivar) {
c->ivars = (gravity_value_t *)mem_alloc(nivar * sizeof(gravity_value_t));
for (uint32_t i=0; i<nivar; ++i) c->ivars[i] = VALUE_FROM_NULL;
}
if (vm) gravity_vm_transfer(vm, (gravity_object_t*) c);
return c;
}
gravity_class_t *gravity_class_new_pair (gravity_vm *vm, const char *identifier, gravity_class_t *superclass, uint32_t nivar, uint32_t nsvar) {
if (!identifier) return NULL;
char buffer[512];
snprintf(buffer, sizeof(buffer), "%s meta", identifier);
gravity_class_t *supermeta = (superclass) ? gravity_class_get_meta(superclass) : NULL;
uint32_t n1 = (supermeta) ? supermeta->nivars : 0;
gravity_class_t *meta = gravity_class_new_single(vm, buffer, nsvar + n1);
meta->objclass = gravity_class_object;
gravity_class_setsuper(meta, gravity_class_class);
uint32_t n2 = (superclass) ? superclass->nivars : 0;
gravity_class_t *c = gravity_class_new_single(vm, identifier, nivar + n2);
c->objclass = meta;
gravity_class_setsuper(c, (superclass) ? superclass : gravity_class_object);
return c;
}
gravity_class_t *gravity_class_get_meta (gravity_class_t *c) {
if (c->objclass == gravity_class_object) return c;
return c->objclass;
}
bool gravity_class_is_meta (gravity_class_t *c) {
return (c->objclass == gravity_class_object);
}
uint32_t gravity_class_count_ivars (gravity_class_t *c) {
return (uint32_t)c->nivars;
}
int16_t gravity_class_add_ivar (gravity_class_t *c, const char *identifier) {
#pragma unused(identifier)
++c->nivars;
return c->nivars-1;
}
void gravity_class_dump (gravity_class_t *c) {
gravity_hash_dump(c->htable);
}
void gravity_class_setxdata (gravity_class_t *c, void *xdata) {
c->xdata = xdata;
}
void gravity_class_serialize (gravity_class_t *c, json_t *json) {
json_begin_object(json, c->identifier);
json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_CLASS);
json_add_cstring(json, GRAVITY_JSON_LABELIDENTIFIER, c->identifier);
if ((c->superclass) && (c->superclass->identifier) && (strcmp(c->superclass->identifier, GRAVITY_CLASS_OBJECT_NAME) != 0)) {
json_add_cstring(json, GRAVITY_JSON_LABELSUPER, c->superclass->identifier);
}
gravity_class_t *meta = gravity_class_get_meta(c);
json_add_int(json, GRAVITY_JSON_LABELNIVAR, c->nivars);
if ((c != meta) && (meta->nivars > 0)) json_add_int(json, GRAVITY_JSON_LABELSIVAR, meta->nivars);
if (c->is_struct) json_add_bool(json, GRAVITY_JSON_LABELSTRUCT, true);
if (c->htable) {
gravity_hash_iterate(c->htable, gravity_hash_serialize, (void *)json);
}
if (c != meta) {
if ((meta->htable) && (gravity_hash_count(meta->htable) > 0)) {
json_begin_array(json, GRAVITY_JSON_LABELMETA);
gravity_hash_iterate(meta->htable, gravity_hash_serialize, (void *)json);
json_end_array(json);
}
}
json_end_object(json);
}
gravity_class_t *gravity_class_deserialize (gravity_vm *vm, json_value *json) {
if (json->type != json_object) return NULL;
if (json->u.object.length < 3) return NULL;
json_value *value = json->u.object.values[1].value;
const char *key = json->u.object.values[1].name;
if (string_casencmp(key, GRAVITY_JSON_LABELIDENTIFIER, strlen(key)) != 0) return NULL;
assert(value->type == json_string);
gravity_class_t *c = gravity_class_new_pair(vm, value->u.string.ptr, NULL, 0, 0);
DEBUG_DESERIALIZE("DESERIALIZE CLASS: %p %s\n", c, value->u.string.ptr);
gravity_class_t *meta = gravity_class_get_meta(c);
uint32_t n = json->u.object.length;
for (uint32_t i=2; i<n; ++i) {
value = json->u.object.values[i].value;
key = json->u.object.values[i].name;
if (value->type != json_object) {
if (string_casencmp(key, GRAVITY_JSON_LABELSUPER, strlen(key)) == 0) {
if (strcmp(value->u.string.ptr, GRAVITY_CLASS_OBJECT_NAME) != 0) {
c->xdata = (void *)string_dup(value->u.string.ptr);
}
continue;
}
if (string_casencmp(key, GRAVITY_JSON_LABELNIVAR, strlen(key)) == 0) {
gravity_class_grow(c, (uint32_t)value->u.integer);
continue;
}
if (string_casencmp(key, GRAVITY_JSON_LABELSIVAR, strlen(key)) == 0) {
gravity_class_grow(meta, (uint32_t)value->u.integer);
continue;
}
if (string_casencmp(key, GRAVITY_JSON_LABELSTRUCT, strlen(key)) == 0) {
c->is_struct = true;
continue;
}
if (string_casencmp(key, GRAVITY_JSON_LABELMETA, strlen(key)) == 0) {
uint32_t m = value->u.array.length;
for (uint32_t j=0; j<m; ++j) {
json_value *r = value->u.array.values[j];
if (r->type != json_object) continue;
gravity_object_t *obj = NULL;
bool result = gravity_object_deserialize(vm, r, &obj);
const char *identifier = obj->identifier;
if (OBJECT_ISA_FUNCTION(obj)) obj = (gravity_object_t *)gravity_closure_new(vm, (gravity_function_t *)obj);
if ((result) && (obj)) gravity_class_bind(meta, identifier, VALUE_FROM_OBJECT(obj));
else goto abort_load;
}
continue;
}
assert(0);
}
if (value->type == json_object) {
gravity_object_t *obj = NULL;
if (!gravity_object_deserialize(vm, value, &obj)) goto abort_load;
if (!obj) goto abort_load;
const char *identifier = obj->identifier;
if (OBJECT_ISA_FUNCTION(obj)) obj = (gravity_object_t *)gravity_closure_new(vm, (gravity_function_t *)obj);
gravity_class_bind(c, identifier, VALUE_FROM_OBJECT(obj));
}
}
return c;
abort_load:
if (c) gravity_class_free(vm, c);
return NULL;
}
static void gravity_class_free_internal (gravity_vm *vm, gravity_class_t *c, bool skip_base) {
if (skip_base && gravity_iscore_class(c)) return;
DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)c));
if (c->xdata && vm) {
gravity_delegate_t *delegate = gravity_vm_delegate(vm);
if (delegate && delegate->bridge_free) delegate->bridge_free(vm, (gravity_object_t *)c);
}
if (c->identifier) mem_free((void *)c->identifier);
if (!skip_base) {
gravity_hash_iterate(c->htable, gravity_hash_valuefree, NULL);
}
gravity_hash_free(c->htable);
if (c->ivars) mem_free((void *)c->ivars);
mem_free((void *)c);
}
void gravity_class_free_core (gravity_vm *vm, gravity_class_t *c) {
gravity_class_free_internal(vm, c, false);
}
void gravity_class_free (gravity_vm *vm, gravity_class_t *c) {
gravity_class_free_internal(vm, c, true);
}
inline gravity_object_t *gravity_class_lookup (gravity_class_t *c, gravity_value_t key) {
while (c) {
gravity_value_t *v = gravity_hash_lookup(c->htable, key);
if (v) return (gravity_object_t *)v->p;
c = c->superclass;
}
return NULL;
}
inline gravity_closure_t *gravity_class_lookup_closure (gravity_class_t *c, gravity_value_t key) {
gravity_object_t *obj = gravity_class_lookup(c, key);
if (obj && OBJECT_ISA_CLOSURE(obj)) return (gravity_closure_t *)obj;
return NULL;
}
inline gravity_closure_t *gravity_class_lookup_constructor (gravity_class_t *c, uint32_t nparams) {
if (c->xdata) {
if (nparams == 0) {
STATICVALUE_FROM_STRING(key, CLASS_INTERNAL_INIT_NAME, strlen(CLASS_INTERNAL_INIT_NAME));
return (gravity_closure_t *)gravity_class_lookup(c, key);
}
char name[256]; snprintf(name, sizeof(name), "%s%d", CLASS_INTERNAL_INIT_NAME, nparams);
STATICVALUE_FROM_STRING(key, name, strlen(name));
return (gravity_closure_t *)gravity_class_lookup(c, key);
}
STATICVALUE_FROM_STRING(key, CLASS_CONSTRUCTOR_NAME, strlen(CLASS_CONSTRUCTOR_NAME));
return (gravity_closure_t *)gravity_class_lookup(c, key);
}
uint32_t gravity_class_size (gravity_vm *vm, gravity_class_t *c) {
uint32_t class_size = sizeof(gravity_class_t) + (c->nivars * sizeof(gravity_value_t)) + string_size(c->identifier);
uint32_t hash_size = 0;
gravity_hash_iterate2(c->htable, gravity_hash_internalsize, (void *)&hash_size, (void *)vm);
hash_size += gravity_hash_memsize(c->htable);
gravity_delegate_t *delegate = gravity_vm_delegate(vm);
if ((c->xdata) && (delegate) && (delegate->bridge_size))
class_size += delegate->bridge_size(vm, c->xdata);
return class_size;
}
void gravity_class_blacken (gravity_vm *vm, gravity_class_t *c) {
gravity_vm_memupdate(vm, gravity_class_size(vm, c));
gravity_gray_object(vm, (gravity_object_t *)c->objclass);
gravity_gray_object(vm, (gravity_object_t *)c->superclass);
gravity_hash_iterate(c->htable, gravity_hash_gray, (void *)vm);
for (uint32_t i=0; i<c->nivars; ++i) {
gravity_gray_value(vm, c->ivars[i]);
}
}
gravity_function_t *gravity_function_new (gravity_vm *vm, const char *identifier, uint16_t nparams, uint16_t nlocals, uint16_t ntemps, void *code) {
gravity_function_t *f = (gravity_function_t *)mem_alloc(sizeof(gravity_function_t));
assert(f);
f->isa = gravity_class_function;
f->identifier = (identifier) ? string_dup(identifier) : NULL;
f->tag = EXEC_TYPE_NATIVE;
f->nparams = nparams;
f->nlocals = nlocals;
f->ntemps = ntemps;
f->nupvalues = 0;
f->useargs = false;
f->bytecode = (uint32_t *)code;
marray_init(f->cpool);
if (vm) gravity_vm_transfer(vm, (gravity_object_t*)f);
return f;
}
gravity_function_t *gravity_function_new_internal (gravity_vm *vm, const char *identifier, gravity_c_internal exec, uint16_t nparams) {
gravity_function_t *f = gravity_function_new(vm, identifier, nparams, 0, 0, NULL);
f->tag = EXEC_TYPE_INTERNAL;
f->internal = exec;
return f;
}
gravity_function_t *gravity_function_new_special (gravity_vm *vm, const char *identifier, uint16_t index, void *getter, void *setter) {
gravity_function_t *f = gravity_function_new(vm, identifier, 0, 0, 0, NULL);
f->tag = EXEC_TYPE_SPECIAL;
f->index = index;
f->special[0] = getter;
f->special[1] = setter;
return f;
}
gravity_function_t *gravity_function_new_bridged (gravity_vm *vm, const char *identifier, void *xdata) {
gravity_function_t *f = gravity_function_new(vm, identifier, 0, 0, 0, NULL);
f->tag = EXEC_TYPE_BRIDGED;
f->xdata = xdata;
return f;
}
uint16_t gravity_function_cpool_add (gravity_vm *vm, gravity_function_t *f, gravity_value_t v) {
assert(f->tag == EXEC_TYPE_NATIVE);
size_t n = marray_size(f->cpool);
for (size_t i=0; i<n; i++) {
gravity_value_t v2 = marray_get(f->cpool, i);
if (gravity_value_equals(v,v2)) {
gravity_value_free(NULL, v);
return i;
}
}
if ((vm) && (gravity_value_isobject(v))) gravity_vm_transfer(vm, VALUE_AS_OBJECT(v));
marray_push(gravity_value_t, f->cpool, v);
return (uint16_t)marray_size(f->cpool)-1;
}
gravity_value_t gravity_function_cpool_get (gravity_function_t *f, uint16_t i) {
assert(f->tag == EXEC_TYPE_NATIVE);
return marray_get(f->cpool, i);
}
void gravity_function_setxdata (gravity_function_t *f, void *xdata) {
f->xdata = xdata;
}
static void gravity_function_cpool_serialize (gravity_function_t *f, json_t *json) {
assert(f->tag == EXEC_TYPE_NATIVE);
size_t n = marray_size(f->cpool);
for (size_t i=0; i<n; i++) {
gravity_value_t v = marray_get(f->cpool, i);
gravity_value_serialize(v, json);
}
}
static void gravity_function_cpool_dump (gravity_function_t *f) {
assert(f->tag == EXEC_TYPE_NATIVE);
size_t n = marray_size(f->cpool);
for (size_t i=0; i<n; i++) {
gravity_value_t v = marray_get(f->cpool, i);
if (v.isa == gravity_class_bool) {
printf("%05zu\tBOOL: %d\n", i, (v.n == 0) ? 0 : 1);
} else if (v.isa == gravity_class_int) {
printf("%05zu\tINT: %lld\n", i, (int64_t)v.n);
} else if (v.isa == gravity_class_float) {
printf("%05zu\tFLOAT: %f\n", i, (double)v.f);
} else if (v.isa == gravity_class_function) {
gravity_function_t *vf = VALUE_AS_FUNCTION(v);
printf("%05zu\tFUNC: %s\n", i, (vf->identifier) ? vf->identifier : "$anon");
} else if (v.isa == gravity_class_class) {
gravity_class_t *c = VALUE_AS_CLASS(v);
printf("%05zu\tCLASS: %s\n", i, (c->identifier) ? c->identifier: "$anon");
} else if (v.isa == gravity_class_string) {
printf("%05zu\tSTRING: %s\n", i, VALUE_AS_CSTRING(v));
} else if (v.isa == gravity_class_list) {
gravity_list_t *value = VALUE_AS_LIST(v);
size_t count = marray_size(value->array);
printf("%05zu\tLIST: %zu items\n", i, count);
} else if (v.isa == gravity_class_map) {
gravity_map_t *map = VALUE_AS_MAP(v);
printf("%05zu\tMAP: %u items\n", i, gravity_hash_count(map->hash));
} else {
assert(0);
}
}
}
static void gravity_function_bytecode_serialize (gravity_function_t *f, json_t *json) {
if (!f->bytecode) {
json_add_null(json, GRAVITY_JSON_LABELBYTECODE);
return;
}
uint32_t ninst = f->ninsts;
uint32_t length = ninst * 2 * sizeof(uint32_t);
uint8_t *hexchar = (uint8_t*) mem_alloc(sizeof(uint8_t) * length);
for (uint32_t k=0, i=0; i < ninst; ++i) {
uint32_t value = f->bytecode[i];
for (int32_t j=2*sizeof(value)-1; j>=0; --j) {
uint8_t c = "0123456789ABCDEF"[((value >> (j*4)) & 0xF)];
hexchar[k++] = c;
}
}
json_add_string(json, GRAVITY_JSON_LABELBYTECODE, (const char *)hexchar, length);
mem_free(hexchar);
}
uint32_t *gravity_bytecode_deserialize (const char *buffer, size_t len, uint32_t *n) {
uint32_t ninst = (uint32_t)len / 8;
uint32_t *bytecode = (uint32_t *)mem_alloc(sizeof(uint32_t) * (ninst + 1));
for (uint32_t j=0; j<ninst; ++j) {
register uint32_t v = 0;
for (uint32_t i=(j*8); i<=(j*8)+7; ++i) {
register uint32_t c = buffer[i];
if (c >= 'A' && c <= 'F') {
c = c - 'A' + 10;
} else if (c >= '0' && c <= '9') {
c -= '0';
} else goto abort_conversion;
v = v << 4 | c;
}
bytecode[j] = v;
}
*n = ninst;
return bytecode;
abort_conversion:
if (bytecode) mem_free(bytecode);
*n = 0;
return NULL;
}
void gravity_function_dump (gravity_function_t *f, code_dump_function codef) {
printf("Function: %s\n", (f->identifier) ? f->identifier : "$anon");
printf("Params:%d Locals:%d Temp:%d Upvalues:%d\n", f->nparams, f->nlocals, f->ntemps, f->nupvalues);
if (f->tag == EXEC_TYPE_NATIVE) {
if (marray_size(f->cpool)) printf("======= CPOOL =======\n");
gravity_function_cpool_dump(f);
printf("======= BYTECODE ====\n");
if ((f->bytecode) && (codef)) codef(f->bytecode);
}
printf("\n");
}
void gravity_function_special_serialize (gravity_function_t *f, const char *key, json_t *json) {
json_begin_object(json, key);
json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_FUNCTION);
json_add_cstring(json, GRAVITY_JSON_LABELIDENTIFIER, key);
json_add_int(json, GRAVITY_JSON_LABELTAG, f->tag);
json_add_int(json, GRAVITY_JSON_LABELNPARAM, f->nparams);
json_add_bool(json, GRAVITY_JSON_LABELARGS, f->useargs);
json_add_int(json, GRAVITY_JSON_LABELINDEX, f->index);
if (f->special[0]) {
gravity_function_t *f2 = (gravity_function_t*)f->special[0];
f2->identifier = GRAVITY_JSON_GETTER;
gravity_function_serialize(f2, json);
f2->identifier = NULL;
}
if (f->special[1]) {
gravity_function_t *f2 = (gravity_function_t*)f->special[1];
f2->identifier = GRAVITY_JSON_SETTER;
gravity_function_serialize(f2, json);
f2->identifier = NULL;
}
json_end_object(json);
}
void gravity_function_serialize (gravity_function_t *f, json_t *json) {
if (f->tag == EXEC_TYPE_SPECIAL) {
gravity_function_special_serialize(f, f->identifier, json);
return;
}
const char *identifier = f->identifier;
char temp[256];
if (!identifier) {snprintf(temp, sizeof(temp), "$anon_%p", f); identifier = temp;}
if (identifier) json_begin_object(json, identifier);
json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_FUNCTION);
if (identifier) json_add_cstring(json, GRAVITY_JSON_LABELIDENTIFIER, identifier);
json_add_int(json, GRAVITY_JSON_LABELTAG, f->tag);
json_add_int(json, GRAVITY_JSON_LABELNPARAM, f->nparams);
json_add_bool(json, GRAVITY_JSON_LABELARGS, f->useargs);
if (f->tag == EXEC_TYPE_NATIVE) {
json_add_int(json, GRAVITY_JSON_LABELNLOCAL, f->nlocals);
json_add_int(json, GRAVITY_JSON_LABELNTEMP, f->ntemps);
json_add_int(json, GRAVITY_JSON_LABELNUPV, f->nupvalues);
json_add_double(json, GRAVITY_JSON_LABELPURITY, f->purity);
gravity_function_bytecode_serialize(f, json);
json_begin_array(json, GRAVITY_JSON_LABELPOOL);
gravity_function_cpool_serialize(f, json);
json_end_array(json);
}
if (identifier) json_end_object(json);
}
gravity_function_t *gravity_function_deserialize (gravity_vm *vm, json_value *json) {
gravity_function_t *f = gravity_function_new(vm, NULL, 0, 0, 0, NULL);
DEBUG_DESERIALIZE("DESERIALIZE FUNCTION: %p\n", f);
uint32_t n = json->u.object.length;
for (uint32_t i=1; i<n; ++i) {
const char *label = json->u.object.values[i].name;
json_value *value = json->u.object.values[i].value;
size_t label_size = strlen(label);
if (string_casencmp(label, GRAVITY_JSON_LABELIDENTIFIER, label_size) == 0) {
assert(value->type == json_string);
if (strncmp(value->u.string.ptr, "$anon", 5) != 0) {
f->identifier = string_dup(value->u.string.ptr);
DEBUG_DESERIALIZE("IDENTIFIER: %s\n", value->u.string.ptr);
}
continue;
}
if (string_casencmp(label, GRAVITY_JSON_LABELTAG, label_size) == 0) {
assert(value->type == json_integer);
f->tag = (uint16_t)value->u.integer;
continue;
}
if (string_casencmp(label, GRAVITY_JSON_LABELINDEX, label_size) == 0) {
assert(value->type == json_integer);
assert(f->tag == EXEC_TYPE_SPECIAL);
f->index = (uint16_t)value->u.integer;
continue;
}
if (string_casencmp(label, GRAVITY_JSON_GETTER, strlen(GRAVITY_JSON_GETTER)) == 0) {
assert(f->tag == EXEC_TYPE_SPECIAL);
gravity_function_t *getter = gravity_function_deserialize(vm, value);
f->special[0] = gravity_closure_new(vm, getter);
continue;
}
if (string_casencmp(label, GRAVITY_JSON_SETTER, strlen(GRAVITY_JSON_SETTER)) == 0) {
assert(f->tag == EXEC_TYPE_SPECIAL);
gravity_function_t *setter = gravity_function_deserialize(vm, value);
f->special[1] = gravity_closure_new(vm, setter);
continue;
}
if (string_casencmp(label, GRAVITY_JSON_LABELNPARAM, label_size) == 0) {
assert(value->type == json_integer);
f->nparams = (uint16_t)value->u.integer;
continue;
}
if (string_casencmp(label, GRAVITY_JSON_LABELNLOCAL, label_size) == 0) {
assert(value->type == json_integer);
f->nlocals = (uint16_t)value->u.integer;
continue;
}
if (string_casencmp(label, GRAVITY_JSON_LABELNTEMP, label_size) == 0) {
assert(value->type == json_integer);
f->ntemps = (uint16_t)value->u.integer;
continue;
}
if (string_casencmp(label, GRAVITY_JSON_LABELNUPV, label_size) == 0) {
assert(value->type == json_integer);
f->nupvalues = (uint16_t)value->u.integer;
continue;
}
if (string_casencmp(label, GRAVITY_JSON_LABELARGS, label_size) == 0) {
assert(value->type == json_boolean);
f->useargs = (bool)value->u.boolean;
continue;
}
if (string_casencmp(label, GRAVITY_JSON_LABELBYTECODE, label_size) == 0) {
if (value->type == json_null) continue;
assert(value->type == json_string);
f->bytecode = gravity_bytecode_deserialize(value->u.string.ptr, value->u.string.length, &f->ninsts);
continue;
}
if (string_casencmp(label, GRAVITY_JSON_LABELPOOL, label_size) == 0) {
assert(value->type == json_array);
uint32_t m = value->u.array.length;
for (uint32_t j=0; j<m; ++j) {
json_value *r = value->u.array.values[j];
switch (r->type) {
case json_integer:
gravity_function_cpool_add(NULL, f, VALUE_FROM_INT((gravity_int_t)r->u.integer));
break;
case json_double:
gravity_function_cpool_add(NULL, f, VALUE_FROM_FLOAT((gravity_float_t)r->u.dbl));
break;
case json_boolean:
gravity_function_cpool_add(NULL, f, VALUE_FROM_BOOL(r->u.boolean));
break;
case json_string:
gravity_function_cpool_add(vm, f, VALUE_FROM_STRING(NULL, r->u.string.ptr, r->u.string.length));
break;
case json_object: {
gravity_object_t *obj = NULL;
bool result = gravity_object_deserialize(vm, r, &obj);
if ((result) && (obj)) gravity_function_cpool_add(NULL, f, VALUE_FROM_OBJECT(obj));
else goto abort_load;
break;
}
case json_array: {
uint32_t count = r->u.array.length;
gravity_list_t *list = gravity_list_new(NULL, count);
for (uint32_t k=0; k<count; ++k) {
json_value *jsonv = r->u.array.values[k];
gravity_value_t v;
switch (jsonv->type) {
case json_integer: v = VALUE_FROM_INT((gravity_int_t)jsonv->u.integer); break;
case json_double: v = VALUE_FROM_FLOAT((gravity_float_t)jsonv->u.dbl); break;
case json_boolean: v = VALUE_FROM_BOOL(jsonv->u.boolean); break;
case json_string: v = VALUE_FROM_STRING(vm, jsonv->u.string.ptr, jsonv->u.string.length); break;
default:assert(0);
}
marray_push(gravity_value_t, list->array, v);
}
gravity_function_cpool_add(vm, f, VALUE_FROM_OBJECT(list));
}
case json_none:
case json_null:
gravity_function_cpool_add(NULL, f, VALUE_FROM_NULL);
break;
}
}
}
}
return f;
abort_load:
if (f) gravity_function_free(vm, f);
return NULL;
}
void gravity_function_free (gravity_vm *vm, gravity_function_t *f) {
if (!f) return;
DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)f));
if (f->xdata && vm) {
gravity_delegate_t *delegate = gravity_vm_delegate(vm);
if (delegate && delegate->bridge_free) delegate->bridge_free(vm, (gravity_object_t *)f);
}
if (f->identifier) mem_free((void *)f->identifier);
if (f->tag == EXEC_TYPE_NATIVE) {
if (f->bytecode) mem_free((void *)f->bytecode);
marray_destroy(f->cpool);
}
mem_free((void *)f);
}
uint32_t gravity_function_size (gravity_vm *vm, gravity_function_t *f) {
uint32_t func_size = sizeof(gravity_function_t) + string_size(f->identifier);
if (f->tag == EXEC_TYPE_NATIVE) {
if (f->bytecode) func_size += f->ninsts * sizeof(uint32_t);
size_t n = marray_size(f->cpool);
for (size_t i=0; i<n; i++) {
gravity_value_t v = marray_get(f->cpool, i);
func_size += gravity_value_size(vm, v);
}
} else if (f->tag == EXEC_TYPE_SPECIAL) {
if (f->special[0]) func_size += gravity_closure_size(vm, (gravity_closure_t *)f->special[0]);
if ((f->special[1]) && (f->special[0] != f->special[1])) func_size += gravity_closure_size(vm, (gravity_closure_t *)f->special[1]);
} else if (f->tag == EXEC_TYPE_BRIDGED) {
gravity_delegate_t *delegate = gravity_vm_delegate(vm);
if ((f->xdata) && (delegate) && (delegate->bridge_size))
func_size += delegate->bridge_size(vm, f->xdata);
}
return func_size;
}
void gravity_function_blacken (gravity_vm *vm, gravity_function_t *f) {
gravity_vm_memupdate(vm, gravity_function_size(vm, f));
if (f->tag == EXEC_TYPE_SPECIAL) {
if (f->special[0]) gravity_gray_object(vm, (gravity_object_t *)f->special[0]);
if (f->special[1]) gravity_gray_object(vm, (gravity_object_t *)f->special[1]);
}
if (f->tag == EXEC_TYPE_NATIVE) {
size_t n = marray_size(f->cpool);
for (size_t i=0; i<n; i++) {
gravity_value_t v = marray_get(f->cpool, i);
gravity_gray_value(vm, v);
}
}
}
gravity_closure_t *gravity_closure_new (gravity_vm *vm, gravity_function_t *f) {
#pragma unused(vm)
gravity_closure_t *closure = (gravity_closure_t *)mem_alloc(sizeof(gravity_closure_t));
assert(closure);
closure->isa = gravity_class_closure;
closure->f = f;
uint16_t nupvalues = (f) ? f->nupvalues : 0;
closure->upvalue = (nupvalues) ? (gravity_upvalue_t **)mem_alloc(sizeof(gravity_upvalue_t*) * (f->nupvalues + 1)) : NULL;
if (vm) gravity_vm_transfer(vm, (gravity_object_t*)closure);
return closure;
}
void gravity_closure_free (gravity_vm *vm, gravity_closure_t *closure) {
#pragma unused(vm)
DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)closure));
if (closure->upvalue) mem_free(closure->upvalue);
mem_free(closure);
}
uint32_t gravity_closure_size (gravity_vm *vm, gravity_closure_t *closure) {
#pragma unused(vm)
uint32_t closure_size = sizeof(gravity_closure_t);
gravity_upvalue_t **upvalue = closure->upvalue;
while (upvalue) {
closure_size += sizeof(gravity_upvalue_t*);
++upvalue;
}
return closure_size;
}
void gravity_closure_blacken (gravity_vm *vm, gravity_closure_t *closure) {
gravity_vm_memupdate(vm, gravity_closure_size(vm, closure));
gravity_gray_object(vm, (gravity_object_t*)closure->f);
gravity_upvalue_t **upvalue = closure->upvalue;
while (upvalue) {
gravity_gray_object(vm, (gravity_object_t*)upvalue[0]);
++upvalue;
}
}
gravity_upvalue_t *gravity_upvalue_new (gravity_vm *vm, gravity_value_t *value) {
#pragma unused(vm)
gravity_upvalue_t *upvalue = (gravity_upvalue_t *)mem_alloc(sizeof(gravity_upvalue_t));
upvalue->isa = gravity_class_upvalue;
upvalue->value = value;
upvalue->closed = VALUE_FROM_NULL;
upvalue->next = NULL;
if (vm) gravity_vm_transfer(vm, (gravity_object_t*)upvalue);
return upvalue;
}
uint32_t gravity_upvalue_size (gravity_vm *vm, gravity_upvalue_t *upvalue) {
#pragma unused(vm, upvalue)
return sizeof(gravity_upvalue_t);
}
void gravity_upvalue_blacken (gravity_vm *vm, gravity_upvalue_t *upvalue) {
#pragma unused(vm)
gravity_vm_memupdate(vm, gravity_upvalue_size(vm, upvalue));
gravity_gray_value(vm, upvalue->closed);
}
void gravity_upvalue_free(gravity_vm *vm, gravity_upvalue_t *upvalue) {
#pragma unused(vm)
DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)upvalue));
mem_free(upvalue);
}
gravity_fiber_t *gravity_fiber_new (gravity_vm *vm, gravity_closure_t *closure, uint32_t nstack, uint32_t nframes) {
gravity_fiber_t *fiber = (gravity_fiber_t *)mem_alloc(sizeof(gravity_fiber_t));
assert(fiber);
fiber->isa = gravity_class_fiber;
fiber->caller = NULL;
fiber->result = VALUE_FROM_NULL;
if (nstack < DEFAULT_MINSTACK_SIZE) nstack = DEFAULT_MINSTACK_SIZE;
fiber->stack = (gravity_value_t *)mem_alloc(sizeof(gravity_value_t) * nstack);
fiber->stacktop = fiber->stack;
fiber->stackalloc = nstack;
if (nframes < DEFAULT_MINCFRAME_SIZE) nframes = DEFAULT_MINCFRAME_SIZE;
fiber->frames = (gravity_callframe_t *)mem_alloc(sizeof(gravity_callframe_t) * nframes);
fiber->framesalloc = nframes;
fiber->nframes = 1;
fiber->upvalues = NULL;
gravity_callframe_t *frame = &fiber->frames[0];
if (closure) {
frame->closure = closure;
frame->ip = (closure->f->tag == EXEC_TYPE_NATIVE) ? closure->f->bytecode : NULL;
}
frame->dest = 0;
frame->stackstart = fiber->stack;
gravity_vm_transfer(vm, (gravity_object_t*) fiber);
return fiber;
}
void gravity_fiber_free (gravity_vm *vm, gravity_fiber_t *fiber) {
#pragma unused(vm)
DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)fiber));
if (fiber->error) mem_free(fiber->error);
mem_free(fiber->stack);
mem_free(fiber->frames);
mem_free(fiber);
}
void gravity_fiber_reassign (gravity_fiber_t *fiber, gravity_closure_t *closure, uint16_t nargs) {
gravity_callframe_t *frame = &fiber->frames[0];
frame->closure = closure;
frame->ip = (closure->f->tag == EXEC_TYPE_NATIVE) ? closure->f->bytecode : NULL;
frame->dest = 0;
frame->stackstart = fiber->stack;
fiber->nframes = 1;
fiber->upvalues = NULL;
fiber->stacktop += FN_COUNTREG(closure->f, nargs);
}
void gravity_fiber_seterror (gravity_fiber_t *fiber, const char *error) {
if (fiber->error) mem_free(fiber->error);
fiber->error = (char *)string_dup(error);
}
uint32_t gravity_fiber_size (gravity_vm *vm, gravity_fiber_t *fiber) {
uint32_t fiber_size = sizeof(gravity_fiber_t);
fiber_size += fiber->stackalloc * sizeof(gravity_value_t);
fiber_size += fiber->framesalloc * sizeof(gravity_callframe_t);
for (gravity_value_t* slot = fiber->stack; slot < fiber->stacktop; ++slot) {
fiber_size += gravity_value_size(vm, *slot);
}
fiber_size += string_size(fiber->error);
fiber_size += gravity_object_size(vm, (gravity_object_t *)fiber->caller);
return fiber_size;
}
void gravity_fiber_blacken (gravity_vm *vm, gravity_fiber_t *fiber) {
gravity_vm_memupdate(vm, gravity_fiber_size(vm, fiber));
for (uint32_t i=0; i < fiber->nframes; ++i) {
gravity_gray_object(vm, (gravity_object_t *)fiber->frames[i].closure);
}
for (gravity_value_t* slot = fiber->stack; slot < fiber->stacktop; ++slot) {
gravity_gray_value(vm, *slot);
}
gravity_upvalue_t* upvalue = fiber->upvalues;
while (upvalue) {
gravity_gray_object(vm, (gravity_object_t *)upvalue);
upvalue = upvalue->next;
}
gravity_gray_object(vm, (gravity_object_t *)fiber->caller);
}
void gravity_object_serialize (gravity_object_t *obj, json_t *json) {
if (obj->isa == gravity_class_function)
gravity_function_serialize((gravity_function_t *)obj, json);
else if (obj->isa == gravity_class_class)
gravity_class_serialize((gravity_class_t *)obj, json);
else assert(0);
}
bool gravity_object_deserialize (gravity_vm *vm, json_value *entry, gravity_object_t **obj) {
if (entry->type != json_object) return false;
if (entry->u.object.length == 0) return false;
const char *label = entry->u.object.values[0].name;
json_value *value = entry->u.object.values[0].value;
if (string_casencmp(label, GRAVITY_JSON_LABELTYPE, 4) != 0) return false;
if (value->type != json_string) return false;
if (string_casencmp(value->u.string.ptr, GRAVITY_JSON_FUNCTION, value->u.string.length) == 0) {
gravity_function_t *f = gravity_function_deserialize(vm, entry);
if (!f) return false;
*obj = (gravity_object_t *)f;
return true;
}
if (string_casencmp(value->u.string.ptr, GRAVITY_JSON_CLASS, value->u.string.length) == 0) {
gravity_class_t *c = gravity_class_deserialize(vm, entry);
if (!c) return false;
*obj = (gravity_object_t *)c;
return true;
}
if ((string_casencmp(value->u.string.ptr, GRAVITY_JSON_MAP, value->u.string.length) == 0) ||
(string_casencmp(value->u.string.ptr, GRAVITY_JSON_ENUM, value->u.string.length) == 0)) {
gravity_map_t *m = gravity_map_deserialize(vm, entry);
if (!m) return false;
*obj = (gravity_object_t *)m;
return true;
}
DEBUG_DESERIALIZE("gravity_object_deserialize unknown type");
return false;
}
#undef REPORT_JSON_ERROR
const char *gravity_object_debug (gravity_object_t *obj) {
if ((!obj) || (!OBJECT_IS_VALID(obj))) return "";
if (OBJECT_ISA_INT(obj)) return "INT";
if (OBJECT_ISA_FLOAT(obj)) return "FLOAT";
if (OBJECT_ISA_BOOL(obj)) return "BOOL";
if (OBJECT_ISA_NULL(obj)) return "NULL";
static char buffer[512];
if (OBJECT_ISA_FUNCTION(obj)) {
const char *name = ((gravity_function_t*)obj)->identifier;
if (!name) name = "ANONYMOUS";
snprintf(buffer, sizeof(buffer), "FUNCTION %p %s", obj, name);
return buffer;
}
if (OBJECT_ISA_CLOSURE(obj)) {
const char *name = ((gravity_closure_t*)obj)->f->identifier;
if (!name) name = "ANONYMOUS";
snprintf(buffer, sizeof(buffer), "CLOSURE %p %s", obj, name);
return buffer;
}
if (OBJECT_ISA_CLASS(obj)) {
const char *name = ((gravity_class_t*)obj)->identifier;
if (!name) name = "ANONYMOUS";
snprintf(buffer, sizeof(buffer), "CLASS %p %s", obj, name);
return buffer;
}
if (OBJECT_ISA_STRING(obj)) {
snprintf(buffer, sizeof(buffer), "STRING %p %s", obj, ((gravity_string_t*)obj)->s);
return buffer;
}
if (OBJECT_ISA_INSTANCE(obj)) {
gravity_class_t *c = ((gravity_instance_t*)obj)->objclass;
const char *name = (c->identifier) ? c->identifier : "ANONYMOUS";
snprintf(buffer, sizeof(buffer), "INSTANCE %p OF %s", obj, name);
return buffer;
}
if (OBJECT_ISA_RANGE(obj)) {
snprintf(buffer, sizeof(buffer), "RANGE %p %ld %ld", obj, (long)((gravity_range_t*)obj)->from, (long)((gravity_range_t*)obj)->to);
return buffer;
}
if (OBJECT_ISA_LIST(obj)) {
snprintf(buffer, sizeof(buffer), "LIST %p (%ld items)", obj, (long)marray_size(((gravity_list_t*)obj)->array));
return buffer;
}
if (OBJECT_ISA_MAP(obj)) {
snprintf(buffer, sizeof(buffer), "MAP %p (%ld items)", obj, (long)gravity_hash_count(((gravity_map_t*)obj)->hash));
return buffer;
}
if (OBJECT_ISA_FIBER(obj)) {
snprintf(buffer, sizeof(buffer), "FIBER %p", obj);
return buffer;
}
if (OBJECT_ISA_UPVALUE(obj)) {
snprintf(buffer, sizeof(buffer), "UPVALUE %p", obj);
return buffer;
}
return "N/A";
}
void gravity_object_free (gravity_vm *vm, gravity_object_t *obj) {
if ((!obj) || (!OBJECT_IS_VALID(obj))) return;
if (OBJECT_ISA_CLASS(obj)) gravity_class_free(vm, (gravity_class_t *)obj);
else if (OBJECT_ISA_FUNCTION(obj)) gravity_function_free(vm, (gravity_function_t *)obj);
else if (OBJECT_ISA_CLOSURE(obj)) gravity_closure_free(vm, (gravity_closure_t *)obj);
else if (OBJECT_ISA_INSTANCE(obj)) gravity_instance_free(vm, (gravity_instance_t *)obj);
else if (OBJECT_ISA_LIST(obj)) gravity_list_free(vm, (gravity_list_t *)obj);
else if (OBJECT_ISA_MAP(obj)) gravity_map_free(vm, (gravity_map_t *)obj);
else if (OBJECT_ISA_FIBER(obj)) gravity_fiber_free(vm, (gravity_fiber_t *)obj);
else if (OBJECT_ISA_RANGE(obj)) gravity_range_free(vm, (gravity_range_t *)obj);
else if (OBJECT_ISA_MODULE(obj)) gravity_module_free(vm, (gravity_module_t *)obj);
else if (OBJECT_ISA_STRING(obj)) gravity_string_free(vm, (gravity_string_t *)obj);
else if (OBJECT_ISA_UPVALUE(obj)) gravity_upvalue_free(vm, (gravity_upvalue_t *)obj);
else assert(0);
}
uint32_t gravity_object_size (gravity_vm *vm, gravity_object_t *obj) {
if ((!obj) || (!OBJECT_IS_VALID(obj))) return 0;
if (OBJECT_ISA_CLASS(obj)) return gravity_class_size(vm, (gravity_class_t *)obj);
else if (OBJECT_ISA_FUNCTION(obj)) return gravity_function_size(vm, (gravity_function_t *)obj);
else if (OBJECT_ISA_CLOSURE(obj)) return gravity_closure_size(vm, (gravity_closure_t *)obj);
else if (OBJECT_ISA_INSTANCE(obj)) return gravity_instance_size(vm, (gravity_instance_t *)obj);
else if (OBJECT_ISA_LIST(obj)) return gravity_list_size(vm, (gravity_list_t *)obj);
else if (OBJECT_ISA_MAP(obj)) return gravity_map_size(vm, (gravity_map_t *)obj);
else if (OBJECT_ISA_FIBER(obj)) return gravity_fiber_size(vm, (gravity_fiber_t *)obj);
else if (OBJECT_ISA_RANGE(obj)) return gravity_range_size(vm, (gravity_range_t *)obj);
else if (OBJECT_ISA_MODULE(obj)) return gravity_module_size(vm, (gravity_module_t *)obj);
else if (OBJECT_ISA_STRING(obj)) return gravity_string_size(vm, (gravity_string_t *)obj);
else if (OBJECT_ISA_UPVALUE(obj)) return gravity_upvalue_size(vm, (gravity_upvalue_t *)obj);
return 0;
}
void gravity_object_blacken (gravity_vm *vm, gravity_object_t *obj) {
if ((!obj) || (!OBJECT_IS_VALID(obj))) return;
if (OBJECT_ISA_CLASS(obj)) gravity_class_blacken(vm, (gravity_class_t *)obj);
else if (OBJECT_ISA_FUNCTION(obj)) gravity_function_blacken(vm, (gravity_function_t *)obj);
else if (OBJECT_ISA_CLOSURE(obj)) gravity_closure_blacken(vm, (gravity_closure_t *)obj);
else if (OBJECT_ISA_INSTANCE(obj)) gravity_instance_blacken(vm, (gravity_instance_t *)obj);
else if (OBJECT_ISA_LIST(obj)) gravity_list_blacken(vm, (gravity_list_t *)obj);
else if (OBJECT_ISA_MAP(obj)) gravity_map_blacken(vm, (gravity_map_t *)obj);
else if (OBJECT_ISA_FIBER(obj)) gravity_fiber_blacken(vm, (gravity_fiber_t *)obj);
else if (OBJECT_ISA_RANGE(obj)) gravity_range_blacken(vm, (gravity_range_t *)obj);
else if (OBJECT_ISA_MODULE(obj)) gravity_module_blacken(vm, (gravity_module_t *)obj);
else if (OBJECT_ISA_STRING(obj)) gravity_string_blacken(vm, (gravity_string_t *)obj);
else if (OBJECT_ISA_UPVALUE(obj)) gravity_upvalue_blacken(vm, (gravity_upvalue_t *)obj);
else assert(0);
}
gravity_instance_t *gravity_instance_new (gravity_vm *vm, gravity_class_t *c) {
gravity_instance_t *instance = (gravity_instance_t *)mem_alloc(sizeof(gravity_instance_t) + (c->nivars * sizeof(gravity_value_t)));
instance->isa = gravity_class_instance;
instance->objclass = c;
for (uint32_t i=0; i<c->nivars; ++i) instance->ivars[i] = VALUE_FROM_NULL;
if (vm) gravity_vm_transfer(vm, (gravity_object_t*) instance);
return instance;
}
gravity_instance_t *gravity_instance_dup (gravity_vm *vm, gravity_instance_t *src) {
gravity_class_t *c = src->objclass;
gravity_instance_t *instance = (gravity_instance_t *)mem_alloc(sizeof(gravity_instance_t) + (c->nivars * sizeof(gravity_value_t)));
instance->objclass = c;
for (uint32_t i=0; i<c->nivars; ++i) instance->ivars[i] = src->ivars[i];
if (vm) gravity_vm_transfer(vm, (gravity_object_t*) instance);
return instance;
}
void gravity_instance_setivar (gravity_instance_t *instance, uint32_t idx, gravity_value_t value) {
if (idx < instance->objclass->nivars) instance->ivars[idx] = value;
}
void gravity_instance_setxdata (gravity_instance_t *i, void *xdata) {
i->xdata = xdata;
}
void gravity_instance_free (gravity_vm *vm, gravity_instance_t *i) {
DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)i));
if (i->xdata && vm) {
gravity_delegate_t *delegate = gravity_vm_delegate(vm);
if (delegate && delegate->bridge_free) delegate->bridge_free(vm, (gravity_object_t *)i);
}
mem_free((void *)i);
}
gravity_closure_t *gravity_instance_lookup_event (gravity_instance_t *i, const char *name) {
STATICVALUE_FROM_STRING(key, name, strlen(name));
gravity_class_t *c = i->objclass;
while (c) {
gravity_value_t *v = gravity_hash_lookup(c->htable, key);
if ((v) && (OBJECT_ISA_CLOSURE(v->p))) return (gravity_closure_t *)v->p;
c = c->superclass;
}
return NULL;
}
uint32_t gravity_instance_size (gravity_vm *vm, gravity_instance_t *i) {
uint32_t instance_size = sizeof(gravity_instance_t) + (i->objclass->nivars * sizeof(gravity_value_t));
gravity_delegate_t *delegate = gravity_vm_delegate(vm);
if ((i->xdata) && (delegate) && (delegate->bridge_size))
instance_size += delegate->bridge_size(vm, i->xdata);
return instance_size;
}
void gravity_instance_blacken (gravity_vm *vm, gravity_instance_t *i) {
gravity_vm_memupdate(vm, gravity_instance_size(vm, i));
gravity_gray_object(vm, (gravity_object_t *)i->objclass);
for (uint32_t j=0; j<i->objclass->nivars; ++j) {
gravity_gray_value(vm, i->ivars[j]);
}
}
bool gravity_value_equals (gravity_value_t v1, gravity_value_t v2) {
if (v1.isa != v2.isa) return false;
if ((v1.isa == gravity_class_int) || (v1.isa == gravity_class_bool) || (v1.isa == gravity_class_null)) {
return (v1.n == v2.n);
} else if (v1.isa == gravity_class_float) {
#if GRAVITY_ENABLE_DOUBLE
return (fabs(v1.f - v2.f) < EPSILON);
#else
return (fabsf(v1.f - v2.f) < EPSILON);
#endif
} else if (v1.isa == gravity_class_string) {
gravity_string_t *s1 = VALUE_AS_STRING(v1);
gravity_string_t *s2 = VALUE_AS_STRING(v2);
if (s1->hash != s2->hash) return false;
if (s1->len != s2->len) return false;
return (memcmp(s1->s, s2->s, s1->len) == 0);
}
gravity_object_t *obj1 = VALUE_AS_OBJECT(v1);
gravity_object_t *obj2 = VALUE_AS_OBJECT(v2);
if (obj1->isa != obj2->isa) return false;
return (obj1 == obj2);
}
uint32_t gravity_value_hash (gravity_value_t value) {
if (value.isa == gravity_class_string)
return VALUE_AS_STRING(value)->hash;
if ((value.isa == gravity_class_int) || (value.isa == gravity_class_bool) || (value.isa == gravity_class_null))
return gravity_hash_compute_int(value.n);
if (value.isa == gravity_class_float)
return gravity_hash_compute_float(value.f);
return gravity_hash_compute_buffer((const char *)value.p, sizeof(gravity_object_t*));
}
inline gravity_class_t *gravity_value_getclass (gravity_value_t v) {
if ((v.isa == gravity_class_class) && (v.p->objclass == gravity_class_object)) return (gravity_class_t *)v.p;
if ((v.isa == gravity_class_instance) || (v.isa == gravity_class_class)) return v.p->objclass;
return v.isa;
}
inline gravity_class_t *gravity_value_getsuper (gravity_value_t v) {
gravity_class_t *c = gravity_value_getclass(v);
return (c->superclass) ? c->superclass : NULL;
}
void gravity_value_free (gravity_vm *vm, gravity_value_t v) {
if (v.isa == gravity_class_int) return;
if (v.isa == gravity_class_float) return;
if (v.isa == gravity_class_bool) return;
if (v.isa == gravity_class_null) return;
gravity_object_free(vm, VALUE_AS_OBJECT(v));
}
static void gravity_map_serialize_iterator (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t v, void *data) {
#pragma unused(hashtable)
assert(key.isa == gravity_class_string);
json_t *json = (json_t *)data;
const char *key_value = VALUE_AS_STRING(key)->s;
if (VALUE_ISA_BOOL(v)) {
json_add_bool(json, key_value, (v.n == 0) ? false : true);
return;
}
if (VALUE_ISA_INT(v)) {
json_add_int(json, key_value, (int64_t)v.n);
return;
}
if (VALUE_ISA_FLOAT(v)) {
json_add_double(json, key_value, (double)v.f);
return;
}
if (VALUE_ISA_STRING(v)) {
gravity_string_t *value = VALUE_AS_STRING(v);
json_add_string(json, key_value, value->s, value->len);
return;
}
assert(0);
}
void gravity_value_serialize (gravity_value_t v, json_t *json) {
if (VALUE_ISA_BOOL(v)) {
json_add_bool(json, NULL, (v.n == 0) ? false : true);
return;
}
if (VALUE_ISA_INT(v)) {
json_add_int(json, NULL, (int64_t)v.n);
return;
}
if (VALUE_ISA_FLOAT(v)) {
json_add_double(json, NULL, (double)v.f);
return;
}
if (VALUE_ISA_FUNCTION(v)) {
gravity_function_serialize(VALUE_AS_FUNCTION(v), json);
return;
}
if (VALUE_ISA_CLASS(v)) {
gravity_class_serialize(VALUE_AS_CLASS(v), json);
return;
}
if (VALUE_ISA_STRING(v)) {
gravity_string_t *value = VALUE_AS_STRING(v);
json_add_string(json, NULL, value->s, value->len);
return;
}
if (VALUE_ISA_LIST(v)) {
gravity_list_t *value = VALUE_AS_LIST(v);
json_begin_array(json, NULL);
size_t count = marray_size(value->array);
for (size_t j=0; j<count; j++) {
gravity_value_t item = marray_get(value->array, j);
gravity_value_serialize(item, json);
}
json_end_array(json);
return;
}
if (VALUE_ISA_MAP(v)) {
gravity_map_t *value = VALUE_AS_MAP(v);
json_begin_object(json, NULL);
json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_MAP);
gravity_hash_iterate(value->hash, gravity_map_serialize_iterator, json);
json_end_object(json);
return;
}
assert(0);
}
bool gravity_value_isobject (gravity_value_t v) {
if ((v.isa == NULL) || (v.isa == gravity_class_int) || (v.isa == gravity_class_float) ||
(v.isa == gravity_class_bool) || (v.isa == gravity_class_null)) return false;
return true;
}
uint32_t gravity_value_size (gravity_vm *vm, gravity_value_t v) {
return (gravity_value_isobject(v)) ? gravity_object_size(vm, (gravity_object_t*)v.p) : 0;
}
void *gravity_value_xdata (gravity_value_t value) {
if (VALUE_ISA_INSTANCE(value)) {
gravity_instance_t *i = VALUE_AS_INSTANCE(value);
return i->xdata;
} else if (VALUE_ISA_CLASS(value)) {
gravity_class_t *c = VALUE_AS_CLASS(value);
return c->xdata;
}
return NULL;
}
void gravity_value_dump (gravity_value_t v, char *buffer, uint16_t len) {
const char *type = NULL;
const char *value = NULL;
char sbuffer[1024];
if (buffer == NULL) buffer = sbuffer;
if (len == 0) len = sizeof(sbuffer);
if (v.isa == NULL) {
type = "INVALID!";
snprintf(buffer, len, "%s", type);
value = buffer;
} else if (v.isa == gravity_class_bool) {
type = "BOOL";
value = (v.n == 0) ? "false" : "true";
snprintf(buffer, len, "(%s) %s", type, value);
value = buffer;
} else if (v.isa == gravity_class_null) {
type = (v.n == 0) ? "NULL" : "UNDEFINED";
snprintf(buffer, len, "%s", type);
value = buffer;
} else if (v.isa == gravity_class_int) {
type = "INT";
snprintf(buffer, len, "(%s) %lld", type, v.n);
value = buffer;
} else if (v.isa == gravity_class_float) {
type = "FLOAT";
snprintf(buffer, len, "(%s) %f", type, v.f);
value = buffer;
} else if (v.isa == gravity_class_function) {
type = "FUNCTION";
value = VALUE_AS_FUNCTION(v)->identifier;
snprintf(buffer, len, "(%s) %s (%p)", type, value, VALUE_AS_FUNCTION(v));
value = buffer;
} else if (v.isa == gravity_class_class) {
type = "CLASS";
value = VALUE_AS_CLASS(v)->identifier;
snprintf(buffer, len, "(%s) %s (%p)", type, value, VALUE_AS_CLASS(v));
value = buffer;
} else if (v.isa == gravity_class_string) {
type = "STRING";
gravity_string_t *s = VALUE_AS_STRING(v);
snprintf(buffer, len, "(%s) %.*s (%p)", type, s->len, s->s, s);
value = buffer;
} else if (v.isa == gravity_class_instance) {
type = "INSTANCE OF CLASS";
gravity_instance_t *i = VALUE_AS_INSTANCE(v);
gravity_class_t *c = i->objclass;
value = c->identifier;
snprintf(buffer, len, "(%s) %s (%p)", type, value, i);
value = buffer;
} else if (v.isa == gravity_class_list) {
type = "LIST";
value = "N/A";
snprintf(buffer, len, "(%s) %s", type, value);
value = buffer;
} else if (v.isa == gravity_class_map) {
type = "MAP";
value = "N/A";
snprintf(buffer, len, "(%s) %s", type, value);
value = buffer;
} else if (v.isa == gravity_class_range) {
type = "RANGE";
gravity_range_t *r = VALUE_AS_RANGE(v);
snprintf(buffer, len, "(%s) from %lld to %lld", type, r->from, r->to);
value = buffer;
} else if (v.isa == gravity_class_object) {
type = "OBJECT";
value = "N/A";
snprintf(buffer, len, "(%s) %s", type, value);
value = buffer;
} else if (v.isa == gravity_class_fiber) {
type = "FIBER";
snprintf(buffer, len, "(%s) %p", type, v.p);
value = buffer;
} else {
type = "N/A";
value = "N/A";
snprintf(buffer, len, "(%s) %s", type, value);
value = buffer;
}
if (buffer == sbuffer) printf("%s\n", value);
}
gravity_list_t *gravity_list_new (gravity_vm *vm, uint32_t n) {
gravity_list_t *list = (gravity_list_t *)mem_alloc(sizeof(gravity_list_t));
list->isa = gravity_class_list;
marray_init(list->array);
marray_resize(gravity_value_t, list->array, n + MARRAY_DEFAULT_SIZE);
if (vm) gravity_vm_transfer(vm, (gravity_object_t*) list);
return list;
}
gravity_list_t *gravity_list_from_array (gravity_vm *vm, uint32_t n, gravity_value_t *p) {
gravity_list_t *list = (gravity_list_t *)mem_alloc(sizeof(gravity_list_t));
list->isa = gravity_class_list;
marray_init(list->array);
for (size_t i=0; i<n; ++i) marray_push(gravity_value_t, list->array, p[i]);
gravity_vm_transfer(vm, (gravity_object_t*) list);
return list;
}
void gravity_list_free (gravity_vm *vm, gravity_list_t *list) {
#pragma unused(vm)
DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)list));
marray_destroy(list->array);
mem_free((void *)list);
}
void gravity_list_append_list (gravity_vm *vm, gravity_list_t *list1, gravity_list_t *list2) {
#pragma unused(vm)
size_t count = marray_size(list2->array);
for (size_t i=0; i<count; ++i) {
marray_push(gravity_value_t, list1->array, marray_get(list2->array, i));
}
}
uint32_t gravity_list_size (gravity_vm *vm, gravity_list_t *list) {
uint32_t internal_size = 0;
size_t count = marray_size(list->array);
for (size_t i=0; i<count; ++i) {
internal_size += gravity_value_size(vm, marray_get(list->array, i));
}
return sizeof(gravity_list_t) + internal_size;
}
void gravity_list_blacken (gravity_vm *vm, gravity_list_t *list) {
gravity_vm_memupdate(vm, gravity_list_size(vm, list));
size_t count = marray_size(list->array);
for (size_t i=0; i<count; ++i) {
gravity_gray_value(vm, marray_get(list->array, i));
}
}
gravity_map_t *gravity_map_new (gravity_vm *vm, uint32_t n) {
gravity_map_t *map = (gravity_map_t *)mem_alloc(sizeof(gravity_map_t));
map->isa = gravity_class_map;
map->hash = gravity_hash_create(n, gravity_value_hash, gravity_value_equals, NULL, NULL);
gravity_vm_transfer(vm, (gravity_object_t*) map);
return map;
}
void gravity_map_free (gravity_vm *vm, gravity_map_t *map) {
#pragma unused(vm)
DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)map));
gravity_hash_free(map->hash);
mem_free((void *)map);
}
void gravity_map_append_map (gravity_vm *vm, gravity_map_t *map1, gravity_map_t *map2) {
#pragma unused(vm)
gravity_hash_append(map1->hash, map2->hash);
}
void gravity_map_insert (gravity_vm *vm, gravity_map_t *map, gravity_value_t key, gravity_value_t value) {
#pragma unused(vm)
gravity_hash_insert(map->hash, key, value);
}
static gravity_map_t *gravity_map_deserialize (gravity_vm *vm, json_value *json) {
uint32_t n = json->u.object.length;
gravity_map_t *map = gravity_map_new(vm, n);
DEBUG_DESERIALIZE("DESERIALIZE MAP: %p\n", map);
for (uint32_t i=1; i<n; ++i) {
const char *label = json->u.object.values[i].name;
json_value *jsonv = json->u.object.values[i].value;
gravity_value_t key = VALUE_FROM_CSTRING(vm, label);
gravity_value_t value;
switch (jsonv->type) {
case json_integer: value = VALUE_FROM_INT((gravity_int_t)jsonv->u.integer); break;
case json_double: value = VALUE_FROM_FLOAT((gravity_float_t)jsonv->u.dbl); break;
case json_boolean: value = VALUE_FROM_BOOL(jsonv->u.boolean); break;
case json_string: value = VALUE_FROM_STRING(vm, jsonv->u.string.ptr, jsonv->u.string.length); break;
default:assert(0);
}
gravity_map_insert(NULL, map, key, value);
}
return map;
}
uint32_t gravity_map_size (gravity_vm *vm, gravity_map_t *map) {
uint32_t hash_size = 0;
gravity_hash_iterate2(map->hash, gravity_hash_internalsize, (void *)&hash_size, (void *)vm);
hash_size += gravity_hash_memsize(map->hash);
return sizeof(gravity_map_t) + hash_size;
}
void gravity_map_blacken (gravity_vm *vm, gravity_map_t *map) {
gravity_vm_memupdate(vm, gravity_map_size(vm, map));
gravity_hash_iterate(map->hash, gravity_hash_gray, (void *)vm);
}
gravity_range_t *gravity_range_new (gravity_vm *vm, gravity_int_t from_range, gravity_int_t to_range, bool inclusive) {
gravity_range_t *range = mem_alloc(sizeof(gravity_range_t));
range->isa = gravity_class_range;
range->from = from_range;
range->to = (inclusive) ? to_range : --to_range;
gravity_vm_transfer(vm, (gravity_object_t*) range);
return range;
}
void gravity_range_free (gravity_vm *vm, gravity_range_t *range) {
#pragma unused(vm)
DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)range));
mem_free((void *)range);
}
uint32_t gravity_range_size (gravity_vm *vm, gravity_range_t *range) {
#pragma unused(vm, range)
return sizeof(gravity_range_t);
}
void gravity_range_blacken (gravity_vm *vm, gravity_range_t *range) {
gravity_vm_memupdate(vm, gravity_range_size(vm, range));
}
inline gravity_value_t gravity_string_to_value (gravity_vm *vm, const char *s, uint32_t len) {
gravity_string_t *obj = mem_alloc(sizeof(gravity_string_t));
if (len == AUTOLENGTH) len = (uint32_t)strlen(s);
uint32_t alloc = MAXNUM(len+1, DEFAULT_MINSTRING_SIZE);
char *ptr = mem_alloc(alloc);
memcpy(ptr, s, len);
obj->isa = gravity_class_string;
obj->s = ptr;
obj->len = len;
obj->alloc = alloc;
obj->hash = gravity_hash_compute_buffer((const char *)ptr, len);
gravity_value_t value;
value.isa = gravity_class_string;
value.p = (gravity_object_t *)obj;
if (vm) gravity_vm_transfer(vm, (gravity_object_t*) obj);
return value;
}
inline gravity_string_t *gravity_string_new (gravity_vm *vm, char *s, uint32_t len, uint32_t alloc) {
gravity_string_t *obj = mem_alloc(sizeof(gravity_string_t));
if (len == AUTOLENGTH) len = (uint32_t)strlen(s);
obj->isa = gravity_class_string;
obj->s = (char *)s;
obj->len = len;
obj->alloc = alloc;
if (s && len) obj->hash = gravity_hash_compute_buffer((const char *)s, len);
if (vm) gravity_vm_transfer(vm, (gravity_object_t*) obj);
return obj;
}
inline void gravity_string_set (gravity_string_t *obj, char *s, uint32_t len) {
obj->s = (char *)s;
obj->len = len;
obj->hash = gravity_hash_compute_buffer((const char *)s, len);
}
inline void gravity_string_free (gravity_vm *vm, gravity_string_t *value) {
#pragma unused(vm)
DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)value));
if (value->alloc) mem_free(value->s);
mem_free(value);
}
uint32_t gravity_string_size (gravity_vm *vm, gravity_string_t *string) {
#pragma unused(vm)
return (sizeof(gravity_string_t)) + string->alloc;
}
void gravity_string_blacken (gravity_vm *vm, gravity_string_t *string) {
gravity_vm_memupdate(vm, gravity_string_size(vm, string));
}