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