1 //
2 //  gravity_value.c
3 //  gravity
4 //
5 //  Created by Marco Bambini on 11/12/14.
6 //  Copyright (c) 2014 CreoLabs. All rights reserved.
7 //
8 
9 #include <inttypes.h>
10 #include "gravity_hash.h"
11 #include "gravity_core.h"
12 #include "gravity_value.h"
13 #include "gravity_utils.h"
14 #include "gravity_memory.h"
15 #include "gravity_macros.h"
16 #include "gravity_opcodes.h"
17 #include "gravity_vmmacros.h"
18 
19                                                 // mark object visited to avoid infinite loop
20 #define SET_OBJECT_VISITED_FLAG(_obj, _flag)    (((gravity_object_t *)_obj)->gc.visited = _flag)
21 
22 // MARK: -
23 
24 static void gravity_function_special_serialize (gravity_function_t *f, const char *key, json_t *json);
25 static gravity_map_t *gravity_map_deserialize (gravity_vm *vm, json_value *json);
26 
gravity_hash_serialize(gravity_hash_t * table,gravity_value_t key,gravity_value_t value,void * data)27 static void gravity_hash_serialize (gravity_hash_t *table, gravity_value_t key, gravity_value_t value, void *data) {
28     #pragma unused(table)
29     json_t *json = (json_t *)data;
30 
31     if (VALUE_ISA_CLOSURE(value)) value = VALUE_FROM_OBJECT(VALUE_AS_CLOSURE(value)->f);
32 
33     if (VALUE_ISA_FUNCTION(value)) {
34         gravity_function_t *f = VALUE_AS_FUNCTION(value);
35         if (f->tag == EXEC_TYPE_SPECIAL) gravity_function_special_serialize(f, VALUE_AS_CSTRING(key), json);
36         else {
37             // there was an issue here due to the fact that when a subclass needs to use a $init from a superclass
38             // internally it has a unique name (key) but f->identifier continue to be called $init
39             // without this fix the subclass would continue to have 2 or more $init functions
40             gravity_string_t *s = VALUE_AS_STRING(key);
41             bool is_super_function = ((s->len > 5) && (string_casencmp(s->s, CLASS_INTERNAL_INIT_NAME, 5) == 0));
42             const char *saved = f->identifier;
43             if (is_super_function) f->identifier = s->s;
44             gravity_function_serialize(f, json);
45             if (is_super_function) f->identifier = saved;
46         }
47     }
48     else if (VALUE_ISA_CLASS(value)) {
49         gravity_class_serialize(VALUE_AS_CLASS(value), json);
50     }
51     else
52         assert(0);
53 }
54 
gravity_hash_internalsize(gravity_hash_t * table,gravity_value_t key,gravity_value_t value,void * data1,void * data2)55 static void gravity_hash_internalsize (gravity_hash_t *table, gravity_value_t key, gravity_value_t value, void *data1, void *data2) {
56     #pragma unused(table)
57     uint32_t    *size = (uint32_t *)data1;
58     gravity_vm    *vm = (gravity_vm *)data2;
59     *size = gravity_value_size(vm, key);
60     *size += gravity_value_size(vm, value);
61 }
62 
gravity_hash_gray(gravity_hash_t * table,gravity_value_t key,gravity_value_t value,void * data1)63 static void gravity_hash_gray (gravity_hash_t *table, gravity_value_t key, gravity_value_t value, void *data1) {
64     #pragma unused(table)
65     gravity_vm *vm = (gravity_vm *)data1;
66     gravity_gray_value(vm, key);
67     gravity_gray_value(vm, value);
68 }
69 
70 // MARK: -
71 
gravity_module_new(gravity_vm * vm,const char * identifier)72 gravity_module_t *gravity_module_new (gravity_vm *vm, const char *identifier) {
73     gravity_module_t *m = (gravity_module_t *)mem_alloc(NULL, sizeof(gravity_module_t));
74     assert(m);
75 
76     m->isa = gravity_class_module;
77     m->identifier = string_dup(identifier);
78     m->htable = gravity_hash_create(0, gravity_value_hash, gravity_value_equals, gravity_hash_keyvaluefree, (void*)vm);
79 
80     if (vm) gravity_vm_transfer(vm, (gravity_object_t*)m);
81     return m;
82 }
83 
gravity_module_free(gravity_vm * vm,gravity_module_t * m)84 void gravity_module_free (gravity_vm *vm, gravity_module_t *m) {
85     #pragma unused(vm)
86 
87     if (m->identifier) mem_free(m->identifier);
88     gravity_hash_free(m->htable);
89     mem_free(m);
90 }
91 
gravity_module_size(gravity_vm * vm,gravity_module_t * m)92 uint32_t gravity_module_size (gravity_vm *vm, gravity_module_t *m) {
93     SET_OBJECT_VISITED_FLAG(m, true);
94 
95     uint32_t hash_size = 0;
96     gravity_hash_iterate2(m->htable, gravity_hash_internalsize, (void*)&hash_size, (void*)vm);
97     uint32_t module_size = (sizeof(gravity_module_t)) + string_size(m->identifier) + hash_size + gravity_hash_memsize(m->htable);
98 
99     SET_OBJECT_VISITED_FLAG(m, false);
100     return module_size;
101 }
102 
gravity_module_blacken(gravity_vm * vm,gravity_module_t * m)103 void gravity_module_blacken (gravity_vm *vm, gravity_module_t *m) {
104     gravity_vm_memupdate(vm, gravity_module_size(vm, m));
105     gravity_hash_iterate(m->htable, gravity_hash_gray, (void*)vm);
106 }
107 
108 // MARK: -
109 
gravity_class_bind(gravity_class_t * c,const char * key,gravity_value_t value)110 void gravity_class_bind (gravity_class_t *c, const char *key, gravity_value_t value) {
111     if (VALUE_ISA_CLASS(value)) {
112         // set has_outer when bind a class inside another class
113         gravity_class_t *obj = VALUE_AS_CLASS(value);
114         obj->has_outer = true;
115     }
116     gravity_hash_insert(c->htable, VALUE_FROM_CSTRING(NULL, key), value);
117 }
118 
gravity_class_getsuper(gravity_class_t * c)119 gravity_class_t *gravity_class_getsuper (gravity_class_t *c) {
120     return c->superclass;
121 }
122 
gravity_class_grow(gravity_class_t * c,uint32_t n)123 bool gravity_class_grow (gravity_class_t *c, uint32_t n) {
124     if (c->ivars) mem_free(c->ivars);
125     if (c->nivars + n >= MAX_IVARS) return false;
126     c->nivars += n;
127     c->ivars = (gravity_value_t *)mem_alloc(NULL, c->nivars * sizeof(gravity_value_t));
128     for (uint32_t i=0; i<c->nivars; ++i) c->ivars[i] = VALUE_FROM_NULL;
129     return true;
130 }
131 
gravity_class_setsuper(gravity_class_t * baseclass,gravity_class_t * superclass)132 bool gravity_class_setsuper (gravity_class_t *baseclass, gravity_class_t *superclass) {
133     if (!superclass) return true;
134     baseclass->superclass = superclass;
135 
136     // check meta class first
137     gravity_class_t *supermeta = (superclass) ? gravity_class_get_meta(superclass) : NULL;
138     uint32_t n1 = (supermeta) ? supermeta->nivars : 0;
139     if (n1) if (!gravity_class_grow (gravity_class_get_meta(baseclass), n1)) return false;
140 
141     // then check real class
142     uint32_t n2 = (superclass) ? superclass->nivars : 0;
143     if (n2) if (!gravity_class_grow (baseclass, n2)) return false;
144 
145     return true;
146 }
147 
gravity_class_setsuper_extern(gravity_class_t * baseclass,const char * identifier)148 bool gravity_class_setsuper_extern (gravity_class_t *baseclass, const char *identifier) {
149     if (identifier) baseclass->superlook = string_dup(identifier);
150     return true;
151 }
152 
gravity_class_new_single(gravity_vm * vm,const char * identifier,uint32_t nivar)153 gravity_class_t *gravity_class_new_single (gravity_vm *vm, const char *identifier, uint32_t nivar) {
154     gravity_class_t *c = (gravity_class_t *)mem_alloc(NULL, sizeof(gravity_class_t));
155     assert(c);
156 
157     c->isa = gravity_class_class;
158     c->identifier = string_dup(identifier);
159     c->superclass = NULL;
160     c->nivars = nivar;
161     c->htable = gravity_hash_create(0, gravity_value_hash, gravity_value_equals, gravity_hash_keyfree, NULL);
162     if (nivar) {
163         c->ivars = (gravity_value_t *)mem_alloc(NULL, nivar * sizeof(gravity_value_t));
164         for (uint32_t i=0; i<nivar; ++i) c->ivars[i] = VALUE_FROM_NULL;
165     }
166 
167     if (vm) gravity_vm_transfer(vm, (gravity_object_t*) c);
168     return c;
169 }
170 
gravity_class_new_pair(gravity_vm * vm,const char * identifier,gravity_class_t * superclass,uint32_t nivar,uint32_t nsvar)171 gravity_class_t *gravity_class_new_pair (gravity_vm *vm, const char *identifier, gravity_class_t *superclass, uint32_t nivar, uint32_t nsvar) {
172     // each class must have a valid identifier
173     if (!identifier) return NULL;
174 
175     char buffer[512];
176     snprintf(buffer, sizeof(buffer), "%s meta", identifier);
177 
178     // ivar count/grow is managed by gravity_class_setsuper
179     gravity_class_t *meta = gravity_class_new_single(vm, buffer, nsvar);
180     meta->objclass = gravity_class_object;
181     gravity_class_setsuper(meta, gravity_class_class);
182 
183     gravity_class_t *c = gravity_class_new_single(vm, identifier, nivar);
184     c->objclass = meta;
185 
186     // a class without a superclass in a subclass of Object
187     gravity_class_setsuper(c, (superclass) ? superclass : gravity_class_object);
188 
189     return c;
190 }
191 
gravity_class_get_meta(gravity_class_t * c)192 gravity_class_t *gravity_class_get_meta (gravity_class_t *c) {
193     // meta classes have objclass set to class object
194     if (c->objclass == gravity_class_object) return c;
195     return c->objclass;
196 }
197 
gravity_class_is_meta(gravity_class_t * c)198 bool gravity_class_is_meta (gravity_class_t *c) {
199     // meta classes have objclass set to class object
200     return (c->objclass == gravity_class_object);
201 }
202 
gravity_class_is_anon(gravity_class_t * c)203 bool gravity_class_is_anon (gravity_class_t *c) {
204     return (string_casencmp(c->identifier, GRAVITY_VM_ANONYMOUS_PREFIX, strlen(GRAVITY_VM_ANONYMOUS_PREFIX)) == 0);
205 }
206 
gravity_class_count_ivars(gravity_class_t * c)207 uint32_t gravity_class_count_ivars (gravity_class_t *c) {
208     return (uint32_t)c->nivars;
209 }
210 
gravity_class_add_ivar(gravity_class_t * c,const char * identifier)211 int16_t gravity_class_add_ivar (gravity_class_t *c, const char *identifier) {
212     #pragma unused(identifier)
213     // TODO: add identifier in array (for easier debugging)
214     ++c->nivars;
215     return c->nivars-1; // its a C array so index is 0 based
216 }
217 
gravity_class_dump(gravity_class_t * c)218 void gravity_class_dump (gravity_class_t *c) {
219     gravity_hash_dump(c->htable);
220 }
221 
gravity_class_setxdata(gravity_class_t * c,void * xdata)222 void gravity_class_setxdata (gravity_class_t *c, void *xdata) {
223     c->xdata = xdata;
224 }
225 
gravity_class_serialize(gravity_class_t * c,json_t * json)226 void gravity_class_serialize (gravity_class_t *c, json_t *json) {
227     const char *label = json_get_label(json, c->identifier);
228     json_begin_object(json, label);
229 
230     json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_CLASS);     // MANDATORY 1st FIELD
231     json_add_cstring(json, GRAVITY_JSON_LABELIDENTIFIER, c->identifier);    // MANDATORY 2nd FIELD
232 
233     // avoid write superclass name if it is the default Object one
234     if ((c->superclass) && (c->superclass->identifier) && (strcmp(c->superclass->identifier, GRAVITY_CLASS_OBJECT_NAME) != 0)) {
235         json_add_cstring(json, GRAVITY_JSON_LABELSUPER, c->superclass->identifier);
236     } else if (c->superlook) {
237         json_add_cstring(json, GRAVITY_JSON_LABELSUPER, c->superlook);
238     }
239 
240     // get c meta class
241     gravity_class_t *meta = gravity_class_get_meta(c);
242 
243     // number of instance (and static) variables
244     json_add_int(json, GRAVITY_JSON_LABELNIVAR, c->nivars);
245     if ((c != meta) && (meta->nivars > 0)) json_add_int(json, GRAVITY_JSON_LABELSIVAR, meta->nivars);
246 
247     // struct flag
248     if (c->is_struct) json_add_bool(json, GRAVITY_JSON_LABELSTRUCT, true);
249 
250     // serialize htable
251     if (c->htable) {
252         gravity_hash_iterate(c->htable, gravity_hash_serialize, (void *)json);
253     }
254 
255     // serialize meta class
256     if (c != meta) {
257         // further proceed only if it has something to be serialized
258         if ((meta->htable) && (gravity_hash_count(meta->htable) > 0)) {
259             json_begin_array(json, GRAVITY_JSON_LABELMETA);
260             gravity_hash_iterate(meta->htable, gravity_hash_serialize, (void *)json);
261             json_end_array(json);
262         }
263     }
264 
265     json_end_object(json);
266 }
267 
gravity_class_deserialize(gravity_vm * vm,json_value * json)268 gravity_class_t *gravity_class_deserialize (gravity_vm *vm, json_value *json) {
269     // sanity check
270     if (json->type != json_object) return NULL;
271     if (json->u.object.length < 3) return NULL;
272 
273     // scan identifier
274     json_value *value = json->u.object.values[1].value;
275     const char *key = json->u.object.values[1].name;
276 
277     // sanity check identifier
278     if (string_casencmp(key, GRAVITY_JSON_LABELIDENTIFIER, strlen(key)) != 0) return NULL;
279     assert(value->type == json_string);
280 
281     // create class and meta
282     gravity_class_t *c = gravity_class_new_pair(vm, value->u.string.ptr, NULL, 0, 0);
283     DEBUG_DESERIALIZE("DESERIALIZE CLASS: %p %s\n", c, value->u.string.ptr);
284 
285     // get its meta class
286     gravity_class_t *meta = gravity_class_get_meta(c);
287 
288     uint32_t n = json->u.object.length;
289     for (uint32_t i=2; i<n; ++i) { // from 2 to skip type and identifier
290 
291         // parse values
292         value = json->u.object.values[i].value;
293         key = json->u.object.values[i].name;
294 
295         if (value->type != json_object) {
296 
297             // super
298             if (string_casencmp(key, GRAVITY_JSON_LABELSUPER, strlen(key)) == 0) {
299                 // the trick here is to re-use a runtime field to store a temporary static data like superclass name
300                 // (only if different than the default Object one)
301                 if (strcmp(value->u.string.ptr, GRAVITY_CLASS_OBJECT_NAME) != 0) {
302                     c->xdata = (void *)string_dup(value->u.string.ptr);
303                 }
304                 continue;
305             }
306 
307             // nivar
308             if (string_casencmp(key, GRAVITY_JSON_LABELNIVAR, strlen(key)) == 0) {
309                 gravity_class_grow(c, (uint32_t)value->u.integer);
310                 continue;
311             }
312 
313             // sivar
314             if (string_casencmp(key, GRAVITY_JSON_LABELSIVAR, strlen(key)) == 0) {
315                 gravity_class_grow(meta, (uint32_t)value->u.integer);
316                 continue;
317             }
318 
319             // struct
320             if (string_casencmp(key, GRAVITY_JSON_LABELSTRUCT, strlen(key)) == 0) {
321                 c->is_struct = true;
322                 continue;
323             }
324 
325             // meta
326             if (string_casencmp(key, GRAVITY_JSON_LABELMETA, strlen(key)) == 0) {
327                 uint32_t m = value->u.array.length;
328                 for (uint32_t j=0; j<m; ++j) {
329                     json_value *r = value->u.array.values[j];
330                     if (r->type != json_object) continue;
331                     gravity_object_t *obj = gravity_object_deserialize(vm, r);
332                     if (!obj) goto abort_load;
333 
334                     const char *identifier = obj->identifier;
335                     if (OBJECT_ISA_FUNCTION(obj)) obj = (gravity_object_t *)gravity_closure_new(vm, (gravity_function_t *)obj);
336                     if (obj) gravity_class_bind(meta, identifier, VALUE_FROM_OBJECT(obj));
337                     else goto abort_load;
338                 }
339                 continue;
340             }
341 
342             // error here
343             goto abort_load;
344         }
345 
346         if (value->type == json_object) {
347             gravity_object_t *obj = gravity_object_deserialize(vm, value);
348             if (!obj) goto abort_load;
349 
350             const char *identifier = obj->identifier;
351             if (OBJECT_ISA_FUNCTION(obj)) obj = (gravity_object_t *)gravity_closure_new(vm, (gravity_function_t *)obj);
352             gravity_class_bind(c, identifier, VALUE_FROM_OBJECT(obj));
353         }
354     }
355 
356     return c;
357 
358 abort_load:
359     // do not free c here because it is already garbage collected
360     return NULL;
361 }
362 
gravity_class_free_internal(gravity_vm * vm,gravity_class_t * c,bool skip_base)363 static void gravity_class_free_internal (gravity_vm *vm, gravity_class_t *c, bool skip_base) {
364     if (skip_base && (gravity_iscore_class(c) || gravity_isopt_class(c))) return;
365 
366     DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)c, true));
367 
368     // check if bridged data needs to be freed too
369     if (c->xdata && vm) {
370         gravity_delegate_t *delegate = gravity_vm_delegate(vm);
371         if (delegate->bridge_free) delegate->bridge_free(vm, (gravity_object_t *)c);
372     }
373 
374     if (c->identifier) mem_free((void *)c->identifier);
375     if (c->superlook) mem_free((void *)c->superlook);
376 
377     if (!skip_base) {
378         // base classes have functions not registered inside VM so manually free all of them
379         gravity_hash_iterate(c->htable, gravity_hash_interalfree, NULL);
380         gravity_hash_iterate(c->htable, gravity_hash_valuefree, NULL);
381     }
382 
383     gravity_hash_free(c->htable);
384     if (c->ivars) mem_free((void *)c->ivars);
385     mem_free((void *)c);
386 }
387 
gravity_class_free_core(gravity_vm * vm,gravity_class_t * c)388 void gravity_class_free_core (gravity_vm *vm, gravity_class_t *c) {
389     gravity_class_free_internal(vm, c, false);
390 }
391 
gravity_class_free(gravity_vm * vm,gravity_class_t * c)392 void gravity_class_free (gravity_vm *vm, gravity_class_t *c) {
393     gravity_class_free_internal(vm, c, true);
394 }
395 
gravity_class_lookup(gravity_class_t * c,gravity_value_t key)396 inline gravity_object_t *gravity_class_lookup (gravity_class_t *c, gravity_value_t key) {
397     while (c) {
398         gravity_value_t *v = gravity_hash_lookup(c->htable, key);
399         if (v) return (gravity_object_t *)v->p;
400         c = c->superclass;
401     }
402     return NULL;
403 }
404 
gravity_class_lookup_class_identifier(gravity_class_t * c,const char * identifier)405 gravity_class_t *gravity_class_lookup_class_identifier (gravity_class_t *c, const char *identifier) {
406     while (c) {
407         if (string_cmp(c->identifier, identifier) == 0) return c;
408         c = c->superclass;
409     }
410     return NULL;
411 }
412 
gravity_class_lookup_closure(gravity_class_t * c,gravity_value_t key)413 inline gravity_closure_t *gravity_class_lookup_closure (gravity_class_t *c, gravity_value_t key) {
414     gravity_object_t *obj = gravity_class_lookup(c, key);
415     if (obj && OBJECT_ISA_CLOSURE(obj)) return (gravity_closure_t *)obj;
416     return NULL;
417 }
418 
gravity_class_lookup_constructor(gravity_class_t * c,uint32_t nparams)419 inline gravity_closure_t *gravity_class_lookup_constructor (gravity_class_t *c, uint32_t nparams) {
420     if (c->xdata) {
421         // bridged class so check for special $initN function
422         if (nparams == 0) {
423             STATICVALUE_FROM_STRING(key, CLASS_INTERNAL_INIT_NAME, strlen(CLASS_INTERNAL_INIT_NAME));
424             return (gravity_closure_t *)gravity_class_lookup(c, key);
425         }
426 
427         // for bridged classed (which can have more than one init constructor like in objc) the convention is
428         // to map each bridged init with a special $initN function (where N>0 is num params)
429         char name[256]; snprintf(name, sizeof(name), "%s%d", CLASS_INTERNAL_INIT_NAME, nparams);
430         STATICVALUE_FROM_STRING(key, name, strlen(name));
431         return (gravity_closure_t *)gravity_class_lookup(c, key);
432     }
433 
434     // for non bridge classes just check for constructor
435     STATICVALUE_FROM_STRING(key, CLASS_CONSTRUCTOR_NAME, strlen(CLASS_CONSTRUCTOR_NAME));
436     return (gravity_closure_t *)gravity_class_lookup(c, key);
437 }
438 
gravity_class_size(gravity_vm * vm,gravity_class_t * c)439 uint32_t gravity_class_size (gravity_vm *vm, gravity_class_t *c) {
440     SET_OBJECT_VISITED_FLAG(c, true);
441 
442     uint32_t class_size = sizeof(gravity_class_t) + (c->nivars * sizeof(gravity_value_t)) + string_size(c->identifier);
443 
444     uint32_t hash_size = 0;
445     gravity_hash_iterate2(c->htable, gravity_hash_internalsize, (void *)&hash_size, (void *)vm);
446     hash_size += gravity_hash_memsize(c->htable);
447 
448     gravity_delegate_t *delegate = gravity_vm_delegate(vm);
449     if (c->xdata && delegate->bridge_size)
450         class_size += delegate->bridge_size(vm, c->xdata);
451 
452     SET_OBJECT_VISITED_FLAG(c, false);
453     return class_size;
454 }
455 
gravity_class_blacken(gravity_vm * vm,gravity_class_t * c)456 void gravity_class_blacken (gravity_vm *vm, gravity_class_t *c) {
457     gravity_vm_memupdate(vm, gravity_class_size(vm, c));
458 
459     // metaclass
460     gravity_gray_object(vm, (gravity_object_t *)c->objclass);
461 
462     // superclass
463     gravity_gray_object(vm, (gravity_object_t *)c->superclass);
464 
465     // internals
466     gravity_hash_iterate(c->htable, gravity_hash_gray, (void *)vm);
467 
468     // ivars
469     for (uint32_t i=0; i<c->nivars; ++i) {
470         gravity_gray_value(vm, c->ivars[i]);
471     }
472 }
473 
474 // MARK: -
475 
gravity_function_new(gravity_vm * vm,const char * identifier,uint16_t nparams,uint16_t nlocals,uint16_t ntemps,void * code)476 gravity_function_t *gravity_function_new (gravity_vm *vm, const char *identifier, uint16_t nparams, uint16_t nlocals, uint16_t ntemps, void *code) {
477     gravity_function_t *f = (gravity_function_t *)mem_alloc(NULL, sizeof(gravity_function_t));
478     assert(f);
479 
480     f->isa = gravity_class_function;
481     f->identifier = (identifier) ? string_dup(identifier) : NULL;
482     f->tag = EXEC_TYPE_NATIVE;
483     f->nparams = nparams;
484     f->nlocals = nlocals;
485     f->ntemps = ntemps;
486     f->nupvalues = 0;
487 
488     // only available in EXEC_TYPE_NATIVE case
489     // code is != NULL when EXEC_TYPE_NATIVE
490     if (code != NULL) {
491         f->useargs = false;
492         f->bytecode = (uint32_t *)code;
493         marray_init(f->cpool);
494         marray_init(f->pvalue);
495         marray_init(f->pname);
496     }
497 
498     if (vm) gravity_vm_transfer(vm, (gravity_object_t*)f);
499     return f;
500 }
501 
gravity_function_new_internal(gravity_vm * vm,const char * identifier,gravity_c_internal exec,uint16_t nparams)502 gravity_function_t *gravity_function_new_internal (gravity_vm *vm, const char *identifier, gravity_c_internal exec, uint16_t nparams) {
503     gravity_function_t *f = gravity_function_new(vm, identifier, nparams, 0, 0, NULL);
504     f->tag = EXEC_TYPE_INTERNAL;
505     f->internal = exec;
506     return f;
507 }
508 
gravity_function_new_special(gravity_vm * vm,const char * identifier,uint16_t index,void * getter,void * setter)509 gravity_function_t *gravity_function_new_special (gravity_vm *vm, const char *identifier, uint16_t index, void *getter, void *setter) {
510     gravity_function_t *f = gravity_function_new(vm, identifier, 0, 0, 0, NULL);
511     f->tag = EXEC_TYPE_SPECIAL;
512     f->index = index;
513     f->special[0] = getter;
514     f->special[1] = setter;
515     return f;
516 }
517 
gravity_function_new_bridged(gravity_vm * vm,const char * identifier,void * xdata)518 gravity_function_t *gravity_function_new_bridged (gravity_vm *vm, const char *identifier, void *xdata) {
519     gravity_function_t *f = gravity_function_new(vm, identifier, 0, 0, 0, NULL);
520     f->tag = EXEC_TYPE_BRIDGED;
521     f->xdata = xdata;
522     return f;
523 }
524 
gravity_function_cpool_add(gravity_vm * vm,gravity_function_t * f,gravity_value_t v)525 uint16_t gravity_function_cpool_add (gravity_vm *vm, gravity_function_t *f, gravity_value_t v) {
526     assert(f->tag == EXEC_TYPE_NATIVE);
527 
528     size_t n = marray_size(f->cpool);
529     for (size_t i=0; i<n; i++) {
530         gravity_value_t v2 = marray_get(f->cpool, i);
531         if (gravity_value_equals(v,v2)) {
532             gravity_value_free(NULL, v);
533             return (uint16_t)i;
534         }
535     }
536 
537     // vm is required here because I cannot know in advance if v is already in the pool or not
538     // and value object v must be added to the VM only once
539     if ((vm) && (gravity_value_isobject(v))) gravity_vm_transfer(vm, VALUE_AS_OBJECT(v));
540 
541     marray_push(gravity_value_t, f->cpool, v);
542     return (uint16_t)marray_size(f->cpool)-1;
543 }
544 
gravity_function_cpool_get(gravity_function_t * f,uint16_t i)545 gravity_value_t gravity_function_cpool_get (gravity_function_t *f, uint16_t i) {
546     assert(f->tag == EXEC_TYPE_NATIVE);
547     return marray_get(f->cpool, i);
548 }
549 
gravity_function_params_get(gravity_vm * vm,gravity_function_t * f)550 gravity_list_t *gravity_function_params_get (gravity_vm *vm, gravity_function_t *f) {
551     #pragma unused(vm)
552     gravity_list_t *list = NULL;
553 
554     if (f->tag == EXEC_TYPE_NATIVE) {
555         // written by user in Gravity
556     } else if (f->tag == EXEC_TYPE_BRIDGED && f->xdata) {
557         // ask bridge
558     } else if (f->tag == EXEC_TYPE_INTERNAL) {
559         // native C function
560     }
561 
562     return list;
563 }
564 
gravity_function_setxdata(gravity_function_t * f,void * xdata)565 void gravity_function_setxdata (gravity_function_t *f, void *xdata) {
566     f->xdata = xdata;
567 }
568 
gravity_function_array_serialize(gravity_function_t * f,json_t * json,gravity_value_r r)569 static void gravity_function_array_serialize (gravity_function_t *f, json_t *json, gravity_value_r r) {
570     assert(f->tag == EXEC_TYPE_NATIVE);
571     size_t n = marray_size(r);
572 
573     for (size_t i=0; i<n; i++) {
574         gravity_value_t v = marray_get(r, i);
575         gravity_value_serialize(NULL, v, json);
576     }
577 }
578 
gravity_function_array_dump(gravity_function_t * f,gravity_value_r r)579 static void gravity_function_array_dump (gravity_function_t *f, gravity_value_r r) {
580     assert(f->tag == EXEC_TYPE_NATIVE);
581     size_t n = marray_size(r);
582 
583     for (size_t i=0; i<n; i++) {
584         gravity_value_t v = marray_get(r, i);
585 
586         if (VALUE_ISA_NULL(v)) {
587             printf("%05zu\tNULL\n", i);
588             continue;
589         }
590 
591         if (VALUE_ISA_UNDEFINED(v)) {
592             printf("%05zu\tUNDEFINED\n", i);
593             continue;
594         }
595 
596         if (VALUE_ISA_BOOL(v)) {
597             printf("%05zu\tBOOL: %d\n", i, (v.n == 0) ? 0 : 1);
598             continue;
599         }
600 
601         if (VALUE_ISA_INT(v)) {
602             printf("%05zu\tINT: %" PRId64 "\n", i, (int64_t)v.n);
603             continue;
604         }
605 
606         if (VALUE_ISA_FLOAT(v)) {
607             printf("%05zu\tFLOAT: %g\n", i, (double)v.f);
608             continue;
609         }
610 
611         if (VALUE_ISA_FUNCTION(v)) {
612             gravity_function_t *vf = VALUE_AS_FUNCTION(v);
613             printf("%05zu\tFUNC: %s\n", i, (vf->identifier) ? vf->identifier : "$anon");
614             continue;
615         }
616 
617         if (VALUE_ISA_CLASS(v)) {
618             gravity_class_t *c = VALUE_AS_CLASS(v);
619             printf("%05zu\tCLASS: %s\n", i, (c->identifier) ? c->identifier: "$anon");
620             continue;
621 
622         }
623 
624         if (VALUE_ISA_STRING(v)) {
625             printf("%05zu\tSTRING: %s\n", i, VALUE_AS_CSTRING(v));
626             continue;
627         }
628 
629         if (VALUE_ISA_LIST(v)) {
630             gravity_list_t *value = VALUE_AS_LIST(v);
631             size_t count = marray_size(value->array);
632             printf("%05zu\tLIST: %zu items\n", i, count);
633             continue;
634 
635         }
636 
637         if (VALUE_ISA_MAP(v)) {
638             gravity_map_t *map = VALUE_AS_MAP(v);
639             printf("%05zu\tMAP: %u items\n", i, gravity_hash_count(map->hash));
640             continue;
641         }
642 
643         // should never reach this point
644         assert(0);
645     }
646 }
647 
gravity_function_bytecode_serialize(gravity_function_t * f,json_t * json)648 static void gravity_function_bytecode_serialize (gravity_function_t *f, json_t *json) {
649     if (!f->bytecode || !f->ninsts) {
650         json_add_null(json, GRAVITY_JSON_LABELBYTECODE);
651         return;
652     }
653 
654     // bytecode
655     uint32_t ninst = f->ninsts;
656     uint32_t length = ninst * 2 * sizeof(uint32_t);
657     uint8_t *hexchar = (uint8_t*) mem_alloc(NULL, sizeof(uint8_t) * length);
658 
659     for (uint32_t k=0, i=0; i < ninst; ++i) {
660         uint32_t value = f->bytecode[i];
661 
662         for (int32_t j=2*sizeof(value)-1; j>=0; --j) {
663             uint8_t c = "0123456789ABCDEF"[((value >> (j*4)) & 0xF)];
664             hexchar[k++] = c;
665         }
666     }
667 
668     json_add_string(json, GRAVITY_JSON_LABELBYTECODE, (const char *)hexchar, length);
669     mem_free(hexchar);
670 
671     // debug lineno
672     if (!f->lineno) return;
673 
674     ninst = f->ninsts;
675     length = ninst * 2 * sizeof(uint32_t);
676     hexchar = (uint8_t*) mem_alloc(NULL, sizeof(uint8_t) * length);
677 
678     for (uint32_t k=0, i=0; i < ninst; ++i) {
679         uint32_t value = f->lineno[i];
680 
681         for (int32_t j=2*sizeof(value)-1; j>=0; --j) {
682             uint8_t c = "0123456789ABCDEF"[((value >> (j*4)) & 0xF)];
683             hexchar[k++] = c;
684         }
685     }
686 
687     json_add_string(json, GRAVITY_JSON_LABELLINENO, (const char *)hexchar, length);
688     mem_free(hexchar);
689 }
690 
gravity_bytecode_deserialize(const char * buffer,size_t len,uint32_t * n)691 uint32_t *gravity_bytecode_deserialize (const char *buffer, size_t len, uint32_t *n) {
692     uint32_t ninst = (uint32_t)len / 8;
693     uint32_t *bytecode = (uint32_t *)mem_alloc(NULL, sizeof(uint32_t) * (ninst + 1));    // +1 to get a 0 terminated bytecode (0 is opcode RET0)
694 
695     for (uint32_t j=0; j<ninst; ++j) {
696         register uint32_t v = 0;
697 
698         for (uint32_t i=(j*8); i<=(j*8)+7; ++i) {
699             // I was using a conversion code from
700             // https://code.google.com/p/yara-project/source/browse/trunk/libyara/xtoi.c?r=150
701             // but it caused issues under ARM processor so I decided to switch to an easier to read/maintain code
702             // http://codereview.stackexchange.com/questions/42976/hexadecimal-to-integer-conversion-function
703 
704             // no needs to have also the case:
705             // if (c >= 'a' && c <= 'f') {
706             //        c = c - 'a' + 10;
707             // }
708             // because bytecode is always uppercase
709             register uint32_t c = buffer[i];
710 
711             if (c >= 'A' && c <= 'F') {
712                 c = c - 'A' + 10;
713             } else if (c >= '0' && c <= '9') {
714                 c -= '0';
715             } else goto abort_conversion;
716 
717             v = v << 4 | c;
718 
719         }
720 
721         bytecode[j] = v;
722     }
723 
724     *n = ninst;
725     return bytecode;
726 
727 abort_conversion:
728     *n = 0;
729     if (bytecode) mem_free(bytecode);
730     return NULL;
731 }
732 
gravity_function_dump(gravity_function_t * f,code_dump_function codef)733 void gravity_function_dump (gravity_function_t *f, code_dump_function codef) {
734     printf("Function: %s\n", (f->identifier) ? f->identifier : "$anon");
735     printf("Params:%d Locals:%d Temp:%d Upvalues:%d Tag:%d xdata:%p\n", f->nparams, f->nlocals, f->ntemps, f->nupvalues, f->tag, f->xdata);
736 
737     if (f->tag == EXEC_TYPE_NATIVE) {
738         if (marray_size(f->cpool)) printf("======= CONST POOL =======\n");
739         gravity_function_array_dump(f, f->cpool);
740 
741         if (marray_size(f->pname)) printf("======= PARAM NAMES =======\n");
742         gravity_function_array_dump(f, f->pname);
743 
744         if (marray_size(f->pvalue)) printf("======= PARAM VALUES =======\n");
745         gravity_function_array_dump(f, f->pvalue);
746 
747         printf("======= BYTECODE =======\n");
748         if ((f->bytecode) && (codef)) codef(f->bytecode);
749     }
750 
751     printf("\n");
752 }
753 
gravity_function_special_serialize(gravity_function_t * f,const char * key,json_t * json)754 void gravity_function_special_serialize (gravity_function_t *f, const char *key, json_t *json) {
755     const char *label = json_get_label(json, key);
756     json_begin_object(json, label);
757 
758     json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_FUNCTION);    // MANDATORY 1st FIELD
759     json_add_cstring(json, GRAVITY_JSON_LABELIDENTIFIER, key);                // MANDATORY 2nd FIELD
760     json_add_int(json, GRAVITY_JSON_LABELTAG, f->tag);
761 
762     // common fields
763     json_add_int(json, GRAVITY_JSON_LABELNPARAM, f->nparams);
764     json_add_bool(json, GRAVITY_JSON_LABELARGS, f->useargs);
765     json_add_int(json, GRAVITY_JSON_LABELINDEX, f->index);
766 
767     if (f->special[0]) {
768         gravity_function_t *f2 = (gravity_function_t*)f->special[0];
769         f2->identifier = GRAVITY_JSON_GETTER;
770         gravity_function_serialize(f2, json);
771         f2->identifier = NULL;
772     }
773     if (f->special[1]) {
774         gravity_function_t *f2 = (gravity_function_t*)f->special[1];
775         f2->identifier = GRAVITY_JSON_SETTER;
776         gravity_function_serialize(f2, json);
777         f2->identifier = NULL;
778     }
779 
780     json_end_object(json);
781 }
782 
gravity_function_serialize(gravity_function_t * f,json_t * json)783 void gravity_function_serialize (gravity_function_t *f, json_t *json) {
784     // special functions need a special serialization
785     if (f->tag == EXEC_TYPE_SPECIAL) {
786         gravity_function_special_serialize(f, f->identifier, json);
787         return;
788     }
789 
790     // compute identifier (cannot be NULL)
791     const char *identifier = f->identifier;
792     char temp[256];
793     if (!identifier) {snprintf(temp, sizeof(temp), "$anon_%p", f); identifier = temp;}
794 
795     const char *label = json_get_label(json, identifier);
796     json_begin_object(json, label);
797 
798     json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_FUNCTION);  // MANDATORY 1st FIELD
799     json_add_cstring(json, GRAVITY_JSON_LABELIDENTIFIER, identifier);       // MANDATORY 2nd FIELD (not for getter/setter)
800     json_add_int(json, GRAVITY_JSON_LABELTAG, f->tag);
801 
802     // common fields
803     json_add_int(json, GRAVITY_JSON_LABELNPARAM, f->nparams);
804     json_add_bool(json, GRAVITY_JSON_LABELARGS, f->useargs);
805 
806     if (f->tag == EXEC_TYPE_NATIVE) {
807         // native only fields
808         json_add_int(json, GRAVITY_JSON_LABELNLOCAL, f->nlocals);
809         json_add_int(json, GRAVITY_JSON_LABELNTEMP, f->ntemps);
810         json_add_int(json, GRAVITY_JSON_LABELNUPV, f->nupvalues);
811         json_add_double(json, GRAVITY_JSON_LABELPURITY, f->purity);
812 
813         // bytecode
814         gravity_function_bytecode_serialize(f, json);
815 
816         // constant pool
817         json_begin_array(json, GRAVITY_JSON_LABELPOOL);
818         gravity_function_array_serialize(f, json, f->cpool);
819         json_end_array(json);
820 
821         // default values (if any)
822         if (marray_size(f->pvalue)) {
823             json_begin_array(json, GRAVITY_JSON_LABELPVALUES);
824             gravity_function_array_serialize(f, json, f->pvalue);
825         json_end_array(json);
826     }
827 
828         // arg names (if any)
829         if (marray_size(f->pname)) {
830             json_begin_array(json, GRAVITY_JSON_LABELPNAMES);
831             gravity_function_array_serialize(f, json, f->pname);
832             json_end_array(json);
833         }
834     }
835 
836     json_end_object(json);
837 }
838 
gravity_function_deserialize(gravity_vm * vm,json_value * json)839 gravity_function_t *gravity_function_deserialize (gravity_vm *vm, json_value *json) {
840     gravity_function_t *f = gravity_function_new(vm, NULL, 0, 0, 0, NULL);
841 
842     DEBUG_DESERIALIZE("DESERIALIZE FUNCTION: %p\n", f);
843 
844     bool identifier_parsed = false;
845     bool getter_parsed = false;
846     bool setter_parsed = false;
847     bool index_parsed = false;
848     bool bytecode_parsed = false;
849     bool cpool_parsed = false;
850     bool nparams_parsed = false;
851     bool nlocals_parsed = false;
852     bool ntemp_parsed = false;
853     bool nupvalues_parsed = false;
854     bool nargs_parsed = false;
855     bool tag_parsed = false;
856 
857     uint32_t n = json->u.object.length;
858     for (uint32_t i=1; i<n; ++i) { // from 1 to skip type
859         const char *label = json->u.object.values[i].name;
860         json_value *value = json->u.object.values[i].value;
861         size_t label_size = strlen(label);
862 
863         // identifier
864         if (string_casencmp(label, GRAVITY_JSON_LABELIDENTIFIER, label_size) == 0) {
865             if (value->type != json_string) goto abort_load;
866             if (identifier_parsed) goto abort_load;
867             if (strncmp(value->u.string.ptr, "$anon", 5) != 0) {
868                 f->identifier = string_dup(value->u.string.ptr);
869                 DEBUG_DESERIALIZE("IDENTIFIER: %s\n", value->u.string.ptr);
870             }
871             identifier_parsed = true;
872             continue;
873         }
874 
875         // tag
876         if (string_casencmp(label, GRAVITY_JSON_LABELTAG, label_size) == 0) {
877             if (value->type != json_integer) goto abort_load;
878             if (tag_parsed) goto abort_load;
879             f->tag = (uint16_t)value->u.integer;
880             tag_parsed = true;
881             continue;
882         }
883 
884         // index (only in special functions)
885         if (string_casencmp(label, GRAVITY_JSON_LABELINDEX, label_size) == 0) {
886             if (value->type != json_integer) goto abort_load;
887             if (f->tag != EXEC_TYPE_SPECIAL) goto abort_load;
888             if (index_parsed) goto abort_load;
889             f->index = (uint16_t)value->u.integer;
890             index_parsed = true;
891             continue;
892         }
893 
894         // getter (only in special functions)
895         if (string_casencmp(label, GRAVITY_JSON_GETTER, strlen(GRAVITY_JSON_GETTER)) == 0) {
896             if (f->tag != EXEC_TYPE_SPECIAL) goto abort_load;
897             if (getter_parsed) goto abort_load;
898             gravity_function_t *getter = gravity_function_deserialize(vm, value);
899             if (!getter) goto abort_load;
900             f->special[0] = gravity_closure_new(vm, getter);
901             getter_parsed = true;
902             continue;
903         }
904 
905         // setter (only in special functions)
906         if (string_casencmp(label, GRAVITY_JSON_SETTER, strlen(GRAVITY_JSON_SETTER)) == 0) {
907             if (f->tag != EXEC_TYPE_SPECIAL) goto abort_load;
908             if (setter_parsed) goto abort_load;
909             gravity_function_t *setter = gravity_function_deserialize(vm, value);
910             if (!setter) goto abort_load;
911             f->special[1] = gravity_closure_new(vm, setter);
912             setter_parsed = true;
913             continue;
914         }
915 
916         // nparams
917         if (string_casencmp(label, GRAVITY_JSON_LABELNPARAM, label_size) == 0) {
918             if (value->type != json_integer) goto abort_load;
919             if (nparams_parsed) goto abort_load;
920             f->nparams = (uint16_t)value->u.integer;
921             nparams_parsed = true;
922             continue;
923         }
924 
925         // nlocals
926         if (string_casencmp(label, GRAVITY_JSON_LABELNLOCAL, label_size) == 0) {
927             if (value->type != json_integer) goto abort_load;
928             if (nlocals_parsed) goto abort_load;
929             f->nlocals = (uint16_t)value->u.integer;
930             nlocals_parsed = true;
931             continue;
932         }
933 
934         // ntemps
935         if (string_casencmp(label, GRAVITY_JSON_LABELNTEMP, label_size) == 0) {
936             if (value->type != json_integer) goto abort_load;
937             if (ntemp_parsed) goto abort_load;
938             f->ntemps = (uint16_t)value->u.integer;
939             ntemp_parsed = true;
940             continue;
941         }
942 
943         // nupvalues
944         if (string_casencmp(label, GRAVITY_JSON_LABELNUPV, label_size) == 0) {
945             if (value->type != json_integer) goto abort_load;
946             if (nupvalues_parsed) goto abort_load;
947             f->nupvalues = (uint16_t)value->u.integer;
948             nupvalues_parsed = true;
949             continue;
950         }
951 
952         // args
953         if (string_casencmp(label, GRAVITY_JSON_LABELARGS, label_size) == 0) {
954             if (value->type != json_boolean) goto abort_load;
955             if (nargs_parsed) goto abort_load;
956             f->useargs = (bool)value->u.boolean;
957             nargs_parsed = true;
958             continue;
959         }
960 
961         // bytecode
962         if (string_casencmp(label, GRAVITY_JSON_LABELBYTECODE, label_size) == 0) {
963             if (bytecode_parsed) goto abort_load;
964             if (value->type == json_null) {
965                 // if function is empty then just one RET0 implicit bytecode instruction
966                 f->ninsts = 0;
967                 f->bytecode = (uint32_t *)mem_alloc(NULL, sizeof(uint32_t) * (f->ninsts + 1));
968             } else {
969                 if (value->type != json_string) goto abort_load;
970                 if (f->tag != EXEC_TYPE_NATIVE) goto abort_load;
971                 f->bytecode = gravity_bytecode_deserialize(value->u.string.ptr, value->u.string.length, &f->ninsts);
972             }
973             bytecode_parsed = true;
974             continue;
975         }
976 
977         // lineno debug info
978         if (string_casencmp(label, GRAVITY_JSON_LABELLINENO, label_size) == 0) {
979             if (value->type == json_string) f->lineno = gravity_bytecode_deserialize(value->u.string.ptr, value->u.string.length, &f->ninsts);
980         }
981 
982         // arguments names
983         if (string_casencmp(label, GRAVITY_JSON_LABELPNAMES, label_size) == 0) {
984             if (value->type != json_array) goto abort_load;
985             if (f->tag != EXEC_TYPE_NATIVE) goto abort_load;
986             uint32_t m = value->u.array.length;
987             for (uint32_t j=0; j<m; ++j) {
988                 json_value *r = value->u.array.values[j];
989                 if (r->type != json_string) goto abort_load;
990                 marray_push(gravity_value_t, f->pname, VALUE_FROM_STRING(NULL, r->u.string.ptr, r->u.string.length));
991             }
992         }
993 
994         // arguments default values
995         if (string_casencmp(label, GRAVITY_JSON_LABELPVALUES, label_size) == 0) {
996             if (value->type != json_array) goto abort_load;
997             if (f->tag != EXEC_TYPE_NATIVE) goto abort_load;
998 
999             uint32_t m = value->u.array.length;
1000             for (uint32_t j=0; j<m; ++j) {
1001                 json_value *r = value->u.array.values[j];
1002                 switch (r->type) {
1003                     case json_integer:
1004                         marray_push(gravity_value_t, f->pvalue, VALUE_FROM_INT((gravity_int_t)r->u.integer));
1005                         break;
1006 
1007                     case json_double:
1008                         marray_push(gravity_value_t, f->pvalue, VALUE_FROM_FLOAT((gravity_float_t)r->u.dbl));
1009                         break;
1010 
1011                     case json_boolean:
1012                         marray_push(gravity_value_t, f->pvalue, VALUE_FROM_BOOL(r->u.boolean));
1013                         break;
1014 
1015                     case json_string:
1016                         marray_push(gravity_value_t, f->pvalue, VALUE_FROM_STRING(NULL, r->u.string.ptr, r->u.string.length));
1017                         break;
1018 
1019                     case json_object:
1020                         marray_push(gravity_value_t, f->pvalue, VALUE_FROM_UNDEFINED);
1021                         break;
1022 
1023                     case json_null:
1024                         marray_push(gravity_value_t, f->pvalue, VALUE_FROM_NULL);
1025                         break;
1026 
1027                     case json_none:
1028                     case json_array:
1029                         marray_push(gravity_value_t, f->pvalue, VALUE_FROM_NULL);
1030                         break;
1031                 }
1032             }
1033         }
1034 
1035         // cpool
1036         if (string_casencmp(label, GRAVITY_JSON_LABELPOOL, label_size) == 0) {
1037             if (value->type != json_array) goto abort_load;
1038             if (f->tag != EXEC_TYPE_NATIVE) goto abort_load;
1039             if (cpool_parsed) goto abort_load;
1040             cpool_parsed = true;
1041 
1042             uint32_t m = value->u.array.length;
1043             for (uint32_t j=0; j<m; ++j) {
1044                 json_value *r = value->u.array.values[j];
1045                 switch (r->type) {
1046                     case json_integer:
1047                         gravity_function_cpool_add(NULL, f, VALUE_FROM_INT((gravity_int_t)r->u.integer));
1048                         break;
1049 
1050                     case json_double:
1051                         gravity_function_cpool_add(NULL, f, VALUE_FROM_FLOAT((gravity_float_t)r->u.dbl));
1052                         break;
1053 
1054                     case json_boolean:
1055                         gravity_function_cpool_add(NULL, f, VALUE_FROM_BOOL(r->u.boolean));
1056                         break;
1057 
1058                     case json_string:
1059                         gravity_function_cpool_add(vm, f, VALUE_FROM_STRING(NULL, r->u.string.ptr, r->u.string.length));
1060                         break;
1061 
1062                     case json_object: {
1063                         gravity_object_t *obj = gravity_object_deserialize(vm, r);
1064                         if (!obj) goto abort_load;
1065                         gravity_function_cpool_add(NULL, f, VALUE_FROM_OBJECT(obj));
1066                         break;
1067                     }
1068 
1069                     case json_array: {
1070                         uint32_t count = r->u.array.length;
1071                         gravity_list_t *list = gravity_list_new (NULL, count);
1072                         if (!list) continue;
1073 
1074                         for (uint32_t k=0; k<count; ++k) {
1075                             json_value *jsonv = r->u.array.values[k];
1076                             gravity_value_t v;
1077 
1078                             // only literals allowed here
1079                             switch (jsonv->type) {
1080                                 case json_integer: v = VALUE_FROM_INT((gravity_int_t)jsonv->u.integer); break;
1081                                 case json_double: v = VALUE_FROM_FLOAT((gravity_float_t)jsonv->u.dbl); break;
1082                                 case json_boolean: v = VALUE_FROM_BOOL(jsonv->u.boolean); break;
1083                                 case json_string: v = VALUE_FROM_STRING(vm, jsonv->u.string.ptr, jsonv->u.string.length); break;
1084                                 default: goto abort_load;
1085                             }
1086 
1087                             marray_push(gravity_value_t, list->array, v);
1088                         }
1089                         gravity_function_cpool_add(vm, f, VALUE_FROM_OBJECT(list));
1090                         break;
1091                     }
1092 
1093                     case json_none:
1094                     case json_null:
1095                         gravity_function_cpool_add(NULL, f, VALUE_FROM_NULL);
1096                         break;
1097                 }
1098             }
1099         }
1100     }
1101 
1102     return f;
1103 
1104 abort_load:
1105     // do not free f here because it is already garbage collected
1106     return NULL;
1107 }
1108 
gravity_function_free(gravity_vm * vm,gravity_function_t * f)1109 void gravity_function_free (gravity_vm *vm, gravity_function_t *f) {
1110     if (!f) return;
1111 
1112     DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)f, true));
1113 
1114     // check if bridged data needs to be freed too
1115     if (f->xdata && vm) {
1116         gravity_delegate_t *delegate = gravity_vm_delegate(vm);
1117         if (delegate->bridge_free) delegate->bridge_free(vm, (gravity_object_t *)f);
1118     }
1119 
1120     if (f->identifier) mem_free((void *)f->identifier);
1121     if (f->tag == EXEC_TYPE_NATIVE) {
1122         if (f->bytecode) mem_free((void *)f->bytecode);
1123         if (f->lineno) mem_free((void *)f->lineno);
1124 
1125         // FREE EACH DEFAULT value
1126         size_t n = marray_size(f->pvalue);
1127         for (size_t i=0; i<n; i++) {
1128             gravity_value_t v = marray_get(f->pvalue, i);
1129             gravity_value_free(NULL, v);
1130         }
1131         marray_destroy(f->pvalue);
1132 
1133         // FREE EACH PARAM name
1134         n = marray_size(f->pname);
1135         for (size_t i=0; i<n; i++) {
1136             gravity_value_t v = marray_get(f->pname, i);
1137             gravity_value_free(NULL, v);
1138         }
1139         marray_destroy(f->pname);
1140 
1141         // DO NOT FREE EACH INDIVIDUAL CPOOL ITEM HERE
1142         marray_destroy(f->cpool);
1143     }
1144     mem_free((void *)f);
1145 }
1146 
gravity_function_size(gravity_vm * vm,gravity_function_t * f)1147 uint32_t gravity_function_size (gravity_vm *vm, gravity_function_t *f) {
1148     SET_OBJECT_VISITED_FLAG(f, true);
1149 
1150     uint32_t func_size = sizeof(gravity_function_t) + string_size(f->identifier);
1151 
1152     if (f->tag == EXEC_TYPE_NATIVE) {
1153         if (f->bytecode) func_size += f->ninsts * sizeof(uint32_t);
1154         // cpool size
1155         size_t n = marray_size(f->cpool);
1156         for (size_t i=0; i<n; i++) {
1157             gravity_value_t v = marray_get(f->cpool, i);
1158             func_size += gravity_value_size(vm, v);
1159         }
1160     } else if (f->tag == EXEC_TYPE_SPECIAL) {
1161         if (f->special[0]) func_size += gravity_closure_size(vm, (gravity_closure_t *)f->special[0]);
1162         if ((f->special[1]) && (f->special[0] != f->special[1])) func_size += gravity_closure_size(vm, (gravity_closure_t *)f->special[1]);
1163     } else if (f->tag == EXEC_TYPE_BRIDGED) {
1164         gravity_delegate_t *delegate = gravity_vm_delegate(vm);
1165         if (f->xdata && delegate->bridge_size)
1166             func_size += delegate->bridge_size(vm, f->xdata);
1167     }
1168 
1169     SET_OBJECT_VISITED_FLAG(f, false);
1170     return func_size;
1171 }
1172 
gravity_function_blacken(gravity_vm * vm,gravity_function_t * f)1173 void gravity_function_blacken (gravity_vm *vm, gravity_function_t *f) {
1174     gravity_vm_memupdate(vm, gravity_function_size(vm, f));
1175 
1176     if (f->tag == EXEC_TYPE_SPECIAL) {
1177         if (f->special[0]) gravity_gray_object(vm, (gravity_object_t *)f->special[0]);
1178         if (f->special[1]) gravity_gray_object(vm, (gravity_object_t *)f->special[1]);
1179     }
1180 
1181     if (f->tag == EXEC_TYPE_NATIVE) {
1182         // constant pool
1183         size_t n = marray_size(f->cpool);
1184         for (size_t i=0; i<n; i++) {
1185             gravity_value_t v = marray_get(f->cpool, i);
1186             gravity_gray_value(vm, v);
1187         }
1188     }
1189 }
1190 
1191 // MARK: -
1192 
gravity_closure_new(gravity_vm * vm,gravity_function_t * f)1193 gravity_closure_t *gravity_closure_new (gravity_vm *vm, gravity_function_t *f) {
1194     gravity_closure_t *closure = (gravity_closure_t *)mem_alloc(NULL, sizeof(gravity_closure_t));
1195     assert(closure);
1196 
1197     closure->isa = gravity_class_closure;
1198     closure->vm = vm;
1199     closure->f = f;
1200 
1201     // allocate upvalue array (+1 so I can simplify the iterator without the needs to access closure->f->nupvalues)
1202     uint16_t nupvalues = (f) ? f->nupvalues : 0;
1203     closure->upvalue = (nupvalues) ? (gravity_upvalue_t **)mem_alloc(NULL, sizeof(gravity_upvalue_t*) * (f->nupvalues + 1)) : NULL;
1204 
1205     if (vm) gravity_vm_transfer(vm, (gravity_object_t*)closure);
1206     return closure;
1207 }
1208 
gravity_closure_free(gravity_vm * vm,gravity_closure_t * closure)1209 void gravity_closure_free (gravity_vm *vm, gravity_closure_t *closure) {
1210     #pragma unused(vm)
1211     if (closure->refcount > 0) return;
1212 
1213     DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)closure, true));
1214     if (closure->upvalue) mem_free(closure->upvalue);
1215     mem_free(closure);
1216 }
1217 
gravity_closure_size(gravity_vm * vm,gravity_closure_t * closure)1218 uint32_t gravity_closure_size (gravity_vm *vm, gravity_closure_t *closure) {
1219     #pragma unused(vm)
1220     SET_OBJECT_VISITED_FLAG(closure, true);
1221 
1222     uint32_t closure_size = sizeof(gravity_closure_t);
1223     gravity_upvalue_t **upvalue = closure->upvalue;
1224     while (upvalue && upvalue[0]) {
1225         closure_size += sizeof(gravity_upvalue_t*);
1226         ++upvalue;
1227     }
1228 
1229     SET_OBJECT_VISITED_FLAG(closure, false);
1230     return closure_size;
1231 }
1232 
gravity_closure_inc_refcount(gravity_vm * vm,gravity_closure_t * closure)1233 void gravity_closure_inc_refcount (gravity_vm *vm, gravity_closure_t *closure) {
1234     if (closure->refcount == 0) gravity_gc_temppush(vm, (gravity_object_t *)closure);
1235     ++closure->refcount;
1236 }
1237 
gravity_closure_dec_refcount(gravity_vm * vm,gravity_closure_t * closure)1238 void gravity_closure_dec_refcount (gravity_vm *vm, gravity_closure_t *closure) {
1239     if (closure->refcount == 1) gravity_gc_tempnull(vm, (gravity_object_t *)closure);
1240     if (closure->refcount >= 1) --closure->refcount;
1241 }
1242 
gravity_closure_blacken(gravity_vm * vm,gravity_closure_t * closure)1243 void gravity_closure_blacken (gravity_vm *vm, gravity_closure_t *closure) {
1244     gravity_vm_memupdate(vm, gravity_closure_size(vm, closure));
1245 
1246     // mark function
1247     gravity_gray_object(vm, (gravity_object_t*)closure->f);
1248 
1249     // mark each upvalue
1250     gravity_upvalue_t **upvalue = closure->upvalue;
1251     while (upvalue && upvalue[0]) {
1252         gravity_gray_object(vm, (gravity_object_t*)upvalue[0]);
1253         ++upvalue;
1254     }
1255 
1256     // mark context (if any)
1257     if (closure->context) gravity_gray_object(vm, closure->context);
1258 }
1259 
1260 // MARK: -
1261 
gravity_upvalue_new(gravity_vm * vm,gravity_value_t * value)1262 gravity_upvalue_t *gravity_upvalue_new (gravity_vm *vm, gravity_value_t *value) {
1263     gravity_upvalue_t *upvalue = (gravity_upvalue_t *)mem_alloc(NULL, sizeof(gravity_upvalue_t));
1264 
1265     upvalue->isa = gravity_class_upvalue;
1266     upvalue->value = value;
1267     upvalue->closed = VALUE_FROM_NULL;
1268     upvalue->next = NULL;
1269 
1270     if (vm) gravity_vm_transfer(vm, (gravity_object_t*) upvalue);
1271     return upvalue;
1272 }
1273 
gravity_upvalue_free(gravity_vm * vm,gravity_upvalue_t * upvalue)1274 void gravity_upvalue_free(gravity_vm *vm, gravity_upvalue_t *upvalue) {
1275     #pragma unused(vm)
1276 
1277     DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)upvalue, true));
1278     mem_free(upvalue);
1279 }
1280 
gravity_upvalue_size(gravity_vm * vm,gravity_upvalue_t * upvalue)1281 uint32_t gravity_upvalue_size (gravity_vm *vm, gravity_upvalue_t *upvalue) {
1282     #pragma unused(vm, upvalue)
1283 
1284     SET_OBJECT_VISITED_FLAG(upvalue, true);
1285     uint32_t upvalue_size = sizeof(gravity_upvalue_t);
1286     SET_OBJECT_VISITED_FLAG(upvalue, false);
1287 
1288     return upvalue_size;
1289 }
1290 
gravity_upvalue_blacken(gravity_vm * vm,gravity_upvalue_t * upvalue)1291 void gravity_upvalue_blacken (gravity_vm *vm, gravity_upvalue_t *upvalue) {
1292     gravity_vm_memupdate(vm, gravity_upvalue_size(vm, upvalue));
1293     // gray both closed and still opened values
1294     gravity_gray_value(vm, *upvalue->value);
1295     gravity_gray_value(vm, upvalue->closed);
1296 }
1297 
1298 // MARK: -
1299 
gravity_fiber_new(gravity_vm * vm,gravity_closure_t * closure,uint32_t nstack,uint32_t nframes)1300 gravity_fiber_t *gravity_fiber_new (gravity_vm *vm, gravity_closure_t *closure, uint32_t nstack, uint32_t nframes) {
1301     gravity_fiber_t *fiber = (gravity_fiber_t *)mem_alloc(NULL, sizeof(gravity_fiber_t));
1302     assert(fiber);
1303 
1304     fiber->isa = gravity_class_fiber;
1305     fiber->caller = NULL;
1306     fiber->result = VALUE_FROM_NULL;
1307 
1308     if (nstack < DEFAULT_MINSTACK_SIZE) nstack = DEFAULT_MINSTACK_SIZE;
1309     fiber->stack = (gravity_value_t *)mem_alloc(NULL, sizeof(gravity_value_t) * nstack);
1310     fiber->stacktop = fiber->stack;
1311     fiber->stackalloc = nstack;
1312 
1313     if (nframes < DEFAULT_MINCFRAME_SIZE) nframes = DEFAULT_MINCFRAME_SIZE;
1314     fiber->frames = (gravity_callframe_t *)mem_alloc(NULL, sizeof(gravity_callframe_t) * nframes);
1315     fiber->framesalloc = nframes;
1316     fiber->nframes = 1;
1317 
1318     fiber->upvalues = NULL;
1319 
1320     gravity_callframe_t *frame = &fiber->frames[0];
1321     if (closure) {
1322         frame->closure = closure;
1323         frame->ip = (closure->f->tag == EXEC_TYPE_NATIVE) ? closure->f->bytecode : NULL;
1324     }
1325     frame->dest = 0;
1326     frame->stackstart = fiber->stack;
1327 
1328     // replace self with fiber instance
1329     frame->stackstart[0] = VALUE_FROM_OBJECT(fiber);
1330 
1331     if (vm) gravity_vm_transfer(vm, (gravity_object_t*) fiber);
1332     return fiber;
1333 }
1334 
gravity_fiber_free(gravity_vm * vm,gravity_fiber_t * fiber)1335 void gravity_fiber_free (gravity_vm *vm, gravity_fiber_t *fiber) {
1336     #pragma unused(vm)
1337 
1338     DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)fiber, true));
1339     if (fiber->error) mem_free(fiber->error);
1340     mem_free(fiber->stack);
1341     mem_free(fiber->frames);
1342     mem_free(fiber);
1343 }
1344 
gravity_fiber_reassign(gravity_fiber_t * fiber,gravity_closure_t * closure,uint16_t nargs)1345 void gravity_fiber_reassign (gravity_fiber_t *fiber, gravity_closure_t *closure, uint16_t nargs) {
1346     gravity_callframe_t *frame = &fiber->frames[0];
1347     frame->closure = closure;
1348     frame->ip = (closure->f->tag == EXEC_TYPE_NATIVE) ? closure->f->bytecode : NULL;
1349 
1350     frame->dest = 0;
1351     frame->stackstart = fiber->stack;
1352 
1353     fiber->nframes = 1;
1354     fiber->upvalues = NULL;
1355 
1356     // update stacktop in order to be GC friendly
1357     fiber->stacktop += FN_COUNTREG(closure->f, nargs);
1358 }
1359 
gravity_fiber_reset(gravity_fiber_t * fiber)1360 void gravity_fiber_reset (gravity_fiber_t *fiber) {
1361     fiber->caller = NULL;
1362     fiber->result = VALUE_FROM_NULL;
1363     fiber->nframes = 0;
1364     fiber->upvalues = NULL;
1365     fiber->stacktop = fiber->stack;
1366 }
1367 
gravity_fiber_seterror(gravity_fiber_t * fiber,const char * error)1368 void gravity_fiber_seterror (gravity_fiber_t *fiber, const char *error) {
1369     if (fiber->error) mem_free(fiber->error);
1370     fiber->error = (char *)string_dup(error);
1371 }
1372 
gravity_fiber_size(gravity_vm * vm,gravity_fiber_t * fiber)1373 uint32_t gravity_fiber_size (gravity_vm *vm, gravity_fiber_t *fiber) {
1374     SET_OBJECT_VISITED_FLAG(fiber, true);
1375 
1376     // internal size
1377     uint32_t fiber_size = sizeof(gravity_fiber_t);
1378     fiber_size += fiber->stackalloc * sizeof(gravity_value_t);
1379     fiber_size += fiber->framesalloc * sizeof(gravity_callframe_t);
1380 
1381     // stack size
1382     for (gravity_value_t* slot = fiber->stack; slot < fiber->stacktop; ++slot) {
1383         fiber_size += gravity_value_size(vm, *slot);
1384     }
1385 
1386     fiber_size += string_size(fiber->error);
1387     fiber_size += gravity_object_size(vm, (gravity_object_t *)fiber->caller);
1388 
1389     SET_OBJECT_VISITED_FLAG(fiber, false);
1390     return fiber_size;
1391 }
1392 
gravity_fiber_blacken(gravity_vm * vm,gravity_fiber_t * fiber)1393 void gravity_fiber_blacken (gravity_vm *vm, gravity_fiber_t *fiber) {
1394     gravity_vm_memupdate(vm, gravity_fiber_size(vm, fiber));
1395 
1396     // gray call frame functions
1397     for (uint32_t i=0; i < fiber->nframes; ++i) {
1398         gravity_gray_object(vm, (gravity_object_t *)fiber->frames[i].closure);
1399 		gravity_gray_object(vm, (gravity_object_t *)fiber->frames[i].args);
1400     }
1401 
1402     // gray stack variables
1403     for (gravity_value_t *slot = fiber->stack; slot < fiber->stacktop; ++slot) {
1404         gravity_gray_value(vm, *slot);
1405     }
1406 
1407     // gray upvalues
1408     gravity_upvalue_t *upvalue = fiber->upvalues;
1409     while (upvalue) {
1410         gravity_gray_object(vm, (gravity_object_t *)upvalue);
1411         upvalue = upvalue->next;
1412     }
1413 
1414     gravity_gray_object(vm, (gravity_object_t *)fiber->caller);
1415 }
1416 
1417 // MARK: -
1418 
gravity_object_serialize(gravity_object_t * obj,json_t * json)1419 void gravity_object_serialize (gravity_object_t *obj, json_t *json) {
1420     if (obj->isa == gravity_class_function)
1421         gravity_function_serialize((gravity_function_t *)obj, json);
1422     else if (obj->isa == gravity_class_class)
1423         gravity_class_serialize((gravity_class_t *)obj, json);
1424     else assert(0);
1425 }
1426 
gravity_object_deserialize(gravity_vm * vm,json_value * entry)1427 gravity_object_t *gravity_object_deserialize (gravity_vm *vm, json_value *entry) {
1428     // this function is able to deserialize ONLY objects with a type label
1429 
1430     // sanity check
1431     if (entry->type != json_object) return NULL;
1432     if (entry->u.object.length == 0) return NULL;
1433 
1434     // the first entry value must specify gravity object type
1435     const char *label = entry->u.object.values[0].name;
1436     json_value *value = entry->u.object.values[0].value;
1437 
1438     if (string_casencmp(label, GRAVITY_JSON_LABELTYPE, 4) != 0) {
1439         // if no label type found then assume it is a map object
1440         gravity_map_t *m = gravity_map_deserialize(vm, entry);
1441         return (gravity_object_t *)m;
1442     }
1443 
1444     // sanity check
1445     if (value->type != json_string) return NULL;
1446 
1447     // FUNCTION case
1448     if (string_casencmp(value->u.string.ptr, GRAVITY_JSON_FUNCTION, value->u.string.length) == 0) {
1449         gravity_function_t *f = gravity_function_deserialize(vm, entry);
1450         return (gravity_object_t *)f;
1451     }
1452 
1453     // CLASS case
1454     if (string_casencmp(value->u.string.ptr, GRAVITY_JSON_CLASS, value->u.string.length) == 0) {
1455         gravity_class_t *c = gravity_class_deserialize(vm, entry);
1456         return (gravity_object_t *)c;
1457     }
1458 
1459     // MAP/ENUM case
1460     if ((string_casencmp(value->u.string.ptr, GRAVITY_JSON_MAP, value->u.string.length) == 0) ||
1461         (string_casencmp(value->u.string.ptr, GRAVITY_JSON_ENUM, value->u.string.length) == 0)) {
1462         gravity_map_t *m = gravity_map_deserialize(vm, entry);
1463         return (gravity_object_t *)m;
1464     }
1465 
1466     // RANGE case
1467     if (string_casencmp(value->u.string.ptr, GRAVITY_JSON_RANGE, value->u.string.length) == 0) {
1468         gravity_range_t *range = gravity_range_deserialize(vm, entry);
1469         return (gravity_object_t *)range;
1470     }
1471 
1472     // unhandled case
1473     DEBUG_DESERIALIZE("gravity_object_deserialize unknown type");
1474     return NULL;
1475 }
1476 #undef REPORT_JSON_ERROR
1477 
gravity_object_debug(gravity_object_t * obj,bool is_free)1478 const char *gravity_object_debug (gravity_object_t *obj, bool is_free) {
1479     if ((!obj) || (!OBJECT_IS_VALID(obj))) return "";
1480 
1481     if (OBJECT_ISA_INT(obj)) return "INT";
1482     if (OBJECT_ISA_FLOAT(obj)) return "FLOAT";
1483     if (OBJECT_ISA_BOOL(obj)) return "BOOL";
1484     if (OBJECT_ISA_NULL(obj)) return "NULL";
1485 
1486     static char buffer[512];
1487     if (OBJECT_ISA_FUNCTION(obj)) {
1488         const char *name = ((gravity_function_t*)obj)->identifier;
1489         if (!name) name = "ANONYMOUS";
1490         snprintf(buffer, sizeof(buffer), "FUNCTION %p %s", obj, name);
1491         return buffer;
1492     }
1493 
1494     if (OBJECT_ISA_CLOSURE(obj)) {
1495         // cannot guarantee ptr validity during a free
1496         const char *name = (is_free) ? NULL : ((gravity_closure_t*)obj)->f->identifier;
1497         if (!name) name = "ANONYMOUS";
1498         snprintf(buffer, sizeof(buffer), "CLOSURE %p %s", obj, name);
1499         return buffer;
1500     }
1501 
1502     if (OBJECT_ISA_CLASS(obj)) {
1503         const char *name = ((gravity_class_t*)obj)->identifier;
1504         if (!name) name = "ANONYMOUS";
1505         snprintf(buffer, sizeof(buffer), "CLASS %p %s", obj, name);
1506         return buffer;
1507     }
1508 
1509     if (OBJECT_ISA_STRING(obj)) {
1510         snprintf(buffer, sizeof(buffer), "STRING %p %s", obj, ((gravity_string_t*)obj)->s);
1511         return buffer;
1512     }
1513 
1514     if (OBJECT_ISA_INSTANCE(obj)) {
1515         // cannot guarantee ptr validity during a free
1516         gravity_class_t *c = (is_free) ? NULL : ((gravity_instance_t*)obj)->objclass;
1517         const char *name = (c && c->identifier) ? c->identifier : "ANONYMOUS";
1518         snprintf(buffer, sizeof(buffer), "INSTANCE %p OF %s", obj, name);
1519         return buffer;
1520     }
1521 
1522     if (OBJECT_ISA_RANGE(obj)) {
1523         snprintf(buffer, sizeof(buffer), "RANGE %p %ld %ld", obj, (long)((gravity_range_t*)obj)->from, (long)((gravity_range_t*)obj)->to);
1524         return buffer;
1525     }
1526 
1527     if (OBJECT_ISA_LIST(obj)) {
1528         snprintf(buffer, sizeof(buffer), "LIST %p (%ld items)", obj, (long)marray_size(((gravity_list_t*)obj)->array));
1529         return buffer;
1530     }
1531 
1532     if (OBJECT_ISA_MAP(obj)) {
1533          snprintf(buffer, sizeof(buffer), "MAP %p (%ld items)", obj, (long)gravity_hash_count(((gravity_map_t*)obj)->hash));
1534          return buffer;
1535     }
1536 
1537     if (OBJECT_ISA_FIBER(obj)) {
1538         snprintf(buffer, sizeof(buffer), "FIBER %p", obj);
1539         return buffer;
1540     }
1541 
1542     if (OBJECT_ISA_UPVALUE(obj)) {
1543         snprintf(buffer, sizeof(buffer), "UPVALUE %p", obj);
1544         return buffer;
1545     }
1546 
1547     return "N/A";
1548 }
1549 
gravity_object_free(gravity_vm * vm,gravity_object_t * obj)1550 void gravity_object_free (gravity_vm *vm, gravity_object_t *obj) {
1551     if ((!obj) || (!OBJECT_IS_VALID(obj))) return;
1552 
1553     if (obj->gc.free) obj->gc.free(vm, obj);
1554     else if (OBJECT_ISA_CLASS(obj)) gravity_class_free(vm, (gravity_class_t *)obj);
1555     else if (OBJECT_ISA_FUNCTION(obj)) gravity_function_free(vm, (gravity_function_t *)obj);
1556     else if (OBJECT_ISA_CLOSURE(obj)) gravity_closure_free(vm, (gravity_closure_t *)obj);
1557     else if (OBJECT_ISA_INSTANCE(obj)) gravity_instance_free(vm, (gravity_instance_t *)obj);
1558     else if (OBJECT_ISA_LIST(obj)) gravity_list_free(vm, (gravity_list_t *)obj);
1559     else if (OBJECT_ISA_MAP(obj)) gravity_map_free(vm, (gravity_map_t *)obj);
1560     else if (OBJECT_ISA_FIBER(obj)) gravity_fiber_free(vm, (gravity_fiber_t *)obj);
1561     else if (OBJECT_ISA_RANGE(obj)) gravity_range_free(vm, (gravity_range_t *)obj);
1562     else if (OBJECT_ISA_MODULE(obj)) gravity_module_free(vm, (gravity_module_t *)obj);
1563     else if (OBJECT_ISA_STRING(obj)) gravity_string_free(vm, (gravity_string_t *)obj);
1564     else if (OBJECT_ISA_UPVALUE(obj)) gravity_upvalue_free(vm, (gravity_upvalue_t *)obj);
1565     else assert(0); // should never reach this point
1566 }
1567 
gravity_object_size(gravity_vm * vm,gravity_object_t * obj)1568 uint32_t gravity_object_size (gravity_vm *vm, gravity_object_t *obj) {
1569     if ((!obj) || (!OBJECT_IS_VALID(obj))) return 0;
1570 
1571     // check if object has already been visited (to avoid infinite loop)
1572     if (obj->gc.visited) return 0;
1573 
1574     if (obj->gc.size) return obj->gc.size(vm, obj);
1575     else if (OBJECT_ISA_CLASS(obj)) return gravity_class_size(vm, (gravity_class_t *)obj);
1576     else if (OBJECT_ISA_FUNCTION(obj)) return gravity_function_size(vm, (gravity_function_t *)obj);
1577     else if (OBJECT_ISA_CLOSURE(obj)) return gravity_closure_size(vm, (gravity_closure_t *)obj);
1578     else if (OBJECT_ISA_INSTANCE(obj)) return gravity_instance_size(vm, (gravity_instance_t *)obj);
1579     else if (OBJECT_ISA_LIST(obj)) return gravity_list_size(vm, (gravity_list_t *)obj);
1580     else if (OBJECT_ISA_MAP(obj)) return gravity_map_size(vm, (gravity_map_t *)obj);
1581     else if (OBJECT_ISA_FIBER(obj)) return gravity_fiber_size(vm, (gravity_fiber_t *)obj);
1582     else if (OBJECT_ISA_RANGE(obj)) return gravity_range_size(vm, (gravity_range_t *)obj);
1583     else if (OBJECT_ISA_MODULE(obj)) return gravity_module_size(vm, (gravity_module_t *)obj);
1584     else if (OBJECT_ISA_STRING(obj)) return gravity_string_size(vm, (gravity_string_t *)obj);
1585     else if (OBJECT_ISA_UPVALUE(obj)) return gravity_upvalue_size(vm, (gravity_upvalue_t *)obj);
1586     return 0;
1587 }
1588 
gravity_object_blacken(gravity_vm * vm,gravity_object_t * obj)1589 void gravity_object_blacken (gravity_vm *vm, gravity_object_t *obj) {
1590     if ((!obj) || (!OBJECT_IS_VALID(obj))) return;
1591 
1592     if (obj->gc.blacken) obj->gc.blacken(vm, obj);
1593     else if (OBJECT_ISA_CLASS(obj)) gravity_class_blacken(vm, (gravity_class_t *)obj);
1594     else if (OBJECT_ISA_FUNCTION(obj)) gravity_function_blacken(vm, (gravity_function_t *)obj);
1595     else if (OBJECT_ISA_CLOSURE(obj)) gravity_closure_blacken(vm, (gravity_closure_t *)obj);
1596     else if (OBJECT_ISA_INSTANCE(obj)) gravity_instance_blacken(vm, (gravity_instance_t *)obj);
1597     else if (OBJECT_ISA_LIST(obj)) gravity_list_blacken(vm, (gravity_list_t *)obj);
1598     else if (OBJECT_ISA_MAP(obj)) gravity_map_blacken(vm, (gravity_map_t *)obj);
1599     else if (OBJECT_ISA_FIBER(obj)) gravity_fiber_blacken(vm, (gravity_fiber_t *)obj);
1600     else if (OBJECT_ISA_RANGE(obj)) gravity_range_blacken(vm, (gravity_range_t *)obj);
1601     else if (OBJECT_ISA_MODULE(obj)) gravity_module_blacken(vm, (gravity_module_t *)obj);
1602     else if (OBJECT_ISA_STRING(obj)) gravity_string_blacken(vm, (gravity_string_t *)obj);
1603     else if (OBJECT_ISA_UPVALUE(obj)) gravity_upvalue_blacken(vm, (gravity_upvalue_t *)obj);
1604     //else assert(0); // should never reach this point
1605 }
1606 
1607 // MARK: -
1608 
gravity_instance_new(gravity_vm * vm,gravity_class_t * c)1609 gravity_instance_t *gravity_instance_new (gravity_vm *vm, gravity_class_t *c) {
1610     gravity_instance_t *instance = (gravity_instance_t *)mem_alloc(NULL, sizeof(gravity_instance_t));
1611 
1612     instance->isa = gravity_class_instance;
1613     instance->objclass = c;
1614 
1615     if (c->nivars) instance->ivars = (gravity_value_t *)mem_alloc(NULL, c->nivars * sizeof(gravity_value_t));
1616     for (uint32_t i=0; i<c->nivars; ++i) instance->ivars[i] = VALUE_FROM_NULL;
1617 
1618     if (vm) gravity_vm_transfer(vm, (gravity_object_t*) instance);
1619     return instance;
1620 }
1621 
gravity_instance_clone(gravity_vm * vm,gravity_instance_t * src_instance)1622 gravity_instance_t *gravity_instance_clone (gravity_vm *vm, gravity_instance_t *src_instance) {
1623     gravity_class_t *c = src_instance->objclass;
1624 
1625     gravity_instance_t *instance = (gravity_instance_t *)mem_alloc(NULL, sizeof(gravity_instance_t));
1626     instance->isa = gravity_class_instance;
1627     instance->objclass = c;
1628 
1629     // if c is an anonymous class then it must be deeply copied
1630     if (gravity_class_is_anon(c)) {
1631         // clone class c and all its closures
1632     }
1633 
1634     gravity_delegate_t *delegate = gravity_vm_delegate(vm);
1635 
1636     instance->xdata = (src_instance->xdata && delegate->bridge_clone) ? delegate->bridge_clone(vm, src_instance->xdata) : NULL;
1637 
1638     if (c->nivars) instance->ivars = (gravity_value_t *)mem_alloc(NULL, c->nivars * sizeof(gravity_value_t));
1639     for (uint32_t i=0; i<c->nivars; ++i) instance->ivars[i] = src_instance->ivars[i];
1640 
1641     if (vm) gravity_vm_transfer(vm, (gravity_object_t*) instance);
1642     return instance;
1643 }
1644 
gravity_instance_setivar(gravity_instance_t * instance,uint32_t idx,gravity_value_t value)1645 void gravity_instance_setivar (gravity_instance_t *instance, uint32_t idx, gravity_value_t value) {
1646     if (idx < instance->objclass->nivars) instance->ivars[idx] = value;
1647 }
1648 
gravity_instance_setxdata(gravity_instance_t * i,void * xdata)1649 void gravity_instance_setxdata (gravity_instance_t *i, void *xdata) {
1650     i->xdata = xdata;
1651 }
1652 
gravity_instance_deinit(gravity_vm * vm,gravity_instance_t * i)1653 void gravity_instance_deinit (gravity_vm *vm, gravity_instance_t *i) {
1654     STATICVALUE_FROM_STRING(key, CLASS_DESTRUCTOR_NAME, strlen(CLASS_DESTRUCTOR_NAME));
1655 
1656     // check for destructor
1657     gravity_closure_t *closure = gravity_class_lookup_closure(i->objclass, key);
1658     if (closure) gravity_vm_runclosure(vm, closure, VALUE_FROM_OBJECT(i), NULL, 0);
1659 }
1660 
gravity_instance_free(gravity_vm * vm,gravity_instance_t * i)1661 void gravity_instance_free (gravity_vm *vm, gravity_instance_t *i) {
1662     DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)i, true));
1663 
1664     // check if bridged data needs to be freed too
1665     if (i->xdata && vm) {
1666         gravity_delegate_t *delegate = gravity_vm_delegate(vm);
1667         if (delegate->bridge_free) delegate->bridge_free(vm, (gravity_object_t *)i);
1668     }
1669 
1670     if (i->ivars) mem_free(i->ivars);
1671     mem_free((void *)i);
1672 }
1673 
gravity_instance_lookup_event(gravity_instance_t * i,const char * name)1674 gravity_closure_t *gravity_instance_lookup_event (gravity_instance_t *i, const char *name) {
1675     // TODO: implemented as gravity_class_lookup but should be the exact opposite
1676 
1677     STATICVALUE_FROM_STRING(key, name, strlen(name));
1678     gravity_class_t *c = i->objclass;
1679     while (c) {
1680         gravity_value_t *v = gravity_hash_lookup(c->htable, key);
1681         // NOTE: there could be events (like InitContainer) which are empty (bytecode NULL) should I handle them here?
1682         if ((v) && (OBJECT_ISA_CLOSURE(v->p))) return (gravity_closure_t *)v->p;
1683         c = c->superclass;
1684     }
1685     return NULL;
1686 }
1687 
gravity_instance_lookup_property(gravity_vm * vm,gravity_instance_t * i,gravity_value_t key)1688 gravity_value_t gravity_instance_lookup_property (gravity_vm *vm, gravity_instance_t *i, gravity_value_t key) {
1689     gravity_closure_t *closure = gravity_class_lookup_closure(i->objclass, key);
1690     if (!closure) return VALUE_NOT_VALID;
1691 
1692     // check if it is a real property
1693     gravity_function_t *func = closure->f;
1694     if (!func || func->tag != EXEC_TYPE_SPECIAL) return VALUE_NOT_VALID;
1695 
1696     // check if it a computed property with a getter
1697     if (FUNCTION_ISA_GETTER(func)) {
1698         gravity_closure_t *getter = (gravity_closure_t *)func->special[EXEC_TYPE_SPECIAL_GETTER];
1699         if (gravity_vm_runclosure(vm, getter, VALUE_FROM_NULL, NULL, 0)) return gravity_vm_result(vm);
1700     }
1701 
1702     // now I am sure that it is a real property (non-computed)
1703     return i->ivars[func->index];
1704 }
1705 
gravity_instance_size(gravity_vm * vm,gravity_instance_t * i)1706 uint32_t gravity_instance_size (gravity_vm *vm, gravity_instance_t *i) {
1707     SET_OBJECT_VISITED_FLAG(i, true);
1708 
1709     uint32_t instance_size = sizeof(gravity_instance_t) + (i->objclass->nivars * sizeof(gravity_value_t));
1710 
1711     gravity_delegate_t *delegate = gravity_vm_delegate(vm);
1712     if (i->xdata && delegate->bridge_size)
1713         instance_size += delegate->bridge_size(vm, i->xdata);
1714 
1715     SET_OBJECT_VISITED_FLAG(i, false);
1716     return instance_size;
1717 }
1718 
gravity_instance_blacken(gravity_vm * vm,gravity_instance_t * i)1719 void gravity_instance_blacken (gravity_vm *vm, gravity_instance_t *i) {
1720     gravity_vm_memupdate(vm, gravity_instance_size(vm, i));
1721 
1722     // instance class
1723     gravity_gray_object(vm, (gravity_object_t *)i->objclass);
1724 
1725     // ivars
1726     for (uint32_t j=0; j<i->objclass->nivars; ++j) {
1727         gravity_gray_value(vm, i->ivars[j]);
1728     }
1729 
1730     // xdata
1731     if (i->xdata) {
1732         gravity_delegate_t *delegate = gravity_vm_delegate(vm);
1733         if (delegate->bridge_blacken) delegate->bridge_blacken(vm, i->xdata);
1734     }
1735 }
1736 
gravity_instance_serialize(gravity_instance_t * instance,json_t * json)1737 void gravity_instance_serialize (gravity_instance_t *instance, json_t *json) {
1738     gravity_class_t *c = instance->objclass;
1739 
1740     const char *label = json_get_label(json, NULL);
1741     json_begin_object(json, label);
1742 
1743     json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_INSTANCE);
1744     json_add_cstring(json, GRAVITY_JSON_CLASS, c->identifier);
1745 
1746     if (c->nivars) {
1747         json_begin_array(json, GRAVITY_JSON_LABELIVAR);
1748         for (uint32_t i=0; i<c->nivars; ++i) {
1749             gravity_value_serialize(NULL, instance->ivars[i], json);
1750         }
1751         json_end_array(json);
1752     }
1753 
1754     json_end_object(json);
1755 }
1756 
gravity_instance_isstruct(gravity_instance_t * i)1757 bool gravity_instance_isstruct (gravity_instance_t *i) {
1758     return i->objclass->is_struct;
1759 }
1760 
1761 // MARK: -
hash_value_compare_cb(gravity_value_t v1,gravity_value_t v2,void * data)1762 static bool hash_value_compare_cb (gravity_value_t v1, gravity_value_t v2, void *data) {
1763     #pragma unused (data)
1764     return gravity_value_equals(v1, v2);
1765 }
1766 
gravity_value_vm_equals(gravity_vm * vm,gravity_value_t v1,gravity_value_t v2)1767 bool gravity_value_vm_equals (gravity_vm *vm, gravity_value_t v1, gravity_value_t v2) {
1768     bool result = gravity_value_equals(v1, v2);
1769     if (result || !vm) return result;
1770 
1771     // sanity check
1772     if (!(VALUE_ISA_INSTANCE(v1) && VALUE_ISA_INSTANCE(v2))) return false;
1773 
1774     // if here means that they are two heap allocated objects
1775     gravity_instance_t *obj1 = (gravity_instance_t *)VALUE_AS_OBJECT(v1);
1776     gravity_instance_t *obj2 = (gravity_instance_t *)VALUE_AS_OBJECT(v2);
1777 
1778     gravity_delegate_t *delegate = gravity_vm_delegate(vm);
1779     if (obj1->xdata && obj2->xdata && delegate->bridge_equals) {
1780         return delegate->bridge_equals(vm, obj1->xdata, obj2->xdata);
1781     }
1782 
1783     return false;
1784 }
1785 
gravity_value_equals(gravity_value_t v1,gravity_value_t v2)1786 bool gravity_value_equals (gravity_value_t v1, gravity_value_t v2) {
1787 
1788     // check same class
1789     if (v1.isa != v2.isa) return false;
1790 
1791     // check same value for value types
1792     if ((v1.isa == gravity_class_int) || (v1.isa == gravity_class_bool) || (v1.isa == gravity_class_null)) {
1793         return (v1.n == v2.n);
1794     } else if (v1.isa == gravity_class_float) {
1795         #if GRAVITY_ENABLE_DOUBLE
1796         return (fabs(v1.f - v2.f) < EPSILON);
1797         #else
1798         return (fabsf(v1.f - v2.f) < EPSILON);
1799         #endif
1800     } else if (v1.isa == gravity_class_string) {
1801         gravity_string_t *s1 = VALUE_AS_STRING(v1);
1802         gravity_string_t *s2 = VALUE_AS_STRING(v2);
1803         if (s1->hash != s2->hash) return false;
1804         if (s1->len != s2->len) return false;
1805         // same hash and same len so let's compare bytes
1806         return (memcmp(s1->s, s2->s, s1->len) == 0);
1807     } else if (v1.isa == gravity_class_range) {
1808         gravity_range_t *r1 = VALUE_AS_RANGE(v1);
1809         gravity_range_t *r2 = VALUE_AS_RANGE(v2);
1810         return ((r1->from == r2->from) && (r1->to == r2->to));
1811     } else if (v1.isa == gravity_class_list) {
1812         gravity_list_t *list1 = VALUE_AS_LIST(v1);
1813         gravity_list_t *list2 = VALUE_AS_LIST(v2);
1814         if (marray_size(list1->array) != marray_size(list2->array)) return false;
1815         size_t count = marray_size(list1->array);
1816         for (size_t i=0; i<count; ++i) {
1817             gravity_value_t value1 = marray_get(list1->array, i);
1818             gravity_value_t value2 = marray_get(list2->array, i);
1819             if (!gravity_value_equals(value1, value2)) return false;
1820         }
1821         return true;
1822     } else if (v1.isa == gravity_class_map) {
1823         gravity_map_t *map1 = VALUE_AS_MAP(v1);
1824         gravity_map_t *map2 = VALUE_AS_MAP(v2);
1825         return gravity_hash_compare(map1->hash, map2->hash, hash_value_compare_cb, NULL);
1826     }
1827 
1828     // if here means that they are two heap allocated objects
1829     gravity_object_t *obj1 = VALUE_AS_OBJECT(v1);
1830     gravity_object_t *obj2 = VALUE_AS_OBJECT(v2);
1831     if (obj1->isa != obj2->isa) return false;
1832 
1833     return (obj1 == obj2);
1834 }
1835 
gravity_value_hash(gravity_value_t value)1836 uint32_t gravity_value_hash (gravity_value_t value) {
1837     if (value.isa == gravity_class_string)
1838         return VALUE_AS_STRING(value)->hash;
1839 
1840     if ((value.isa == gravity_class_int) || (value.isa == gravity_class_bool) || (value.isa == gravity_class_null))
1841         return gravity_hash_compute_int(value.n);
1842 
1843     if (value.isa == gravity_class_float)
1844         return gravity_hash_compute_float(value.f);
1845 
1846     return gravity_hash_compute_buffer((const char *)value.p, sizeof(gravity_object_t*));
1847 }
1848 
gravity_value_getclass(gravity_value_t v)1849 inline gravity_class_t *gravity_value_getclass (gravity_value_t v) {
1850     if ((v.isa == gravity_class_class) && (v.p->objclass == gravity_class_object)) return (gravity_class_t *)v.p;
1851     if ((v.isa == gravity_class_instance) || (v.isa == gravity_class_class)) return (v.p) ? v.p->objclass : NULL;
1852     return v.isa;
1853 }
1854 
gravity_value_getsuper(gravity_value_t v)1855 inline gravity_class_t *gravity_value_getsuper (gravity_value_t v) {
1856     gravity_class_t *c = gravity_value_getclass(v);
1857     return (c && c->superclass) ? c->superclass : NULL;
1858 }
1859 
gravity_value_free(gravity_vm * vm,gravity_value_t v)1860 void gravity_value_free (gravity_vm *vm, gravity_value_t v) {
1861     if (!gravity_value_isobject(v)) return;
1862     gravity_object_free(vm, VALUE_AS_OBJECT(v));
1863 }
1864 
gravity_map_serialize_iterator(gravity_hash_t * hashtable,gravity_value_t key,gravity_value_t v,void * data)1865 static void gravity_map_serialize_iterator (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t v, void *data) {
1866     #pragma unused(hashtable)
1867     assert(key.isa == gravity_class_string);
1868 
1869     json_t *json = (json_t *)data;
1870     const char *key_value = VALUE_AS_STRING(key)->s;
1871 
1872     gravity_value_serialize(key_value, v, json);
1873 }
1874 
gravity_value_serialize(const char * key,gravity_value_t v,json_t * json)1875 void gravity_value_serialize (const char *key, gravity_value_t v, json_t *json) {
1876     // NULL
1877     if (VALUE_ISA_NULL(v)) {
1878         json_add_null(json, key);
1879         return;
1880     }
1881 
1882     // UNDEFINED (convention used to represent an UNDEFINED value)
1883     if (VALUE_ISA_UNDEFINED(v)) {
1884         if (json_option_isset(json, json_opt_no_undef)) {
1885             json_add_null(json, key);
1886         } else {
1887             json_begin_object(json, key);
1888             json_end_object(json);
1889         }
1890         return;
1891     }
1892 
1893     // BOOL
1894     if (VALUE_ISA_BOOL(v)) {
1895         json_add_bool(json, key, (v.n == 0) ? false : true);
1896         return;
1897     }
1898 
1899     // INT
1900     if (VALUE_ISA_INT(v)) {
1901         json_add_int(json, key, (int64_t)v.n);
1902         return;
1903     }
1904 
1905     // FLOAT
1906     if (VALUE_ISA_FLOAT(v)) {
1907         json_add_double(json, key, (double)v.f);
1908         return;
1909     }
1910 
1911     // FUNCTION
1912     if (VALUE_ISA_FUNCTION(v)) {
1913         json_set_label(json, key);
1914         gravity_function_serialize(VALUE_AS_FUNCTION(v), json);
1915         return;
1916     }
1917 
1918     // CLOSURE
1919     if (VALUE_ISA_CLOSURE(v)) {
1920         json_set_label(json, key);
1921         gravity_function_serialize(VALUE_AS_CLOSURE(v)->f, json);
1922         return;
1923     }
1924 
1925     // CLASS
1926     if (VALUE_ISA_CLASS(v)) {
1927         json_set_label(json, key);
1928         gravity_class_serialize(VALUE_AS_CLASS(v), json);
1929         return;
1930     }
1931 
1932     // STRING
1933     if (VALUE_ISA_STRING(v)) {
1934         gravity_string_t *value = VALUE_AS_STRING(v);
1935         json_add_string(json, key, value->s, value->len);
1936         return;
1937     }
1938 
1939     // LIST (ARRAY)
1940     if (VALUE_ISA_LIST(v)) {
1941         gravity_list_t *value = VALUE_AS_LIST(v);
1942         json_begin_array(json, key);
1943         size_t count = marray_size(value->array);
1944         for (size_t j=0; j<count; j++) {
1945             gravity_value_t item = marray_get(value->array, j);
1946             // here I am sure that value is a literal value
1947             gravity_value_serialize(NULL, item, json);
1948         }
1949         json_end_array(json);
1950         return;
1951     }
1952 
1953     // MAP (HASH)
1954     // a map is serialized only if it contains only literals, otherwise it is computed at runtime
1955     if (VALUE_ISA_MAP(v)) {
1956         gravity_map_t *value = VALUE_AS_MAP(v);
1957         json_begin_object(json, key);
1958         if (!json_option_isset(json, json_opt_no_maptype)) json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_MAP);
1959         gravity_hash_iterate(value->hash, gravity_map_serialize_iterator, json);
1960         json_end_object(json);
1961         return;
1962     }
1963 
1964     // RANGE
1965     if (VALUE_ISA_RANGE(v)) {
1966         json_set_label(json, key);
1967         gravity_range_serialize(VALUE_AS_RANGE(v), json);
1968         return;
1969     }
1970 
1971     // INSTANCE
1972     if (VALUE_ISA_INSTANCE(v)) {
1973         json_set_label(json, key);
1974         gravity_instance_serialize(VALUE_AS_INSTANCE(v), json);
1975         return;
1976     }
1977 
1978     // FIBER
1979     if (VALUE_ISA_FIBER(v)) {
1980         return;
1981     }
1982 
1983     // should never reach this point
1984     assert(0);
1985 }
1986 
gravity_value_isobject(gravity_value_t v)1987 bool gravity_value_isobject (gravity_value_t v) {
1988     // was:
1989     // if (VALUE_ISA_NOTVALID(v)) return false;
1990     // if (VALUE_ISA_INT(v)) return false;
1991     // if (VALUE_ISA_FLOAT(v)) return false;
1992     // if (VALUE_ISA_BOOL(v)) return false;
1993     // if (VALUE_ISA_NULL(v)) return false;
1994     // if (VALUE_ISA_UNDEFINED(v)) return false;
1995     // return true;
1996 
1997     if ((v.isa == NULL) || (v.isa == gravity_class_int) || (v.isa == gravity_class_float) ||
1998         (v.isa == gravity_class_bool) || (v.isa == gravity_class_null) || (v.p == NULL)) return false;
1999 
2000     // extra check to allow ONLY known objects
2001     if ((v.isa == gravity_class_string) || (v.isa == gravity_class_object) || (v.isa == gravity_class_function) ||
2002         (v.isa == gravity_class_closure) || (v.isa == gravity_class_fiber) || (v.isa == gravity_class_class) ||
2003         (v.isa == gravity_class_instance) || (v.isa == gravity_class_module) || (v.isa == gravity_class_list) ||
2004         (v.isa == gravity_class_map) || (v.isa == gravity_class_range) || (v.isa == gravity_class_upvalue)) return true;
2005 
2006     return false;
2007 }
2008 
gravity_value_size(gravity_vm * vm,gravity_value_t v)2009 uint32_t gravity_value_size (gravity_vm *vm, gravity_value_t v) {
2010     return (gravity_value_isobject(v)) ? gravity_object_size(vm, (gravity_object_t*)v.p) : 0;
2011 }
2012 
gravity_value_xdata(gravity_value_t value)2013 void *gravity_value_xdata (gravity_value_t value) {
2014     if (VALUE_ISA_INSTANCE(value)) {
2015         gravity_instance_t *i = VALUE_AS_INSTANCE(value);
2016         return i->xdata;
2017     } else if (VALUE_ISA_CLASS(value)) {
2018         gravity_class_t *c = VALUE_AS_CLASS(value);
2019         return c->xdata;
2020     }
2021     return NULL;
2022 }
2023 
gravity_value_name(gravity_value_t value)2024 const char *gravity_value_name (gravity_value_t value) {
2025     if (VALUE_ISA_INSTANCE(value)) {
2026         gravity_instance_t *instance = VALUE_AS_INSTANCE(value);
2027         return instance->objclass->identifier;
2028     } else if (VALUE_ISA_CLASS(value)) {
2029         gravity_class_t *c = VALUE_AS_CLASS(value);
2030         return c->identifier;
2031     }
2032     return NULL;
2033 }
2034 
gravity_value_dump(gravity_vm * vm,gravity_value_t v,char * buffer,uint16_t len)2035 void gravity_value_dump (gravity_vm *vm, gravity_value_t v, char *buffer, uint16_t len) {
2036     const char *type = NULL;
2037     const char *value = NULL;
2038     char        sbuffer[1024];
2039 
2040     if (buffer == NULL) buffer = sbuffer;
2041     if (len == 0) len = sizeof(sbuffer);
2042 
2043     if (v.isa == NULL) {
2044         type = "INVALID!";
2045         snprintf(buffer, len, "%s", type);
2046         value = buffer;
2047     } else if (v.isa == gravity_class_bool) {
2048         type = "BOOL";
2049         value = (v.n == 0) ? "false" : "true";
2050         snprintf(buffer, len, "(%s) %s", type, value);
2051         value = buffer;
2052     } else if (v.isa == gravity_class_null) {
2053         type = (v.n == 0) ? "NULL" : "UNDEFINED";
2054         snprintf(buffer, len, "%s", type);
2055         value = buffer;
2056     } else if (v.isa == gravity_class_int) {
2057         type = "INT";
2058         snprintf(buffer, len, "(%s) %" PRId64, type, v.n);
2059         value = buffer;
2060     } else if (v.isa == gravity_class_float) {
2061         type = "FLOAT";
2062         snprintf(buffer, len, "(%s) %g", type, v.f);
2063         value = buffer;
2064     } else if (v.isa == gravity_class_function) {
2065         type = "FUNCTION";
2066         value = VALUE_AS_FUNCTION(v)->identifier;
2067         snprintf(buffer, len, "(%s) %s (%p)", type, value, VALUE_AS_FUNCTION(v));
2068         value = buffer;
2069     } else if (v.isa == gravity_class_closure) {
2070         type = "CLOSURE";
2071         gravity_function_t *f = VALUE_AS_CLOSURE(v)->f;
2072         value = (f->identifier) ? (f->identifier) : "anon";
2073         snprintf(buffer, len, "(%s) %s (%p)", type, value, VALUE_AS_CLOSURE(v));
2074         value = buffer;
2075     } else if (v.isa == gravity_class_class) {
2076         type = "CLASS";
2077         value = VALUE_AS_CLASS(v)->identifier;
2078         snprintf(buffer, len, "(%s) %s (%p)", type, value, VALUE_AS_CLASS(v));
2079         value = buffer;
2080     } else if (v.isa == gravity_class_string) {
2081         type = "STRING";
2082         gravity_string_t *s = VALUE_AS_STRING(v);
2083         snprintf(buffer, len, "(%s) %.*s (%p)", type, s->len, s->s, s);
2084         value = buffer;
2085     } else if (v.isa == gravity_class_instance) {
2086         type = "INSTANCE";
2087         gravity_instance_t *i = VALUE_AS_INSTANCE(v);
2088         gravity_class_t *c = i->objclass;
2089         value = c->identifier;
2090         snprintf(buffer, len, "(%s) %s (%p)", type, value, i);
2091         value = buffer;
2092     } else if (v.isa == gravity_class_list) {
2093         type = "LIST";
2094         gravity_value_t sval = convert_value2string(vm, v);
2095         gravity_string_t *s = VALUE_AS_STRING(sval);
2096         snprintf(buffer, len, "(%s) %.*s (%p)", type, s->len, s->s, s);
2097         value = buffer;
2098     } else if (v.isa == gravity_class_map) {
2099         type = "MAP";
2100         gravity_value_t sval = convert_value2string(vm, v);
2101         gravity_string_t *s = VALUE_AS_STRING(sval);
2102         snprintf(buffer, len, "(%s) %.*s (%p)", type, s->len, s->s, s);
2103         value = buffer;
2104     } else if (v.isa == gravity_class_range) {
2105         type = "RANGE";
2106         gravity_range_t *r = VALUE_AS_RANGE(v);
2107         snprintf(buffer, len, "(%s) from %" PRId64 " to %" PRId64, type, r->from, r->to);
2108         value = buffer;
2109     } else if (v.isa == gravity_class_object) {
2110         type = "OBJECT";
2111         value = "N/A";
2112         snprintf(buffer, len, "(%s) %s", type, value);
2113         value = buffer;
2114     } else if (v.isa == gravity_class_fiber) {
2115         type = "FIBER";
2116         snprintf(buffer, len, "(%s) %p", type, v.p);
2117         value = buffer;
2118     } else {
2119         type = "N/A";
2120         value = "N/A";
2121         snprintf(buffer, len, "(%s) %s", type, value);
2122         value = buffer;
2123     }
2124 
2125     if (buffer == sbuffer) printf("%s\n", value);
2126 }
2127 
2128 // MARK: -
gravity_list_new(gravity_vm * vm,uint32_t n)2129 gravity_list_t *gravity_list_new (gravity_vm *vm, uint32_t n) {
2130     if (n > MAX_ALLOCATION) return NULL;
2131 
2132     gravity_list_t *list = (gravity_list_t *)mem_alloc(NULL, sizeof(gravity_list_t));
2133 
2134     list->isa = gravity_class_list;
2135     marray_init(list->array);
2136     marray_resize(gravity_value_t, list->array, n + MARRAY_DEFAULT_SIZE);
2137 
2138     if (vm) gravity_vm_transfer(vm, (gravity_object_t*) list);
2139     return list;
2140 }
2141 
gravity_list_from_array(gravity_vm * vm,uint32_t n,gravity_value_t * p)2142 gravity_list_t *gravity_list_from_array (gravity_vm *vm, uint32_t n, gravity_value_t *p) {
2143     gravity_list_t *list = (gravity_list_t *)mem_alloc(NULL, sizeof(gravity_list_t));
2144 
2145     list->isa = gravity_class_list;
2146     marray_init(list->array);
2147     // elements must be copied because for the compiler their registers are TEMP
2148     // and could be reused by other successive operations
2149     for (size_t i=0; i<n; ++i) marray_push(gravity_value_t, list->array, p[i]);
2150 
2151     if (vm) gravity_vm_transfer(vm, (gravity_object_t*) list);
2152     return list;
2153 }
2154 
gravity_list_free(gravity_vm * vm,gravity_list_t * list)2155 void gravity_list_free (gravity_vm *vm, gravity_list_t *list) {
2156     #pragma unused(vm)
2157     DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)list, true));
2158     marray_destroy(list->array);
2159     mem_free((void *)list);
2160 }
2161 
gravity_list_append_list(gravity_vm * vm,gravity_list_t * list1,gravity_list_t * list2)2162 void gravity_list_append_list (gravity_vm *vm, gravity_list_t *list1, gravity_list_t *list2) {
2163     #pragma unused(vm)
2164     // append list2 to list1
2165     size_t count = marray_size(list2->array);
2166     for (size_t i=0; i<count; ++i) {
2167         marray_push(gravity_value_t, list1->array, marray_get(list2->array, i));
2168     }
2169 }
2170 
gravity_list_size(gravity_vm * vm,gravity_list_t * list)2171 uint32_t gravity_list_size (gravity_vm *vm, gravity_list_t *list) {
2172     SET_OBJECT_VISITED_FLAG(list, true);
2173 
2174     uint32_t internal_size = 0;
2175     size_t count = marray_size(list->array);
2176     for (size_t i=0; i<count; ++i) {
2177         internal_size += gravity_value_size(vm, marray_get(list->array, i));
2178     }
2179     internal_size += sizeof(gravity_list_t);
2180 
2181     SET_OBJECT_VISITED_FLAG(list, false);
2182     return internal_size;
2183 }
2184 
gravity_list_blacken(gravity_vm * vm,gravity_list_t * list)2185 void gravity_list_blacken (gravity_vm *vm, gravity_list_t *list) {
2186     gravity_vm_memupdate(vm, gravity_list_size(vm, list));
2187 
2188     size_t count = marray_size(list->array);
2189     for (size_t i=0; i<count; ++i) {
2190         gravity_gray_value(vm, marray_get(list->array, i));
2191     }
2192 }
2193 
2194 // MARK: -
gravity_map_new(gravity_vm * vm,uint32_t n)2195 gravity_map_t *gravity_map_new (gravity_vm *vm, uint32_t n) {
2196     gravity_map_t *map = (gravity_map_t *)mem_alloc(NULL, sizeof(gravity_map_t));
2197 
2198     map->isa = gravity_class_map;
2199     map->hash = gravity_hash_create(n, gravity_value_hash, gravity_value_equals, NULL, NULL);
2200 
2201     if (vm) gravity_vm_transfer(vm, (gravity_object_t*) map);
2202     return map;
2203 }
2204 
gravity_map_free(gravity_vm * vm,gravity_map_t * map)2205 void gravity_map_free (gravity_vm *vm, gravity_map_t *map) {
2206     #pragma unused(vm)
2207 
2208     DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)map, true));
2209     gravity_hash_free(map->hash);
2210     mem_free((void *)map);
2211 }
2212 
gravity_map_append_map(gravity_vm * vm,gravity_map_t * map1,gravity_map_t * map2)2213 void gravity_map_append_map (gravity_vm *vm, gravity_map_t *map1, gravity_map_t *map2) {
2214     #pragma unused(vm)
2215     // append map2 to map1
2216     gravity_hash_append(map1->hash, map2->hash);
2217 }
2218 
gravity_map_insert(gravity_vm * vm,gravity_map_t * map,gravity_value_t key,gravity_value_t value)2219 void gravity_map_insert (gravity_vm *vm, gravity_map_t *map, gravity_value_t key, gravity_value_t value) {
2220     #pragma unused(vm)
2221     gravity_hash_insert(map->hash, key, value);
2222 }
2223 
gravity_map_deserialize(gravity_vm * vm,json_value * json)2224 static gravity_map_t *gravity_map_deserialize (gravity_vm *vm, json_value *json) {
2225     uint32_t n = json->u.object.length;
2226     gravity_map_t *map = gravity_map_new(vm, n);
2227 
2228     DEBUG_DESERIALIZE("DESERIALIZE MAP: %p\n", map);
2229 
2230     for (uint32_t i=0; i<n; ++i) { // from 1 to skip type
2231         const char *label = json->u.object.values[i].name;
2232         json_value *jsonv = json->u.object.values[i].value;
2233 
2234         gravity_value_t key = VALUE_FROM_CSTRING(vm, label);
2235         gravity_value_t value;
2236 
2237         switch (jsonv->type) {
2238             case json_integer:
2239                 value = VALUE_FROM_INT((gravity_int_t)jsonv->u.integer); break;
2240             case json_double:
2241                 value = VALUE_FROM_FLOAT((gravity_float_t)jsonv->u.dbl); break;
2242             case json_boolean:
2243                 value = VALUE_FROM_BOOL(jsonv->u.boolean); break;
2244             case json_string:
2245                 value = VALUE_FROM_STRING(vm, jsonv->u.string.ptr, jsonv->u.string.length); break;
2246             case json_null:
2247                 value = VALUE_FROM_NULL; break;
2248             case json_object: {
2249                 gravity_object_t *obj = gravity_object_deserialize(vm, jsonv);
2250                 value = (obj) ? VALUE_FROM_OBJECT(obj) : VALUE_FROM_NULL;
2251                 break;
2252             }
2253             case json_array: {
2254 
2255             }
2256             default:
2257                 goto abort_load;
2258         }
2259 
2260         gravity_map_insert(NULL, map, key, value);
2261     }
2262     return map;
2263 
2264 abort_load:
2265     // do not free map here because it is already garbage collected
2266     return NULL;
2267 }
2268 
gravity_map_size(gravity_vm * vm,gravity_map_t * map)2269 uint32_t gravity_map_size (gravity_vm *vm, gravity_map_t *map) {
2270     SET_OBJECT_VISITED_FLAG(map, true);
2271 
2272     uint32_t hash_size = 0;
2273     gravity_hash_iterate2(map->hash, gravity_hash_internalsize, (void *)&hash_size, (void *)vm);
2274     hash_size += gravity_hash_memsize(map->hash);
2275     hash_size += sizeof(gravity_map_t);
2276 
2277     SET_OBJECT_VISITED_FLAG(map, false);
2278     return hash_size;
2279 }
2280 
gravity_map_blacken(gravity_vm * vm,gravity_map_t * map)2281 void gravity_map_blacken (gravity_vm *vm, gravity_map_t *map) {
2282     gravity_vm_memupdate(vm, gravity_map_size(vm, map));
2283     gravity_hash_iterate(map->hash, gravity_hash_gray, (void *)vm);
2284 }
2285 
2286 // MARK: -
2287 
gravity_range_new(gravity_vm * vm,gravity_int_t from_range,gravity_int_t to_range,bool inclusive)2288 gravity_range_t *gravity_range_new (gravity_vm *vm, gravity_int_t from_range, gravity_int_t to_range, bool inclusive) {
2289     gravity_range_t *range = mem_alloc(NULL, sizeof(gravity_range_t));
2290 
2291     range->isa = gravity_class_range;
2292     range->from = from_range;
2293     range->to = (inclusive) ? to_range : --to_range;
2294 
2295     if (vm) gravity_vm_transfer(vm, (gravity_object_t*) range);
2296     return range;
2297 }
2298 
gravity_range_free(gravity_vm * vm,gravity_range_t * range)2299 void gravity_range_free (gravity_vm *vm, gravity_range_t *range) {
2300     #pragma unused(vm)
2301 
2302     DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)range, true));
2303     mem_free((void *)range);
2304 }
2305 
gravity_range_size(gravity_vm * vm,gravity_range_t * range)2306 uint32_t gravity_range_size (gravity_vm *vm, gravity_range_t *range) {
2307     #pragma unused(vm, range)
2308 
2309     SET_OBJECT_VISITED_FLAG(range, true);
2310     uint32_t range_size = sizeof(gravity_range_t);
2311     SET_OBJECT_VISITED_FLAG(range, false);
2312 
2313     return range_size;
2314 }
2315 
gravity_range_serialize(gravity_range_t * r,json_t * json)2316 void gravity_range_serialize (gravity_range_t *r, json_t *json) {
2317     const char *label = json_get_label(json, NULL);
2318     json_begin_object(json, label);
2319     json_add_cstring(json, GRAVITY_JSON_LABELTYPE, GRAVITY_JSON_RANGE);                    // MANDATORY 1st FIELD
2320     json_add_int(json, GRAVITY_JSON_LABELFROM, r->from);
2321     json_add_int(json, GRAVITY_JSON_LABELTO, r->to);
2322     json_end_object(json);
2323 }
2324 
gravity_range_deserialize(gravity_vm * vm,json_value * json)2325 gravity_range_t *gravity_range_deserialize (gravity_vm *vm, json_value *json) {
2326     json_int_t from = 0;
2327     json_int_t to = 0;
2328 
2329     uint32_t n = json->u.object.length;
2330     for (uint32_t i=1; i<n; ++i) { // from 1 to skip type
2331         const char *label = json->u.object.values[i].name;
2332         json_value *value = json->u.object.values[i].value;
2333         size_t label_size = strlen(label);
2334 
2335         // from
2336         if (string_casencmp(label, GRAVITY_JSON_LABELFROM, label_size) == 0) {
2337             if (value->type != json_integer) goto abort_load;
2338             from = value->u.integer;
2339             continue;
2340         }
2341 
2342         // to
2343         if (string_casencmp(label, GRAVITY_JSON_LABELTO, label_size) == 0) {
2344             if (value->type != json_integer) goto abort_load;
2345             to = value->u.integer;
2346             continue;
2347         }
2348     }
2349 
2350     return gravity_range_new(vm, (gravity_int_t)from, (gravity_int_t)to, true);
2351 
2352 abort_load:
2353     return NULL;
2354 }
2355 
gravity_range_blacken(gravity_vm * vm,gravity_range_t * range)2356 void gravity_range_blacken (gravity_vm *vm, gravity_range_t *range) {
2357     gravity_vm_memupdate(vm, gravity_range_size(vm, range));
2358 }
2359 
2360 // MARK: -
2361 
gravity_string_to_value(gravity_vm * vm,const char * s,uint32_t len)2362 inline gravity_value_t gravity_string_to_value (gravity_vm *vm, const char *s, uint32_t len) {
2363     gravity_string_t *obj = mem_alloc(NULL, sizeof(gravity_string_t));
2364     if (len == AUTOLENGTH) len = (uint32_t)strlen(s);
2365 
2366     uint32_t alloc = MAXNUM(len+1, DEFAULT_MINSTRING_SIZE);
2367     char *ptr = mem_alloc(NULL, alloc);
2368     memcpy(ptr, s, len);
2369 
2370     obj->isa = gravity_class_string;
2371     obj->s = ptr;
2372     obj->len = len;
2373     obj->alloc = alloc;
2374     obj->hash = gravity_hash_compute_buffer((const char *)ptr, len);
2375 
2376     gravity_value_t value;
2377     value.isa = gravity_class_string;
2378     value.p = (gravity_object_t *)obj;
2379 
2380     if (vm) gravity_vm_transfer(vm, (gravity_object_t*) obj);
2381     return value;
2382 }
2383 
gravity_string_new(gravity_vm * vm,char * s,uint32_t len,uint32_t alloc)2384 inline gravity_string_t *gravity_string_new (gravity_vm *vm, char *s, uint32_t len, uint32_t alloc) {
2385     gravity_string_t *obj = mem_alloc(NULL, sizeof(gravity_string_t));
2386     if (len == AUTOLENGTH) len = (uint32_t)strlen(s);
2387 
2388     obj->isa = gravity_class_string;
2389     obj->s = (char *)s;
2390     obj->len = len;
2391     obj->alloc = (alloc) ? alloc : len;
2392     if (s && len) obj->hash = gravity_hash_compute_buffer((const char *)s, len);
2393 
2394     if (vm) gravity_vm_transfer(vm, (gravity_object_t*) obj);
2395     return obj;
2396 }
2397 
gravity_string_set(gravity_string_t * obj,char * s,uint32_t len)2398 inline void gravity_string_set (gravity_string_t *obj, char *s, uint32_t len) {
2399     obj->s = (char *)s;
2400     obj->len = len;
2401     obj->hash = gravity_hash_compute_buffer((const char *)s, len);
2402 }
2403 
gravity_string_free(gravity_vm * vm,gravity_string_t * value)2404 inline void gravity_string_free (gravity_vm *vm, gravity_string_t *value) {
2405     #pragma unused(vm)
2406     DEBUG_FREE("FREE %s", gravity_object_debug((gravity_object_t *)value, true));
2407     if (value->alloc) mem_free(value->s);
2408     mem_free(value);
2409 }
2410 
gravity_string_size(gravity_vm * vm,gravity_string_t * string)2411 uint32_t gravity_string_size (gravity_vm *vm, gravity_string_t *string) {
2412     #pragma unused(vm)
2413     SET_OBJECT_VISITED_FLAG(string, true);
2414     uint32_t string_size = (sizeof(gravity_string_t)) + string->alloc;
2415     SET_OBJECT_VISITED_FLAG(string, false);
2416 
2417     return string_size;
2418 }
2419 
gravity_string_blacken(gravity_vm * vm,gravity_string_t * string)2420 void gravity_string_blacken (gravity_vm *vm, gravity_string_t *string) {
2421     gravity_vm_memupdate(vm, gravity_string_size(vm, string));
2422 }
2423 
gravity_value_from_error(const char * msg)2424 inline gravity_value_t gravity_value_from_error(const char* msg) {
2425     return ((gravity_value_t){.isa = NULL, .p = ((gravity_object_t *)msg)});
2426 }
2427 
gravity_value_from_object(void * obj)2428 inline gravity_value_t gravity_value_from_object(void *obj) {
2429     return ((gravity_value_t){.isa = (((gravity_object_t *)(obj))->isa), .p = (gravity_object_t *)(obj)});
2430 }
2431 
gravity_value_from_int(gravity_int_t n)2432 inline gravity_value_t gravity_value_from_int(gravity_int_t n) {
2433     return ((gravity_value_t){.isa = gravity_class_int, .n = (n)});
2434 }
2435 
gravity_value_from_float(gravity_float_t f)2436 inline gravity_value_t gravity_value_from_float(gravity_float_t f) {
2437     return ((gravity_value_t){.isa = gravity_class_float, .f = (f)});
2438 }
2439 
gravity_value_from_null(void)2440 inline gravity_value_t gravity_value_from_null(void) {
2441     return ((gravity_value_t){.isa = gravity_class_null, .n = 0});
2442 }
2443 
gravity_value_from_undefined(void)2444 inline gravity_value_t gravity_value_from_undefined(void) {
2445     return ((gravity_value_t){.isa = gravity_class_null, .n = 1});
2446 }
2447 
gravity_value_from_bool(bool b)2448 inline gravity_value_t gravity_value_from_bool(bool b) {
2449     return ((gravity_value_t){.isa = gravity_class_bool, .n = (b)});
2450 }
2451