Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epidemian
GitHub Repository: epidemian/gravity
Path: blob/master/src/runtime/gravity_vm.c
1218 views
1
//
2
// gravity_vm.c
3
// gravity
4
//
5
// Created by Marco Bambini on 11/11/14.
6
// Copyright (c) 2014 CreoLabs. All rights reserved.
7
//
8
9
#include "gravity_hash.h"
10
#include "gravity_array.h"
11
#include "gravity_debug.h"
12
#include "gravity_macros.h"
13
#include "gravity_vm.h"
14
#include "gravity_core.h"
15
#include "gravity_opcodes.h"
16
#include "gravity_memory.h"
17
#include "gravity_vmmacros.h"
18
#include "gravity_json.h"
19
20
// MARK: Internals -
21
static void gravity_gc_cleanup (gravity_vm *vm);
22
static void gravity_gc_transfer (gravity_vm *vm, gravity_object_t *obj);
23
static bool vm_set_superclass (gravity_vm *vm, gravity_object_t *obj);
24
static void gravity_gc_transform (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t *value, void *data);
25
26
// Internal cache to speed up common operations lookup
27
static uint32_t cache_refcount = 0;
28
static gravity_value_t cache[GRAVITY_VTABLE_SIZE];
29
30
// Opaque VM struct
31
struct gravity_vm {
32
gravity_hash_t *context; // context hash table
33
gravity_delegate_t *delegate; // registered runtime delegate
34
gravity_fiber_t *fiber; // current fiber
35
void *data; // custom data optionally set by the user
36
uint32_t pc; // program counter
37
double time; // useful timer for the main function
38
bool aborted; // set when VM has generated a runtime error
39
40
// anonymous names
41
uint32_t nanon; // counter for anonymous classes (used in object_bind)
42
char temp[64]; // temprary buffer used for anonymous names generator
43
44
// callbacks
45
vm_transfer_cb transfer; // function called each time a gravity_object_t is allocated
46
vm_cleanup_cb cleanup; // function called when VM must be cleaned-up
47
vm_filter_cb filter; // function called to filter objects in the cleanup process
48
49
// garbage collector
50
bool gcenabled; // flag to enable/disable garbage collector
51
gravity_int_t memallocated; // total number of allocated memory
52
gravity_object_t *gchead; // head of garbage collected objects
53
gravity_int_t gcminthreshold; // minimum GC threshold size to avoid spending too much time in GC
54
gravity_int_t gcthreshold; // memory required to trigger a GC
55
gravity_float_t gcratio; // ratio used in automatic recomputation of the new gcthreshold value
56
gravity_int_t gccount; // number of objects into GC
57
gravity_object_r graylist; // array of collected objects while GC is in process (gray list)
58
gravity_object_r gcsave; // array of temp objects that need to be saved from GC
59
60
// internal stats fields
61
#if GRAVITY_VM_STATS
62
uint32_t nfrealloc; // to check how many frames reallocation occurred
63
uint32_t nsrealloc; // to check how many stack reallocation occurred
64
uint32_t nstat[GRAVITY_LATEST_OPCODE]; // internal used to collect opcode usage stats
65
double tstat[GRAVITY_LATEST_OPCODE]; // internal used to collect microbenchmarks
66
nanotime_t t; // internal timer
67
#endif
68
};
69
70
// MARK: -
71
72
static void report_runtime_error (gravity_vm *vm, error_type_t error_type, const char *format, ...) {
73
char buffer[1024];
74
va_list arg;
75
76
if (vm->aborted) return;
77
vm->aborted = true;
78
79
if (format) {
80
va_start (arg, format);
81
vsnprintf(buffer, sizeof(buffer), format, arg);
82
va_end (arg);
83
}
84
85
gravity_error_callback errorf = NULL;
86
if (vm->delegate) errorf = ((gravity_delegate_t *)vm->delegate)->error_callback;
87
88
if (errorf) {
89
void *data = ((gravity_delegate_t *)vm->delegate)->xdata;
90
errorf(error_type, buffer, ERROR_DESC_NONE, data);
91
} else {
92
printf("%s\n", buffer);
93
fflush(stdout);
94
}
95
}
96
97
static void gravity_cache_setup (void) {
98
++cache_refcount;
99
100
// NULL here because I do not want them to be in the GC
101
mem_check(false);
102
cache[GRAVITY_NOTFOUND_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_INTERNAL_NOTFOUND_NAME);
103
cache[GRAVITY_ADD_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_ADD_NAME);
104
cache[GRAVITY_SUB_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_SUB_NAME);
105
cache[GRAVITY_DIV_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_DIV_NAME);
106
cache[GRAVITY_MUL_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_MUL_NAME);
107
cache[GRAVITY_REM_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_REM_NAME);
108
cache[GRAVITY_AND_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_AND_NAME);
109
cache[GRAVITY_OR_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_OR_NAME);
110
cache[GRAVITY_CMP_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_CMP_NAME);
111
cache[GRAVITY_EQQ_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_EQQ_NAME);
112
cache[GRAVITY_ISA_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_ISA_NAME);
113
cache[GRAVITY_MATCH_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_MATCH_NAME);
114
cache[GRAVITY_NEG_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_NEG_NAME);
115
cache[GRAVITY_NOT_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_NOT_NAME);
116
cache[GRAVITY_LSHIFT_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_LSHIFT_NAME);
117
cache[GRAVITY_RSHIFT_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_RSHIFT_NAME);
118
cache[GRAVITY_BAND_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_BAND_NAME);
119
cache[GRAVITY_BOR_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_BOR_NAME);
120
cache[GRAVITY_BXOR_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_BXOR_NAME);
121
cache[GRAVITY_BNOT_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_OPERATOR_BNOT_NAME);
122
cache[GRAVITY_LOAD_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_INTERNAL_LOAD_NAME);
123
cache[GRAVITY_LOADS_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_INTERNAL_LOADS_NAME);
124
cache[GRAVITY_LOADAT_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_INTERNAL_LOADAT_NAME);
125
cache[GRAVITY_STORE_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_INTERNAL_STORE_NAME);
126
cache[GRAVITY_STOREAT_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_INTERNAL_STOREAT_NAME);
127
cache[GRAVITY_INT_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_CLASS_INT_NAME);
128
cache[GRAVITY_FLOAT_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_CLASS_FLOAT_NAME);
129
cache[GRAVITY_BOOL_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_CLASS_BOOL_NAME);
130
cache[GRAVITY_STRING_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_CLASS_STRING_NAME);
131
cache[GRAVITY_EXEC_INDEX] = VALUE_FROM_CSTRING(NULL, GRAVITY_INTERNAL_EXEC_NAME);
132
mem_check(true);
133
}
134
135
static void gravity_cache_free (void) {
136
--cache_refcount;
137
if (cache_refcount > 0) return;
138
139
mem_check(false);
140
for (uint32_t index = 0; index <GRAVITY_VTABLE_SIZE; ++index) {
141
gravity_value_free(NULL, cache[index]);
142
}
143
mem_check(true);
144
}
145
146
gravity_value_t gravity_vm_keyindex (gravity_vm *vm, uint32_t index) {
147
#pragma unused (vm)
148
return cache[index];
149
}
150
151
#pragma clang diagnostic push
152
#pragma clang diagnostic ignored "-Wunused-function"
153
static void gravity_stack_dump (gravity_fiber_t *fiber) {
154
uint32_t index = 0;
155
for (gravity_value_t *stack = fiber->stack; stack < fiber->stacktop; ++stack) {
156
printf("[%05d]\t", index++);
157
if (!stack->isa) {printf("\n"); continue;}
158
gravity_value_dump(*stack, NULL, 0);
159
}
160
if (index) printf("\n\n");
161
}
162
#pragma clang diagnostic pop
163
164
static inline gravity_callframe_t *gravity_new_callframe (gravity_vm *vm, gravity_fiber_t *fiber) {
165
#pragma unused(vm)
166
167
// check if there are enought slots in the call frame and optionally create new cframes
168
if (fiber->framesalloc - fiber->nframes < 1) {
169
uint32_t new_size = fiber->framesalloc * 2;
170
fiber->frames = (gravity_callframe_t *) mem_realloc(fiber->frames, sizeof(gravity_callframe_t) * new_size);
171
fiber->framesalloc = new_size;
172
STAT_FRAMES_REALLOCATED(vm);
173
}
174
175
// update frames counter
176
++fiber->nframes;
177
178
// return first available cframe (-1 because I just updated the frames counter)
179
return &fiber->frames[fiber->nframes - 1];
180
}
181
182
static inline void gravity_check_stack (gravity_vm *vm, gravity_fiber_t *fiber, uint32_t rneeds, gravity_value_t **stackstart) {
183
#pragma unused(vm)
184
185
// update stacktop pointer before a call
186
fiber->stacktop += rneeds;
187
188
// check stack size
189
uint32_t stack_size = (uint32_t)(fiber->stacktop - fiber->stack);
190
uint32_t stack_needed = MAXNUM(stack_size + rneeds, DEFAULT_MINSTACK_SIZE);
191
if (fiber->stackalloc >= stack_needed) return;
192
193
// perform stack reallocation
194
uint32_t new_size = power_of2_ceil(fiber->stackalloc + stack_needed);
195
gravity_value_t *old_stack = fiber->stack;
196
fiber->stack = (gravity_value_t *) mem_realloc(fiber->stack, sizeof(gravity_value_t) * new_size);
197
fiber->stackalloc = new_size;
198
STAT_STACK_REALLOCATED(vm);
199
200
// check if reallocation moved the stack
201
if (fiber->stack == old_stack) return;
202
203
// re-compute ptr offset
204
ptrdiff_t offset = (ptrdiff_t)(fiber->stack - old_stack);
205
206
// adjust stack pointer for each call frame
207
for (uint32_t i=0; i < fiber->nframes; ++i) {
208
fiber->frames[i].stackstart += offset;
209
}
210
211
// adjust upvalues ptr offeset
212
gravity_upvalue_t* upvalue = fiber->upvalues;
213
while (upvalue) {
214
upvalue->value += offset;
215
upvalue = upvalue->next;
216
}
217
218
// adjust fiber stack pointer
219
fiber->stacktop += offset;
220
221
// stack is changed so update currently used stackstart
222
*stackstart += offset;
223
}
224
225
static gravity_upvalue_t *gravity_capture_upvalue (gravity_vm *vm, gravity_fiber_t *fiber, gravity_value_t *value) {
226
// closures and upvalues implementation inspired by Lua and Wren
227
// fiber->upvalues list must be ORDERED by the level of the corrisponding variables in the stack starting from top
228
229
// if upvalues is empty then create it
230
if (!fiber->upvalues) {
231
fiber->upvalues = gravity_upvalue_new(vm, value);
232
return fiber->upvalues;
233
}
234
235
// scan list looking for upvalue first (keeping track of the order)
236
gravity_upvalue_t *prevupvalue = NULL;
237
gravity_upvalue_t *upvalue = fiber->upvalues;
238
while (upvalue && upvalue->value > value) {
239
prevupvalue = upvalue;
240
upvalue = upvalue->next;
241
}
242
243
// if upvalue found then re-use it
244
if (upvalue != NULL && upvalue->value == value) return upvalue;
245
246
// upvalue not found in list so creates a new one and add it in list ORDERED
247
gravity_upvalue_t *newvalue = gravity_upvalue_new(vm, value);
248
if (prevupvalue == NULL) fiber->upvalues = newvalue;
249
else prevupvalue->next = newvalue;
250
251
// returns newly created upvalue
252
newvalue->next = upvalue;
253
return newvalue;
254
}
255
256
static void gravity_close_upvalues (gravity_fiber_t *fiber, gravity_value_t *level) {
257
while (fiber->upvalues != NULL && fiber->upvalues->value >= level) {
258
gravity_upvalue_t *upvalue = fiber->upvalues;
259
260
// move the value into the upvalue itself and point the upvalue to it
261
upvalue->closed = *upvalue->value;
262
upvalue->value = &upvalue->closed;
263
264
// remove it from the open upvalue list
265
fiber->upvalues = upvalue->next;
266
}
267
}
268
269
static void gravity_vm_loadclass (gravity_vm *vm, gravity_class_t *c) {
270
// convert func to closure for class and for its meta class
271
gravity_hash_transform(c->htable, gravity_gc_transform, (void *)vm);
272
gravity_class_t *meta = gravity_class_get_meta(c);
273
gravity_hash_transform(meta->htable, gravity_gc_transform, (void *)vm);
274
}
275
276
// MARK: -
277
278
static bool gravity_vm_exec (gravity_vm *vm) {
279
DECLARE_DISPATCH_TABLE;
280
281
gravity_fiber_t *fiber = vm->fiber; // current fiber
282
gravity_delegate_t *delegate = vm->delegate; // current delegate
283
gravity_callframe_t *frame; // current executing frame
284
gravity_function_t *func; // current executing function
285
gravity_value_t *stackstart; // SP => stack pointer
286
register uint32_t *ip; // IP => instruction pointer
287
register uint32_t inst; // IR => instruction register
288
register opcode_t op; // OP => opcode register
289
290
// load current callframe
291
LOAD_FRAME();
292
DEBUG_CALL("Executing", func);
293
294
// sanity check
295
if ((ip == NULL) || (!func->bytecode) || (func->ninsts == 0)) return true;
296
297
DEBUG_STACK();
298
299
while (1) {
300
INTERPRET_LOOP {
301
302
// MARK: - OPCODES -
303
// MARK: NOP
304
CASE_CODE(NOP): {
305
DEBUG_VM("NOP");
306
DISPATCH();
307
}
308
309
// MARK: MOVE
310
CASE_CODE(MOVE): {
311
OPCODE_GET_ONE8bit_ONE18bit(inst, const uint32_t r1, const uint32_t r2);
312
DEBUG_VM("MOVE %d %d", r1, r2);
313
314
SETVALUE(r1, STACK_GET(r2));
315
DISPATCH();
316
}
317
318
// MARK: LOAD
319
// MARK: LOADS
320
// MARK: LOADAT
321
CASE_CODE(LOAD):
322
CASE_CODE(LOADS):
323
CASE_CODE(LOADAT):{
324
OPCODE_GET_TWO8bit_ONE10bit(inst, const uint32_t r1, const uint32_t r2, const uint32_t r3);
325
DEBUG_VM("%s %d %d %d", (op == LOAD) ? "LOAD" : ((op == LOADAT) ? "LOADAT" : "LOADS"), r1, r2, r3);
326
327
// r1 result
328
// r2 target
329
// r3 key
330
331
DEFINE_STACK_VARIABLE(v2,r2);
332
DEFINE_INDEX_VARIABLE(v3,r3);
333
334
// prepare function call for binary operation
335
PREPARE_FUNC_CALL2(closure, v2, v3, (op == LOAD) ? GRAVITY_LOAD_INDEX : ((op == LOADAT) ? GRAVITY_LOADAT_INDEX : GRAVITY_LOADS_INDEX), rwin);
336
337
// call closure (do not use a macro here because we want to handle both the bridged and special cases)
338
STORE_FRAME();
339
execute_load_function:
340
switch(closure->f->tag) {
341
case EXEC_TYPE_NATIVE: {
342
PUSH_FRAME(closure, &stackstart[rwin], r1, 2);
343
} break;
344
345
case EXEC_TYPE_INTERNAL: {
346
SETVALUE(r1, VALUE_FROM_NULL);
347
if (!closure->f->internal(vm, &stackstart[rwin], 2, r1)) {
348
349
// check for special getter trick
350
if (VALUE_ISA_CLOSURE(STACK_GET(r1))) {
351
closure = VALUE_AS_CLOSURE(STACK_GET(r1));
352
SETVALUE(r1, VALUE_FROM_NULL);
353
goto execute_load_function;
354
}
355
356
// check for special fiber error
357
fiber = vm->fiber;
358
if (fiber == NULL) return true;
359
if (fiber->error) RUNTIME_FIBER_ERROR(fiber->error);
360
}
361
} break;
362
363
case EXEC_TYPE_BRIDGED: {
364
ASSERT(delegate->bridge_getvalue, "bridge_getvalue delegate callback is mandatory");
365
if (!delegate->bridge_getvalue(vm, closure->f->xdata, v2, VALUE_AS_CSTRING(v3), r1)) {
366
if (fiber->error) RUNTIME_FIBER_ERROR(fiber->error);
367
}
368
} break;
369
370
case EXEC_TYPE_SPECIAL: {
371
if (!closure->f->special[EXEC_TYPE_SPECIAL_GETTER]) RUNTIME_ERROR("Missing special getter function for property %s", VALUE_AS_CSTRING(v3));
372
closure = closure->f->special[EXEC_TYPE_SPECIAL_GETTER];
373
goto execute_load_function;
374
} break;
375
}
376
LOAD_FRAME();
377
SYNC_STACKTOP(closure, _rneed);
378
379
// continue execution
380
DISPATCH();
381
}
382
383
// MARK: LOADI
384
CASE_CODE(LOADI): {
385
OPCODE_GET_ONE8bit_SIGN_ONE17bit(inst, const uint32_t r1, const int32_t value);
386
DEBUG_VM("LOADI %d %d", r1, value);
387
388
SETVALUE_INT(r1, value);
389
DISPATCH();
390
}
391
392
// MARK: LOADK
393
CASE_CODE(LOADK): {
394
OPCODE_GET_ONE8bit_ONE18bit(inst, const uint32_t r1, const uint32_t index);
395
DEBUG_VM("LOADK %d %d", r1, index);
396
397
// constant pool case
398
if (index < CPOOL_INDEX_MAX) {
399
gravity_value_t v = gravity_function_cpool_get(func, index);
400
SETVALUE(r1, v);
401
DISPATCH();
402
}
403
404
// special value case
405
switch (index) {
406
case CPOOL_VALUE_SUPER: {
407
// get super class from STACK_GET(0) which is self
408
gravity_class_t *super = gravity_value_getsuper(STACK_GET(0));
409
SETVALUE(r1, (super) ? VALUE_FROM_OBJECT(super) : VALUE_FROM_NULL);
410
} break;
411
case CPOOL_VALUE_ARGUMENTS: SETVALUE(r1, VALUE_FROM_OBJECT(frame->args)); break;
412
case CPOOL_VALUE_NULL: SETVALUE(r1, VALUE_FROM_NULL); break;
413
case CPOOL_VALUE_UNDEFINED: SETVALUE(r1, VALUE_FROM_UNDEFINED); break;
414
case CPOOL_VALUE_TRUE: SETVALUE(r1, VALUE_FROM_TRUE); break;
415
case CPOOL_VALUE_FALSE: SETVALUE(r1, VALUE_FROM_FALSE); break;
416
case CPOOL_VALUE_FUNC: SETVALUE(r1, VALUE_FROM_OBJECT(frame->closure)); break;
417
default: RUNTIME_ERROR("Unknown LOADK index"); break;
418
}
419
DISPATCH();
420
}
421
422
// MARK: LOADG
423
CASE_CODE(LOADG): {
424
OPCODE_GET_ONE8bit_ONE18bit(inst, uint32_t r1, int32_t index);
425
DEBUG_VM("LOADG %d %d", r1, index);
426
427
gravity_value_t key = gravity_function_cpool_get(func, index);
428
gravity_value_t *v = gravity_hash_lookup(vm->context, key);
429
if (!v) RUNTIME_ERROR("Unable to find object %s", VALUE_AS_CSTRING(key));
430
431
SETVALUE(r1, *v);
432
DISPATCH();
433
}
434
435
// MARK: LOADU
436
CASE_CODE(LOADU): {
437
OPCODE_GET_ONE8bit_ONE18bit(inst, const uint32_t r1, const uint32_t r2);
438
DEBUG_VM("LOADU %d %d", r1, r2);
439
440
gravity_upvalue_t *upvalue = frame->closure->upvalue[r2];
441
SETVALUE(r1, *upvalue->value);
442
DISPATCH();
443
}
444
445
// MARK: STORE
446
// MARK: STOREAT
447
CASE_CODE(STORE):
448
CASE_CODE(STOREAT):{
449
OPCODE_GET_TWO8bit_ONE10bit(inst, const uint32_t r1, const uint32_t r2, const uint32_t r3);
450
DEBUG_VM("%s %d %d %d", (op == STORE) ? "STORE" : "STOREAT", r1, r2,r3);
451
452
// r1 value
453
// r2 target
454
// r3 key
455
456
DEFINE_STACK_VARIABLE(v1,r1);
457
DEFINE_STACK_VARIABLE(v2,r2);
458
DEFINE_INDEX_VARIABLE(v3,r3);
459
460
// prepare function call
461
PREPARE_FUNC_CALL3(closure, v2, v3, v1, (op == STORE) ? GRAVITY_STORE_INDEX : GRAVITY_STOREAT_INDEX, rwin);
462
463
// call function f (do not use a macro here because we want to handle both the bridged and special cases)
464
STORE_FRAME();
465
execute_store_function:
466
switch(closure->f->tag) {
467
case EXEC_TYPE_NATIVE: {
468
SETVALUE(rwin+1, v1);
469
PUSH_FRAME(closure, &stackstart[rwin], r1, 2);
470
} break;
471
472
case EXEC_TYPE_INTERNAL: {
473
SETVALUE(r1, VALUE_FROM_NULL);
474
if (!closure->f->internal(vm, &stackstart[rwin], 2, r1)) {
475
// check for special getter trick
476
if (VALUE_ISA_CLOSURE(STACK_GET(r1))) {
477
closure = VALUE_AS_CLOSURE(STACK_GET(r1));
478
SETVALUE(r1, VALUE_FROM_NULL);
479
goto execute_store_function;
480
}
481
482
// check for special fiber error
483
fiber = vm->fiber;
484
if (fiber == NULL) return true;
485
if (fiber->error) RUNTIME_FIBER_ERROR(fiber->error);
486
}
487
} break;
488
489
case EXEC_TYPE_BRIDGED: {
490
ASSERT(delegate->bridge_setvalue, "bridge_setvalue delegate callback is mandatory");
491
if (!delegate->bridge_setvalue(vm, closure->f->xdata, v2, VALUE_AS_CSTRING(v3), v1)) {
492
if (fiber->error) RUNTIME_FIBER_ERROR(fiber->error);
493
}
494
} break;
495
496
case EXEC_TYPE_SPECIAL: {
497
if (!closure->f->special[EXEC_TYPE_SPECIAL_SETTER]) RUNTIME_ERROR("Missing special setter function for property %s", VALUE_AS_CSTRING(v3));
498
closure = closure->f->special[EXEC_TYPE_SPECIAL_SETTER];
499
goto execute_store_function;
500
} break;
501
}
502
LOAD_FRAME();
503
SYNC_STACKTOP(closure, _rneed);
504
505
// continue execution
506
DISPATCH();
507
}
508
509
// MARK: STOREG
510
CASE_CODE(STOREG): {
511
OPCODE_GET_ONE8bit_ONE18bit(inst, uint32_t r1, int32_t index);
512
DEBUG_VM("STOREG %d %d", r1, index);
513
514
gravity_value_t key = gravity_function_cpool_get(func, index);
515
gravity_value_t v = STACK_GET(r1);
516
gravity_hash_insert(vm->context, key, v);
517
DISPATCH();
518
}
519
520
// MARK: STOREU
521
CASE_CODE(STOREU): {
522
OPCODE_GET_ONE8bit_ONE18bit(inst, const uint32_t r1, const uint32_t r2);
523
DEBUG_VM("STOREU %d %d", r1, r2);
524
525
gravity_upvalue_t *upvalue = frame->closure->upvalue[r2];
526
*upvalue->value = STACK_GET(r1);
527
DISPATCH();
528
}
529
530
// MARK: - EQUALITY
531
CASE_CODE(EQQ):
532
CASE_CODE(NEQQ): {
533
// decode operation
534
DECODE_BINARY_OPERATION(r1,r2,r3);
535
536
// get registers
537
DEFINE_STACK_VARIABLE(v2,r2);
538
DEFINE_STACK_VARIABLE(v3,r3);
539
540
// prepare function call for binary operation
541
PREPARE_FUNC_CALL2(closure, v2, v3, GRAVITY_EQQ_INDEX, rwin);
542
543
// call function f
544
CALL_FUNC(EQQ, closure, r1, 2, rwin);
545
546
// get result of the EQQ selector
547
register gravity_int_t result = STACK_GET(r1).n;
548
549
// save result (NEQQ is not EQQ)
550
SETVALUE_BOOL(r1, (op == EQQ) ? result : !result);
551
552
DISPATCH();
553
}
554
555
CASE_CODE(ISA):
556
CASE_CODE(MATCH): {
557
// decode operation
558
DECODE_BINARY_OPERATION(r1, r2, r3);
559
560
// get registers
561
DEFINE_STACK_VARIABLE(v2,r2);
562
DEFINE_STACK_VARIABLE(v3,r3);
563
564
// prepare function call for binary operation
565
PREPARE_FUNC_CALL2(closure, v2, v3, (op == ISA) ? GRAVITY_ISA_INDEX : GRAVITY_MATCH_INDEX, rwin);
566
567
// call function f
568
CALL_FUNC(ISA, closure, r1, 2, rwin);
569
570
// continue execution
571
DISPATCH();
572
}
573
574
// MARK: COMPARISON
575
CASE_CODE(LT):
576
CASE_CODE(GT):
577
CASE_CODE(EQ):
578
CASE_CODE(LEQ):
579
CASE_CODE(GEQ):
580
CASE_CODE(NEQ): {
581
582
// decode operation
583
DECODE_BINARY_OPERATION(r1,r2,r3);
584
585
// check fast comparison only if both values are boolean OR if one of them is undefined
586
DEFINE_STACK_VARIABLE(v2,r2);
587
DEFINE_STACK_VARIABLE(v3,r3);
588
if ((VALUE_ISA_BOOL(v2) && (VALUE_ISA_BOOL(v3))) || (VALUE_ISA_UNDEFINED(v2) || (VALUE_ISA_UNDEFINED(v3)))) {
589
register gravity_int_t eq_result = (v2.isa == v3.isa) && (v2.n == v3.n);
590
SETVALUE(r1, VALUE_FROM_BOOL((op == EQ) ? eq_result : !eq_result));
591
DISPATCH();
592
} else if (VALUE_ISA_INT(v2) && VALUE_ISA_INT(v3)) {
593
// INT optimization expecially useful in loops
594
if (v2.n == v3.n) SETVALUE(r1, VALUE_FROM_INT(0));
595
else SETVALUE(r1, VALUE_FROM_INT((v2.n > v3.n) ? 1 : -1));
596
} else {
597
// prepare function call for binary operation
598
PREPARE_FUNC_CALL2(closure, v2, v3, GRAVITY_CMP_INDEX, rwin);
599
600
// call function f
601
CALL_FUNC(CMP, closure, r1, 2, rwin);
602
}
603
604
// compare returns 0 if v1 and v2 are equals, 1 if v1>v2 and -1 if v1<v2
605
register gravity_int_t result = STACK_GET(r1).n;
606
607
switch(op) {
608
case LT: SETVALUE_BOOL(r1, result < 0); break;
609
case GT: SETVALUE_BOOL(r1, result > 0); break;
610
case EQ: SETVALUE_BOOL(r1, result == 0); break;
611
case LEQ: SETVALUE_BOOL(r1, result <= 0); break;
612
case GEQ: SETVALUE_BOOL(r1, result >= 0); break;
613
case NEQ: SETVALUE_BOOL(r1, result != 0); break;
614
default: assert(0);
615
}
616
617
// optimize the case where after a comparison there is a JUMPF instruction (usually in a loop)
618
uint32_t inext = *ip++;
619
if ((STACK_GET(r1).n == 0) && (OPCODE_GET_OPCODE(inext) == JUMPF)) {
620
OPCODE_GET_LAST18bit(inext, int32_t value);
621
DEBUG_VM("JUMPF %d %d", (int)result, value);
622
ip = COMPUTE_JUMP(value); // JUMP is an absolute value
623
DISPATCH();
624
}
625
626
// JUMPF not executed so I need to go back in instruction pointer
627
--ip;
628
629
// continue execution
630
DISPATCH();
631
}
632
633
// MARK: - BIT OPERATORS -
634
// MARK: LSHIFT
635
CASE_CODE(LSHIFT): {
636
// decode operation
637
DECODE_BINARY_OPERATION(r1, r2, r3);
638
639
// check fast bit math operation first (only if both v2 and v3 are int)
640
CHECK_FAST_BINARY_BIT(r1, r2, r3, v2, v3, <<);
641
642
// prepare function call for binary operation
643
PREPARE_FUNC_CALL2(closure, v2, v3, GRAVITY_LSHIFT_INDEX, rwin);
644
645
// call function f
646
CALL_FUNC(LSHIFT, closure, r1, 2, rwin);
647
648
// continue execution
649
DISPATCH();
650
}
651
652
// MARK: RSHIFT
653
CASE_CODE(RSHIFT): {
654
// decode operation
655
DECODE_BINARY_OPERATION(r1, r2, r3);
656
657
// check fast bit math operation first (only if both v2 and v3 are int)
658
CHECK_FAST_BINARY_BIT(r1, r2, r3, v2, v3, >>);
659
660
// prepare function call for binary operation
661
PREPARE_FUNC_CALL2(closure, v2, v3, GRAVITY_RSHIFT_INDEX, rwin);
662
663
// call function f
664
CALL_FUNC(RSHIFT, closure, r1, 2, rwin);
665
666
// continue execution
667
DISPATCH();
668
}
669
670
// MARK: BAND
671
CASE_CODE(BAND): {
672
// decode operation
673
DECODE_BINARY_OPERATION(r1, r2, r3);
674
675
// check fast bit math operation first (only if both v2 and v3 are int)
676
CHECK_FAST_BINARY_BIT(r1, r2, r3, v2, v3, &);
677
678
// prepare function call for binary operation
679
PREPARE_FUNC_CALL2(closure, v2, v3, GRAVITY_BAND_INDEX, rwin);
680
681
// call function f
682
CALL_FUNC(BAND, closure, r1, 2, rwin);
683
684
// continue execution
685
DISPATCH();
686
}
687
688
// MARK: BOR
689
CASE_CODE(BOR): {
690
// decode operation
691
DECODE_BINARY_OPERATION(r1, r2, r3);
692
693
// check fast bit math operation first (only if both v2 and v3 are int)
694
CHECK_FAST_BINARY_BIT(r1, r2, r3, v2, v3, |);
695
696
// prepare function call for binary operation
697
PREPARE_FUNC_CALL2(closure, v2, v3, GRAVITY_BOR_INDEX, rwin);
698
699
// call function f
700
CALL_FUNC(BOR, closure, r1, 2, rwin);
701
702
// continue execution
703
DISPATCH();
704
}
705
706
// MARK: BXOR
707
CASE_CODE(BXOR):{
708
// decode operation
709
DECODE_BINARY_OPERATION(r1, r2, r3);
710
711
// check fast bit math operation first (only if both v2 and v3 are int)
712
CHECK_FAST_BINARY_BIT(r1, r2, r3, v2, v3, ^);
713
714
// prepare function call for binary operation
715
PREPARE_FUNC_CALL2(closure, v2, v3, GRAVITY_BXOR_INDEX, rwin);
716
717
// call function f
718
CALL_FUNC(BXOR, closure, r1, 2, rwin);
719
720
// continue execution
721
DISPATCH();
722
}
723
724
// MARK: - BINARY OPERATORS -
725
// MARK: ADD
726
CASE_CODE(ADD): {
727
// decode operation
728
DECODE_BINARY_OPERATION(r1, r2, r3);
729
730
// check fast math operation first (only in case of int and float)
731
CHECK_FAST_BINARY_MATH(r1, r2, r3, v2, v3, +, NO_CHECK);
732
733
// fast math operation cannot be performed so let's try with a regular call
734
// prepare function call for binary operation
735
PREPARE_FUNC_CALL2(closure, v2, v3, GRAVITY_ADD_INDEX, rwin);
736
737
// call function f
738
CALL_FUNC(ADD, closure, r1, 2, rwin);
739
740
// continue execution
741
DISPATCH();
742
}
743
744
// MARK: SUB
745
CASE_CODE(SUB): {
746
// decode operation
747
DECODE_BINARY_OPERATION(r1, r2, r3);
748
749
// check fast math operation first (only in case of int and float)
750
CHECK_FAST_BINARY_MATH(r1, r2, r3, v2, v3, -, NO_CHECK);
751
752
// prepare function call for binary operation
753
PREPARE_FUNC_CALL2(closure, v2, v3, GRAVITY_SUB_INDEX, rwin);
754
755
// call function f
756
CALL_FUNC(SUB, closure, r1, 2, rwin);
757
758
// continue execution
759
DISPATCH();
760
}
761
762
// MARK: DIV
763
CASE_CODE(DIV): {
764
// decode operation
765
DECODE_BINARY_OPERATION(r1, r2, r3);
766
767
// check fast math operation first (only in case of int and float)
768
// a special check macro is added in order to check for divide by zero cases
769
CHECK_FAST_BINARY_MATH(r1, r2, r3, v2, v3, /, CHECK_ZERO(v3));
770
771
// prepare function call for binary operation
772
PREPARE_FUNC_CALL2(closure, v2, v3, GRAVITY_DIV_INDEX, rwin);
773
774
// call function f
775
CALL_FUNC(DIV, closure, r1, 2, rwin);
776
777
// continue execution
778
DISPATCH();
779
}
780
781
// MARK: MUL
782
CASE_CODE(MUL): {
783
// decode operation
784
DECODE_BINARY_OPERATION(r1, r2, r3);
785
786
// check fast math operation first (only in case of int and float)
787
CHECK_FAST_BINARY_MATH(r1, r2, r3, v2, v3, *, NO_CHECK);
788
789
// prepare function call for binary operation
790
PREPARE_FUNC_CALL2(closure, v2, v3, GRAVITY_MUL_INDEX, rwin);
791
792
// call function f
793
CALL_FUNC(MUL, closure, r1, 2, rwin);
794
795
// continue execution
796
DISPATCH();
797
}
798
799
// MARK: REM
800
CASE_CODE(REM): {
801
// decode operation
802
DECODE_BINARY_OPERATION(r1, r2, r3);
803
804
// check fast math operation first (only in case of int and float)
805
CHECK_FAST_BINARY_REM(r1, r2, r3, v2, v3);
806
807
// prepare function call for binary operation
808
PREPARE_FUNC_CALL2(closure, v2, v3, GRAVITY_REM_INDEX, rwin);
809
810
// call function f
811
CALL_FUNC(REM, closure, r1, 2, rwin);
812
813
// continue execution
814
DISPATCH();
815
}
816
817
// MARK: AND
818
CASE_CODE(AND): {
819
// decode operation
820
DECODE_BINARY_OPERATION(r1, r2, r3);
821
822
// check fast bool operation first (only if both are bool)
823
CHECK_FAST_BINARY_BOOL(r1, r2, r3, v2, v3, &&);
824
825
// prepare function call for binary operation
826
PREPARE_FUNC_CALL2(closure, v2, v3, GRAVITY_AND_INDEX, rwin);
827
828
// call function f
829
CALL_FUNC(AND, closure, r1, 2, rwin);
830
831
// continue execution
832
DISPATCH();
833
}
834
835
// MARK: OR
836
CASE_CODE(OR): {
837
// decode operation
838
DECODE_BINARY_OPERATION(r1, r2, r3);
839
840
// check fast bool operation first (only if both are bool)
841
CHECK_FAST_BINARY_BOOL(r1, r2, r3, v2, v3, ||);
842
843
// prepare function call for binary operation
844
PREPARE_FUNC_CALL2(closure, v2, v3, GRAVITY_OR_INDEX, rwin);
845
846
// call function f
847
CALL_FUNC(OR, closure, r1, 2, rwin);
848
849
// continue execution
850
DISPATCH();
851
}
852
853
// MARK: - UNARY OPERATORS -
854
// MARK: NEG
855
CASE_CODE(NEG): {
856
// decode operation
857
DECODE_BINARY_OPERATION(r1, r2, r3);
858
#pragma unused(r3)
859
860
// check fast bool operation first (only if it is int or float)
861
CHECK_FAST_UNARY_MATH(r1, r2, v2, -);
862
863
// prepare function call for binary operation
864
PREPARE_FUNC_CALL1(closure, v2, GRAVITY_NEG_INDEX, rwin);
865
866
// call function f
867
CALL_FUNC(NEG, closure, r1, 1, rwin);
868
869
// continue execution
870
DISPATCH();
871
}
872
873
// MARK: NOT
874
CASE_CODE(NOT): {
875
// decode operation
876
DECODE_BINARY_OPERATION(r1, r2, r3);
877
#pragma unused(r3)
878
879
// check fast bool operation first (only if it is bool)
880
CHECK_FAST_UNARY_BOOL(r1, r2, v2, !);
881
882
// prepare function call for binary operation
883
PREPARE_FUNC_CALL1(closure, v2, GRAVITY_NOT_INDEX, rwin);
884
885
// call function f
886
CALL_FUNC(NOT, closure, r1, 1, rwin);
887
888
// continue execution
889
DISPATCH();
890
}
891
892
// MARK: BNOT
893
CASE_CODE(BNOT): {
894
// decode operation
895
DECODE_BINARY_OPERATION(r1, r2, r3);
896
#pragma unused(r3)
897
898
// check fast int operation first
899
DEFINE_STACK_VARIABLE(v2,r2);
900
// ~v2.n == -v2.n-1
901
if (VALUE_ISA_INT(v2)) {SETVALUE(r1, VALUE_FROM_INT(~v2.n)); DISPATCH();}
902
903
// prepare function call for binary operation
904
PREPARE_FUNC_CALL1(closure, v2, GRAVITY_BNOT_INDEX, rwin);
905
906
// call function f
907
CALL_FUNC(BNOT, closure, r1, 1, rwin);
908
909
// continue execution
910
DISPATCH();
911
}
912
913
// MARK: -
914
// MARK: JUMPF
915
CASE_CODE(JUMPF): {
916
// JUMPF like JUMP is an absolute value
917
OPCODE_GET_ONE8bit_FLAG_ONE17bit(inst, const uint32_t r1, const uint32_t flag, const int32_t value);
918
DEBUG_VM("JUMPF %d %d (flag %d)", r1, value, flag);
919
920
if (flag) {
921
// if flag is set then ONLY boolean values must be checked (compiler must guarantee this condition)
922
// this is necessary in a FOR loop over numeric values (with an iterator) where otherwise number 0
923
// could be computed as a false condition
924
if ((VALUE_ISA_BOOL(STACK_GET(r1))) && (GETVALUE_INT(STACK_GET(r1)) == 0)) ip = COMPUTE_JUMP(value);
925
DISPATCH();
926
}
927
928
// no flag set so convert r1 to BOOL
929
DEFINE_STACK_VARIABLE(v1, r1);
930
931
// common NULL/UNDEFINED/BOOL/INT/FLOAT/STRING cases
932
// NULL/UNDEFINED => no check
933
// BOOL/INT => check n
934
// FLOAT => check f
935
// STRING => check len
936
if (VALUE_ISA_NULL(v1) || (VALUE_ISA_UNDEFINED(v1))) {ip = COMPUTE_JUMP(value);}
937
else if (VALUE_ISA_BOOL(v1) || (VALUE_ISA_INT(v1))) {if (GETVALUE_INT(v1) == 0) ip = COMPUTE_JUMP(value);}
938
else if (VALUE_ISA_FLOAT(v1)) {if (GETVALUE_FLOAT(v1) == 0.0) ip = COMPUTE_JUMP(value);}
939
else if (VALUE_ISA_STRING(v1)) {if (VALUE_AS_STRING(v1)->len == 0) ip = COMPUTE_JUMP(value);}
940
else {
941
// no common case so check if it implements the Bool command
942
gravity_closure_t *closure = (gravity_closure_t *)gravity_class_lookup_closure(gravity_value_getclass(v1), cache[GRAVITY_BOOL_INDEX]);
943
944
// if no closure is found then object is considered TRUE
945
if (closure) {
946
// prepare func call
947
uint32_t rwin = FN_COUNTREG(func, frame->nargs);
948
uint32_t _rneed = FN_COUNTREG(closure->f, 1);
949
gravity_check_stack(vm, fiber, _rneed, &stackstart);
950
SETVALUE(rwin, v1);
951
952
// call func and check result
953
CALL_FUNC(JUMPF, closure, rwin, 2, rwin);
954
gravity_int_t result = STACK_GET(rwin).n;
955
956
// re-compute ip ONLY if false
957
if (!result) ip = COMPUTE_JUMP(value);
958
}
959
}
960
DISPATCH();
961
}
962
963
// MARK: JUMP
964
CASE_CODE(JUMP): {
965
OPCODE_GET_ONE26bit(inst, const uint32_t value);
966
DEBUG_VM("JUMP %d", value);
967
968
ip = COMPUTE_JUMP(value); // JUMP is an absolute value
969
DISPATCH();
970
}
971
972
// MARK: CALL
973
CASE_CODE(CALL): {
974
// CALL A B C => R(A) = B(B+1...B+C)
975
OPCODE_GET_THREE8bit(inst, const uint32_t r1, const uint32_t r2, register uint32_t r3);
976
DEBUG_VM("CALL %d %d %d", r1, r2, r3);
977
978
DEBUG_STACK();
979
980
// r1 is the destination register
981
// r2 is the register which contains the callable object
982
// r3 is the number of arguments (nparams)
983
984
// register window
985
const uint32_t rwin = r2 + 1;
986
987
// object to call
988
gravity_value_t v = STACK_GET(r2);
989
990
// closure to call
991
gravity_closure_t *closure = NULL;
992
993
if (VALUE_ISA_CLOSURE(v)) {
994
closure = VALUE_AS_CLOSURE(v);
995
} else {
996
// check for exec closure inside object
997
closure = (gravity_closure_t *)gravity_class_lookup_closure(gravity_value_getclass(v), cache[GRAVITY_EXEC_INDEX]);
998
}
999
1000
// sanity check
1001
if (!closure) RUNTIME_ERROR("Unable to call object (in function %s)", func->identifier);
1002
1003
// check stack size
1004
uint32_t _rneed = FN_COUNTREG(closure->f, r3);
1005
gravity_check_stack(vm, fiber, _rneed, &stackstart);
1006
1007
// if less arguments are passed then fill the holes with UNDEFINED values
1008
while (r3 < closure->f->nparams) {
1009
SETVALUE(rwin+r3, VALUE_FROM_UNDEFINED);
1010
++r3;
1011
}
1012
1013
DEBUG_STACK();
1014
1015
// execute function
1016
STORE_FRAME();
1017
execute_call_function:
1018
switch(closure->f->tag) {
1019
case EXEC_TYPE_NATIVE: {
1020
PUSH_FRAME(closure, &stackstart[rwin], r1, r3);
1021
} break;
1022
1023
case EXEC_TYPE_INTERNAL: {
1024
SETVALUE(r1, VALUE_FROM_NULL);
1025
if (!closure->f->internal(vm, &stackstart[rwin], r3, r1)) {
1026
// check for special getter trick
1027
if (VALUE_ISA_CLOSURE(STACK_GET(r1))) {
1028
closure = VALUE_AS_CLOSURE(STACK_GET(r1));
1029
SETVALUE(r1, VALUE_FROM_NULL);
1030
goto execute_call_function;
1031
}
1032
1033
// check for special fiber error
1034
fiber = vm->fiber;
1035
if (fiber == NULL) return true;
1036
if (fiber->error) RUNTIME_FIBER_ERROR(fiber->error);
1037
}
1038
} break;
1039
1040
case EXEC_TYPE_BRIDGED: {
1041
bool result;
1042
if (VALUE_ISA_CLASS(v)) {
1043
ASSERT(delegate->bridge_initinstance, "bridge_initinstance delegate callback is mandatory");
1044
gravity_instance_t *instance = (gravity_instance_t *)VALUE_AS_OBJECT(stackstart[rwin]);
1045
result = delegate->bridge_initinstance(vm, closure->f->xdata, instance, &stackstart[rwin], r3);
1046
SETVALUE(r1, VALUE_FROM_OBJECT(instance));
1047
} else {
1048
ASSERT(delegate->bridge_execute, "bridge_execute delegate callback is mandatory");
1049
result = delegate->bridge_execute(vm, closure->f->xdata, &stackstart[rwin], r3, r1);
1050
}
1051
if (!result && fiber->error) RUNTIME_FIBER_ERROR(fiber->error);
1052
} break;
1053
1054
case EXEC_TYPE_SPECIAL:
1055
RUNTIME_ERROR("Unable to handle a special function in current context");
1056
break;
1057
}
1058
LOAD_FRAME();
1059
SYNC_STACKTOP(closure, _rneed);
1060
1061
DISPATCH();
1062
}
1063
1064
// MARK: RET
1065
// MARK: RET0
1066
CASE_CODE(RET0):
1067
CASE_CODE(RET): {
1068
gravity_value_t result;
1069
1070
if (op == RET0) {
1071
DEBUG_VM("RET0");
1072
result = VALUE_FROM_NULL;
1073
} else {
1074
OPCODE_GET_ONE8bit(inst, const uint32_t r1);
1075
DEBUG_VM("RET %d", r1);
1076
result = STACK_GET(r1);
1077
}
1078
1079
// sanity check
1080
ASSERT(fiber->nframes > 0, "Number of active frames cannot be 0.");
1081
1082
// POP frame
1083
--fiber->nframes;
1084
1085
// close open upvalues (if any)
1086
gravity_close_upvalues(fiber, stackstart);
1087
1088
// check if it was a gravity_vm_runfunc execution
1089
if (frame->outloop) {
1090
fiber->result = result;
1091
return true;
1092
}
1093
1094
// retrieve destination register
1095
uint32_t dest = fiber->frames[fiber->nframes].dest;
1096
if (fiber->nframes == 0) {
1097
if (fiber->caller == NULL) {fiber->result = result; return true;}
1098
fiber = fiber->caller;
1099
vm->fiber = fiber;
1100
} else {
1101
fiber->stacktop = frame->stackstart;
1102
}
1103
1104
LOAD_FRAME();
1105
DEBUG_CALL("Resuming", func);
1106
SETVALUE(dest, result);
1107
1108
DISPATCH();
1109
}
1110
1111
// MARK: HALT
1112
CASE_CODE(HALT): {
1113
DEBUG_VM("HALT");
1114
return true;
1115
}
1116
1117
// MARK: SWITCH
1118
CASE_CODE(SWITCH): {
1119
ASSERT(0, "To be implemented");
1120
DISPATCH();
1121
}
1122
1123
// MARK: -
1124
// MARK: MAPNEW
1125
CASE_CODE(MAPNEW): {
1126
OPCODE_GET_ONE8bit_ONE18bit(inst, const uint32_t r1, const uint32_t n);
1127
DEBUG_VM("MAPNEW %d %d", r1, n);
1128
1129
gravity_map_t *map = gravity_map_new(vm, n);
1130
SETVALUE(r1, VALUE_FROM_OBJECT(map));
1131
DISPATCH();
1132
}
1133
1134
// MARK: LISTNEW
1135
CASE_CODE(LISTNEW): {
1136
OPCODE_GET_ONE8bit_ONE18bit(inst, const uint32_t r1, const uint32_t n);
1137
DEBUG_VM("LISTNEW %d %d", r1, n);
1138
1139
gravity_list_t *list = gravity_list_new(vm, n);
1140
SETVALUE(r1, VALUE_FROM_OBJECT(list));
1141
DISPATCH();
1142
}
1143
1144
// MARK: RANGENEW
1145
CASE_CODE(RANGENEW): {
1146
OPCODE_GET_THREE8bit_ONE2bit(inst, const uint32_t r1, const uint32_t r2, const uint32_t r3, const bool flag);
1147
DEBUG_VM("RANGENEW %d %d %d (flag %d)", r1, r2, r3, flag);
1148
1149
// sanity check
1150
if ((!VALUE_ISA_INT(STACK_GET(r2))) || (!VALUE_ISA_INT(STACK_GET(r3))))
1151
RUNTIME_ERROR("Unable to build Range from a non Int value");
1152
1153
gravity_range_t *range = gravity_range_new(vm, VALUE_AS_INT(STACK_GET(r2)), VALUE_AS_INT(STACK_GET(r3)), !flag);
1154
SETVALUE(r1, VALUE_FROM_OBJECT(range));
1155
DISPATCH();
1156
}
1157
1158
// MARK: SETLIST
1159
CASE_CODE(SETLIST): {
1160
OPCODE_GET_TWO8bit_ONE10bit(inst, uint32_t r1, uint32_t r2, const uint32_t r3);
1161
DEBUG_VM("SETLIST %d %d", r1, r2);
1162
1163
// since this code is produced by the compiler I can trust the fact that if v1 is not a map
1164
// then it is an array and nothing else
1165
gravity_value_t v1 = STACK_GET(r1);
1166
bool v1_is_map = VALUE_ISA_MAP(v1);
1167
1168
// r2 == 0 is an optimization, it means that list/map is composed all of literals
1169
// and can be filled using the constant pool (r3 in this case represents an index inside cpool)
1170
if (r2 == 0) {
1171
gravity_value_t v2 = gravity_function_cpool_get(func, r3);
1172
if (v1_is_map) {
1173
gravity_map_t *map = VALUE_AS_MAP(v1);
1174
gravity_map_append_map(vm, map, VALUE_AS_MAP(v2));
1175
} else {
1176
gravity_list_t *list = VALUE_AS_LIST(v1);
1177
gravity_list_append_list(vm, list, VALUE_AS_LIST(v2));
1178
}
1179
DISPATCH();
1180
}
1181
1182
if (v1_is_map) {
1183
gravity_map_t *map = VALUE_AS_MAP(v1);
1184
while (r2) {
1185
gravity_value_t key = STACK_GET(++r1);
1186
gravity_value_t value = STACK_GET(++r1);
1187
gravity_hash_insert(map->hash, key, value);
1188
--r2;
1189
}
1190
} else {
1191
gravity_list_t *list = VALUE_AS_LIST(v1);
1192
while (r2) {
1193
marray_push(gravity_value_t, list->array, STACK_GET(++r1));
1194
--r2;
1195
}
1196
}
1197
DISPATCH();
1198
}
1199
1200
// MARK: -
1201
// MARK: CLOSURE
1202
CASE_CODE(CLOSURE): {
1203
OPCODE_GET_ONE8bit_ONE18bit(inst, const uint32_t r1, const uint32_t index);
1204
DEBUG_VM("CLOSURE %d %d", r1, index);
1205
1206
// get function prototype from cpool
1207
gravity_value_t v = gravity_function_cpool_get(func, index);
1208
if (!VALUE_ISA_FUNCTION(v)) RUNTIME_ERROR("Unable to create a closure from a non function object.");
1209
gravity_function_t *f = VALUE_AS_FUNCTION(v);
1210
1211
// create closure
1212
gravity_closure_t *closure = gravity_closure_new(vm, f);
1213
1214
// loop for each upvalue setup instruction
1215
for (uint16_t i=0; i<f->nupvalues; ++i) {
1216
// code is generated by the compiler so op must be MOVE
1217
inst = *ip++;
1218
op = (opcode_t)OPCODE_GET_OPCODE(inst);
1219
OPCODE_GET_ONE8bit_ONE18bit(inst, const uint32_t p1, const uint32_t p2);
1220
1221
// p2 can be 1 (means that upvalue is in the current call frame) or 0 (means that upvalue is in the upvalue list of the caller)
1222
ASSERT(op == MOVE, "Wrong OPCODE in CLOSURE statement");
1223
closure->upvalue[i] = (p2) ? gravity_capture_upvalue (vm, fiber, &stackstart[p1]) : frame->closure->upvalue[p1];
1224
}
1225
1226
SETVALUE(r1, VALUE_FROM_OBJECT(closure));
1227
DISPATCH();
1228
}
1229
1230
// MARK: CLOSE
1231
CASE_CODE(CLOSE): {
1232
OPCODE_GET_ONE8bit(inst, const uint32_t r1);
1233
DEBUG_VM("CLOSE %d", r1);
1234
1235
// close open upvalues (if any) starting from R1
1236
gravity_close_upvalues(fiber, &stackstart[r1]);
1237
DISPATCH();
1238
}
1239
1240
// MARK: - RESERVED
1241
CASE_CODE(RESERVED1):
1242
CASE_CODE(RESERVED2):
1243
CASE_CODE(RESERVED3):
1244
CASE_CODE(RESERVED4):
1245
CASE_CODE(RESERVED5):
1246
CASE_CODE(RESERVED6):{
1247
RUNTIME_ERROR("Opcode not implemented in this VM version.");
1248
DISPATCH();
1249
}
1250
}
1251
1252
// MARK: -
1253
1254
INC_PC;
1255
};
1256
1257
return true;
1258
}
1259
1260
gravity_vm *gravity_vm_new (gravity_delegate_t *delegate/*, uint32_t context_size, gravity_int_t gcminthreshold, gravity_int_t gcthreshold, gravity_float_t gcratio*/) {
1261
gravity_vm *vm = mem_alloc(sizeof(gravity_vm));
1262
if (!vm) return NULL;
1263
1264
// setup default callbacks
1265
vm->transfer = gravity_gc_transfer;
1266
vm->cleanup = gravity_gc_cleanup;
1267
1268
vm->pc = 0;
1269
vm->delegate = delegate;
1270
vm->context = gravity_hash_create(/*(context_size) ? context_size : */DEFAULT_CONTEXT_SIZE, gravity_value_hash, gravity_value_equals, NULL, NULL);
1271
1272
// garbage collector
1273
vm->gcenabled = true;
1274
vm->gcminthreshold = /*(gcminthreshold) ? gcminthreshold :*/ DEFAULT_CG_MINTHRESHOLD;
1275
vm->gcthreshold = /*(gcthreshold) ? gcthreshold :*/ DEFAULT_CG_THRESHOLD;
1276
vm->gcratio = /*(gcratio) ? gcratio :*/ DEFAULT_CG_RATIO;
1277
vm->memallocated = 0;
1278
marray_init(vm->graylist);
1279
marray_init(vm->gcsave);
1280
1281
// init base and core
1282
gravity_core_register(vm);
1283
gravity_cache_setup();
1284
1285
RESET_STATS(vm);
1286
return vm;
1287
}
1288
1289
gravity_vm *gravity_vm_newmini (void) {
1290
gravity_vm *vm = mem_alloc(sizeof(gravity_vm));
1291
return vm;
1292
}
1293
1294
void gravity_vm_free (gravity_vm *vm) {
1295
if (!vm) return;
1296
1297
if (vm->context) gravity_cache_free();
1298
gravity_vm_cleanup(vm);
1299
if (vm->context) gravity_hash_free(vm->context);
1300
marray_destroy(vm->gcsave);
1301
marray_destroy(vm->graylist);
1302
mem_free(vm);
1303
}
1304
1305
inline gravity_value_t gravity_vm_lookup (gravity_vm *vm, gravity_value_t key) {
1306
gravity_value_t *value = gravity_hash_lookup(vm->context, key);
1307
return (value) ? *value : VALUE_NOT_VALID;
1308
}
1309
1310
inline gravity_closure_t *gravity_vm_fastlookup (gravity_vm *vm, gravity_class_t *c, int index) {
1311
#pragma unused(vm)
1312
return (gravity_closure_t *)gravity_class_lookup_closure(c, cache[index]);
1313
}
1314
1315
inline gravity_value_t gravity_vm_getvalue (gravity_vm *vm, const char *key, uint32_t keylen) {
1316
STATICVALUE_FROM_STRING(k, key, keylen);
1317
return gravity_vm_lookup(vm, k);
1318
}
1319
1320
inline void gravity_vm_setvalue (gravity_vm *vm, const char *key, gravity_value_t value) {
1321
gravity_hash_insert(vm->context, VALUE_FROM_CSTRING(vm, key), value);
1322
}
1323
1324
double gravity_vm_time (gravity_vm *vm) {
1325
return vm->time;
1326
}
1327
1328
gravity_value_t gravity_vm_result (gravity_vm *vm) {
1329
gravity_value_t result = vm->fiber->result;
1330
vm->fiber->result = VALUE_FROM_NULL;
1331
return result;
1332
}
1333
1334
gravity_delegate_t *gravity_vm_delegate (gravity_vm *vm) {
1335
return vm->delegate;
1336
}
1337
1338
gravity_fiber_t *gravity_vm_fiber (gravity_vm* vm) {
1339
return vm->fiber;
1340
}
1341
1342
void gravity_vm_setfiber(gravity_vm* vm, gravity_fiber_t *fiber) {
1343
vm->fiber = fiber;
1344
}
1345
1346
void gravity_vm_seterror (gravity_vm* vm, const char *format, ...) {
1347
gravity_fiber_t *fiber = vm->fiber;
1348
1349
if (fiber->error) mem_free(fiber->error);
1350
size_t err_size = 2048;
1351
fiber->error = mem_alloc(err_size);
1352
1353
va_list arg;
1354
va_start (arg, format);
1355
vsnprintf(fiber->error, err_size, (format) ? format : "%s", arg);
1356
va_end (arg);
1357
}
1358
1359
void gravity_vm_seterror_string (gravity_vm* vm, const char *s) {
1360
gravity_fiber_t *fiber = vm->fiber;
1361
if (fiber->error) mem_free(fiber->error);
1362
fiber->error = (char *)string_dup(s);
1363
}
1364
1365
#if GRAVITY_VM_STATS
1366
static void gravity_vm_stats (gravity_vm *vm) {
1367
printf("\n============================================\n");
1368
printf("%12s %10s %20s\n", "OPCODE", "USAGE", "MICROBENCH (ms)");
1369
printf("============================================\n");
1370
1371
double total = 0.0;
1372
for (uint32_t i=0; i<GRAVITY_LATEST_OPCODE; ++i) {
1373
if (vm->nstat[i]) {
1374
total += vm->tstat[i];
1375
}
1376
}
1377
1378
for (uint32_t i=0; i<GRAVITY_LATEST_OPCODE; ++i) {
1379
if (vm->nstat[i]) {
1380
uint32_t n = vm->nstat[i];
1381
double d = vm->tstat[i] / (double)n;
1382
double p = (vm->tstat[i] * 100) / total;
1383
printf("%12s %*d %*.4f (%.2f%%)\n", opcode_name((opcode_t)i), 10, n, 11, d, p);
1384
}
1385
}
1386
printf("============================================\n");
1387
printf("# Frames reallocs: %d (%d)\n", vm->nfrealloc, vm->fiber->framesalloc);
1388
printf("# Stack reallocs: %d (%d)\n", vm->nsrealloc, vm->fiber->stackalloc);
1389
printf("============================================\n");
1390
}
1391
#endif
1392
1393
bool gravity_vm_runclosure (gravity_vm *vm, gravity_closure_t *closure, gravity_value_t selfvalue, gravity_value_t params[], uint16_t nparams) {
1394
if (vm->aborted) return false;
1395
1396
// do not waste cycles on empty functions
1397
gravity_function_t *f = closure->f;
1398
if ((f->tag == EXEC_TYPE_NATIVE) && ((!f->bytecode) || (f->ninsts == 0))) return true;
1399
1400
// current execution fiber
1401
gravity_fiber_t *fiber = vm->fiber;
1402
gravity_value_t *stackstart = NULL;
1403
uint32_t rwin = 0;
1404
1405
DEBUG_STACK();
1406
1407
// if fiber->nframes is not zero it means that this event has been recursively called
1408
// from somewhere inside the main function so we need to protect and correctly setup
1409
// the new activation frame
1410
if (fiber->nframes) {
1411
// current call frame
1412
gravity_callframe_t *frame = &fiber->frames[fiber->nframes - 1];
1413
1414
// current top of the stack
1415
stackstart = frame->stackstart;
1416
1417
// current instruction pointer
1418
uint32_t *ip = frame->ip;
1419
1420
// compute register window
1421
rwin = FN_COUNTREG(frame->closure->f, frame->nargs);
1422
1423
// check stack size
1424
gravity_check_stack(vm, vm->fiber, FN_COUNTREG(f,nparams+1), &stackstart);
1425
1426
// setup params (first param is self)
1427
SETVALUE(rwin, selfvalue);
1428
for (uint16_t i=0; i<nparams; ++i) {
1429
SETVALUE(rwin+i+1, params[i]);
1430
}
1431
1432
STORE_FRAME();
1433
PUSH_FRAME(closure, &stackstart[rwin], rwin, nparams);
1434
SETFRAME_OUTLOOP(cframe);
1435
} else {
1436
// there are no execution frames when called outside main function
1437
gravity_fiber_reassign(vm->fiber, closure, nparams+1);
1438
stackstart = vm->fiber->stack;
1439
1440
// setup params (first param is self)
1441
SETVALUE(rwin, selfvalue);
1442
for (uint16_t i=0; i<nparams; ++i) {
1443
SETVALUE(rwin+i+1, params[i]);
1444
}
1445
}
1446
1447
// f can be native, internal or bridge because this function
1448
// is called also by convert_value2string
1449
// for example in Creo:
1450
// var mData = Data();
1451
// Console.write("data: " + mData);
1452
// mData.String is a bridged objc method
1453
DEBUG_STACK();
1454
1455
bool result = false;
1456
switch (f->tag) {
1457
case EXEC_TYPE_NATIVE:
1458
result = gravity_vm_exec(vm);
1459
break;
1460
1461
case EXEC_TYPE_INTERNAL:
1462
result = f->internal(vm, &stackstart[rwin], nparams, GRAVITY_FIBER_REGISTER);
1463
break;
1464
1465
case EXEC_TYPE_BRIDGED:
1466
if (vm && vm->delegate && vm->delegate->bridge_execute)
1467
result = vm->delegate->bridge_execute(vm, f->xdata, &stackstart[rwin], nparams, GRAVITY_FIBER_REGISTER);
1468
break;
1469
1470
case EXEC_TYPE_SPECIAL:
1471
result = false;
1472
break;
1473
}
1474
if (f->tag != EXEC_TYPE_NATIVE) --fiber->nframes;
1475
1476
DEBUG_STACK();
1477
return result;
1478
}
1479
1480
bool gravity_vm_run (gravity_vm *vm, gravity_closure_t *closure) {
1481
// f is $moduleinit (first function to be executed)
1482
vm->fiber = gravity_fiber_new(vm, closure, 0, 0);
1483
1484
// execute $moduleinit in order to initialize VM
1485
gravity_vm_exec(vm);
1486
1487
// lookup main function
1488
gravity_value_t main = gravity_vm_getvalue(vm, MAIN_FUNCTION, (uint32_t)strlen(MAIN_FUNCTION));
1489
if (!VALUE_ISA_CLOSURE(main)) {
1490
report_runtime_error(vm, GRAVITY_ERROR_RUNTIME, "%s", "Unable to find main function.");
1491
return false;
1492
}
1493
1494
// re-use main fiber
1495
gravity_closure_t *main_closure = VALUE_AS_CLOSURE(main);
1496
gravity_fiber_reassign(vm->fiber, main_closure, 0);
1497
1498
// execute main function
1499
RESET_STATS(vm);
1500
nanotime_t tstart = nanotime();
1501
bool result = gravity_vm_exec(vm);
1502
nanotime_t tend = nanotime();
1503
vm->time = millitime(tstart, tend);
1504
1505
PRINT_STATS(vm);
1506
return result;
1507
}
1508
1509
// MARK: - User -
1510
1511
void gravity_vm_setslot (gravity_vm *vm, gravity_value_t value, uint32_t index) {
1512
if (index == GRAVITY_FIBER_REGISTER) {
1513
vm->fiber->result = value;
1514
return;
1515
}
1516
1517
gravity_callframe_t *frame = &(vm->fiber->frames[vm->fiber->nframes-1]);
1518
frame->stackstart[index] = value;
1519
}
1520
1521
gravity_value_t gravity_vm_getslot (gravity_vm *vm, uint32_t index) {
1522
gravity_callframe_t *frame = &(vm->fiber->frames[vm->fiber->nframes-1]);
1523
return frame->stackstart[index];
1524
}
1525
1526
void gravity_vm_setdata (gravity_vm *vm, void *data) {
1527
vm->data = data;
1528
}
1529
1530
void *gravity_vm_getdata (gravity_vm *vm) {
1531
return vm->data;
1532
}
1533
1534
void gravity_vm_set_callbacks (gravity_vm *vm, vm_transfer_cb vm_transfer, vm_cleanup_cb vm_cleanup) {
1535
vm->transfer = vm_transfer;
1536
vm->cleanup = vm_cleanup;
1537
}
1538
1539
void gravity_vm_transfer (gravity_vm* vm, gravity_object_t *obj) {
1540
if (vm->transfer) vm->transfer(vm, obj);
1541
}
1542
1543
void gravity_vm_cleanup (gravity_vm* vm) {
1544
if (vm->cleanup) vm->cleanup(vm);
1545
}
1546
1547
void gravity_vm_filter (gravity_vm* vm, vm_filter_cb cleanup_filter) {
1548
vm->filter = cleanup_filter;
1549
}
1550
1551
bool gravity_vm_ismini (gravity_vm *vm) {
1552
return (vm->context == NULL);
1553
}
1554
1555
bool gravity_vm_isaborted (gravity_vm *vm) {
1556
if (!vm) return true;
1557
return vm->aborted;
1558
}
1559
1560
void gravity_vm_setaborted (gravity_vm *vm) {
1561
vm->aborted = true;
1562
}
1563
1564
char *gravity_vm_anonymous (gravity_vm *vm) {
1565
snprintf(vm->temp, sizeof(vm->temp), "%sanon%d", GRAVITY_VM_ANONYMOUS_PREFIX, ++vm->nanon);
1566
return vm->temp;
1567
}
1568
1569
void gravity_vm_memupdate (gravity_vm *vm, gravity_int_t value) {
1570
vm->memallocated += value;
1571
}
1572
1573
// MARK: - Get/Set Internal Settings -
1574
1575
gravity_value_t gravity_vm_get (gravity_vm *vm, const char *key) {
1576
if (key) {
1577
if (strcmp(key, "gcenabled") == 0) return VALUE_FROM_BOOL(vm->gcenabled);
1578
if (strcmp(key, "gcminthreshold") == 0) return VALUE_FROM_INT(vm->gcminthreshold);
1579
if (strcmp(key, "gcthreshold") == 0) return VALUE_FROM_INT(vm->gcthreshold);
1580
if (strcmp(key, "gcratio") == 0) return VALUE_FROM_FLOAT(vm->gcratio);
1581
}
1582
return VALUE_FROM_NULL;
1583
}
1584
1585
bool gravity_vm_set (gravity_vm *vm, const char *key, gravity_value_t value) {
1586
if (key) {
1587
if ((strcmp(key, "gcenabled") == 0) && VALUE_ISA_BOOL(value)) {vm->gcenabled = VALUE_AS_BOOL(value); return true;}
1588
if ((strcmp(key, "gcminthreshold") == 0) && VALUE_ISA_INT(value)) {vm->gcminthreshold = VALUE_AS_INT(value); return true;}
1589
if ((strcmp(key, "gcthreshold") == 0) && VALUE_ISA_INT(value)) {vm->gcthreshold = VALUE_AS_INT(value); return true;}
1590
if ((strcmp(key, "gcratio") == 0) && VALUE_ISA_FLOAT(value)) {vm->gcratio = VALUE_AS_FLOAT(value); return true;}
1591
}
1592
return false;
1593
}
1594
1595
1596
// MARK: - Deserializer -
1597
1598
static bool real_set_superclass (gravity_vm *vm, gravity_class_t *c, gravity_value_t key, const char *supername) {
1599
// 1. LOOKUP in current stack hierarchy
1600
STATICVALUE_FROM_STRING(superkey, supername, strlen(supername));
1601
void_r *stack = (void_r *)vm->data;
1602
size_t n = marray_size(*stack);
1603
for (size_t i=0; i<n; i++) {
1604
gravity_object_t *obj = marray_get(*stack, i);
1605
// if object is a CLASS then lookup hash table
1606
if (OBJECT_ISA_CLASS(obj)) {
1607
gravity_class_t *c2 = (gravity_class_t *)gravity_class_lookup((gravity_class_t *)obj, superkey);
1608
if ((c2) && (OBJECT_ISA_CLASS(c2))) {
1609
mem_free(supername);
1610
if (!gravity_class_setsuper(c, c2)) goto error_max_ivar;
1611
return true;
1612
}
1613
}
1614
// if object is a FUNCTION then iterate the constant pool
1615
else if (OBJECT_ISA_FUNCTION(obj)) {
1616
gravity_function_t *f = (gravity_function_t *)obj;
1617
if (f->tag == EXEC_TYPE_NATIVE) {
1618
size_t count = marray_size(f->cpool);
1619
for (size_t j=0; j<count; j++) {
1620
gravity_value_t v = marray_get(f->cpool, j);
1621
if (VALUE_ISA_CLASS(v)) {
1622
gravity_class_t *c2 = VALUE_AS_CLASS(v);
1623
if (strcmp(c2->identifier, supername) == 0) {
1624
mem_free(supername);
1625
if (!gravity_class_setsuper(c, c2)) goto error_max_ivar;
1626
return true;
1627
}
1628
}
1629
}
1630
}
1631
}
1632
}
1633
1634
// 2. not found in stack hierarchy so LOOKUP in VM
1635
gravity_value_t v = gravity_vm_lookup(vm, superkey);
1636
if (VALUE_ISA_CLASS(v)) {
1637
mem_free(supername);
1638
if (!gravity_class_setsuper(c, VALUE_AS_CLASS(v))) goto error_max_ivar;
1639
return true;
1640
}
1641
1642
report_runtime_error(vm, GRAVITY_ERROR_RUNTIME, "Unable to find superclass %s of class %s.", supername, VALUE_AS_CSTRING(key));
1643
mem_free(supername);
1644
return false;
1645
1646
error_max_ivar:
1647
report_runtime_error(vm, GRAVITY_ERROR_RUNTIME, "Maximum number of allowed ivars (%d) reached for class %s.", MAX_IVARS, VALUE_AS_CSTRING(key));
1648
return false;
1649
}
1650
1651
static void vm_set_superclass_callback (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data) {
1652
#pragma unused(hashtable)
1653
gravity_vm *vm = (gravity_vm *)data;
1654
1655
if (VALUE_ISA_FUNCTION(value)) vm_set_superclass(vm, VALUE_AS_OBJECT(value));
1656
if (!VALUE_ISA_CLASS(value)) return;
1657
1658
// process class
1659
gravity_class_t *c = VALUE_AS_CLASS(value);
1660
DEBUG_DESERIALIZE("set_superclass_callback for class %s", c->identifier);
1661
1662
const char *supername = c->xdata;
1663
c->xdata = NULL;
1664
if (supername) {
1665
if (!real_set_superclass(vm, c, key, supername)) return;
1666
}
1667
1668
gravity_hash_iterate(c->htable, vm_set_superclass_callback, vm);
1669
}
1670
1671
static bool vm_set_superclass (gravity_vm *vm, gravity_object_t *obj) {
1672
void_r *stack = (void_r *)vm->data;
1673
marray_push(void*, *stack, obj);
1674
1675
if (OBJECT_ISA_CLASS(obj)) {
1676
// CLASS case: process class and its hash table
1677
gravity_class_t *c = (gravity_class_t *)obj;
1678
DEBUG_DESERIALIZE("set_superclass for class %s", c->identifier);
1679
1680
STATICVALUE_FROM_STRING(key, c->identifier, strlen(c->identifier));
1681
const char *supername = c->xdata;
1682
c->xdata = NULL;
1683
if (supername) real_set_superclass(vm, c, key, supername);
1684
gravity_hash_iterate(c->htable, vm_set_superclass_callback, vm);
1685
1686
} else if (OBJECT_ISA_FUNCTION(obj)) {
1687
// FUNCTION case: scan constant pool and recursively call fixsuper
1688
1689
gravity_function_t *f = (gravity_function_t *)obj;
1690
if (f->tag == EXEC_TYPE_NATIVE) {
1691
size_t n = marray_size(f->cpool);
1692
for (size_t i=0; i<n; i++) {
1693
gravity_value_t v = marray_get(f->cpool, i);
1694
if (VALUE_ISA_FUNCTION(v)) vm_set_superclass(vm, (gravity_object_t *)VALUE_AS_FUNCTION(v));
1695
else if (VALUE_ISA_CLASS(v)) vm_set_superclass(vm, (gravity_object_t *)VALUE_AS_CLASS(v));
1696
}
1697
}
1698
} else {
1699
report_runtime_error(vm, GRAVITY_ERROR_RUNTIME, "%s", "Unable to recognize object type.");
1700
return false;
1701
}
1702
1703
marray_pop(*stack);
1704
return true;
1705
}
1706
1707
gravity_closure_t *gravity_vm_loadfile (gravity_vm *vm, const char *path) {
1708
size_t len;
1709
const char *buffer = file_read(path, &len);
1710
if (!buffer) return NULL;
1711
1712
gravity_closure_t *closure = gravity_vm_loadbuffer(vm, buffer, len);
1713
1714
mem_free(buffer);
1715
return closure;
1716
}
1717
1718
gravity_closure_t *gravity_vm_loadbuffer (gravity_vm *vm, const char *buffer, size_t len) {
1719
json_value *json = json_parse (buffer, len);
1720
if (!json) goto abort_load;
1721
if (json->type != json_object) goto abort_load;
1722
1723
// state buffer for further processing super classes
1724
void_r objects;
1725
marray_init(objects);
1726
1727
// disable GC while deserializing objects
1728
gravity_gc_setenabled(vm, false);
1729
1730
// scan loop
1731
gravity_closure_t *closure = NULL;
1732
uint32_t n = json->u.object.length;
1733
for (uint32_t i=0; i<n; ++i) {
1734
json_value *entry = json->u.object.values[i].value;
1735
if (entry->u.object.length == 0) continue;
1736
1737
// each entry must be an object
1738
if (entry->type != json_object) goto abort_load;
1739
1740
gravity_object_t *obj = NULL;
1741
if (!gravity_object_deserialize(vm, entry, &obj)) goto abort_load;
1742
if (!obj) continue;
1743
1744
// save object for further processing
1745
marray_push(void*, objects, obj);
1746
1747
// add a sanity check here: obj can be a function or a class, nothing else
1748
1749
// transform every function to a closure
1750
if (OBJECT_ISA_FUNCTION(obj)) {
1751
gravity_function_t *f = (gravity_function_t *)obj;
1752
const char *identifier = f->identifier;
1753
gravity_closure_t *cl = gravity_closure_new(vm, f);
1754
if (string_casencmp(identifier, INITMODULE_NAME, strlen(identifier)) == 0) {
1755
closure = cl;
1756
} else {
1757
gravity_vm_setvalue(vm, identifier, VALUE_FROM_OBJECT(cl));
1758
}
1759
}
1760
}
1761
json_value_free(json);
1762
1763
// fix superclass(es)
1764
size_t count = marray_size(objects);
1765
if (count) {
1766
void *saved = vm->data;
1767
1768
// prepare stack to help resolve nested super classes
1769
void_r stack;
1770
marray_init(stack);
1771
vm->data = (void *)&stack;
1772
1773
// loop of each processed object
1774
for (size_t i=0; i<count; ++i) {
1775
gravity_object_t *obj = (gravity_object_t *)marray_get(objects, i);
1776
if (!vm_set_superclass(vm, obj)) goto abort_generic;
1777
}
1778
1779
marray_destroy(stack);
1780
marray_destroy(objects);
1781
vm->data = saved;
1782
}
1783
1784
gravity_gc_setenabled(vm, true);
1785
return closure;
1786
1787
abort_load:
1788
report_runtime_error(vm, GRAVITY_ERROR_RUNTIME, "%s", "Unable to parse JSON executable file.");
1789
if (json) json_value_free(json);
1790
gravity_gc_setenabled(vm, true);
1791
return NULL;
1792
1793
abort_generic:
1794
if (json) json_value_free(json);
1795
gravity_gc_setenabled(vm, true);
1796
return NULL;
1797
}
1798
1799
// MARK: - Garbage Collector -
1800
1801
void gravity_gray_object (gravity_vm *vm, gravity_object_t *obj) {
1802
if (!obj) return;
1803
1804
// avoid recursion if object has already been visited
1805
if (obj->gc.isdark) return;
1806
1807
DEBUG_GC("GRAY %s", gravity_object_debug(obj));
1808
1809
// object has been reached
1810
obj->gc.isdark = true;
1811
1812
// add to marked array
1813
marray_push(gravity_object_t *, vm->graylist, obj);
1814
}
1815
1816
void gravity_gray_value (gravity_vm *vm, gravity_value_t v) {
1817
if (gravity_value_isobject(v)) gravity_gray_object(vm, (gravity_object_t *)v.p);
1818
}
1819
1820
static void gravity_gray_hash (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t value, void *data) {
1821
#pragma unused (hashtable)
1822
gravity_vm *vm = (gravity_vm*)data;
1823
gravity_gray_value(vm, key);
1824
gravity_gray_value(vm, value);
1825
}
1826
1827
// MARK: -
1828
1829
static void gravity_gc_transform (gravity_hash_t *hashtable, gravity_value_t key, gravity_value_t *value, void *data) {
1830
#pragma unused (hashtable)
1831
1832
gravity_vm *vm = (gravity_vm *)data;
1833
gravity_object_t *obj = VALUE_AS_OBJECT(*value);
1834
1835
if (OBJECT_ISA_FUNCTION(obj)) {
1836
gravity_function_t *f = (gravity_function_t *)obj;
1837
if (f->tag == EXEC_TYPE_SPECIAL) {
1838
if (f->special[0]) {
1839
gravity_gc_transfer(vm, (gravity_object_t *)f->special[0]);
1840
f->special[0] = gravity_closure_new(vm, f->special[0]);
1841
}
1842
if (f->special[1]) {
1843
gravity_gc_transfer(vm, (gravity_object_t *)f->special[1]);
1844
f->special[1] = gravity_closure_new(vm, f->special[1]);
1845
}
1846
} else if (f->tag == EXEC_TYPE_NATIVE) {
1847
gravity_vm_initmodule(vm, f);
1848
}
1849
1850
bool is_super_function = false;
1851
if (VALUE_ISA_STRING(key)) {
1852
gravity_string_t *s = VALUE_AS_STRING(key);
1853
// looking for a string that begins with $init AND it is longer than strlen($init)
1854
is_super_function = ((s->len > 5) && (string_casencmp(s->s, CLASS_INTERNAL_INIT_NAME, 5) == 0));
1855
}
1856
1857
gravity_closure_t *closure = gravity_closure_new(vm, f);
1858
*value = VALUE_FROM_OBJECT((gravity_object_t *)closure);
1859
if (!is_super_function) gravity_gc_transfer(vm, obj);
1860
} else if (OBJECT_ISA_CLASS(obj)) {
1861
gravity_class_t *c = (gravity_class_t *)obj;
1862
gravity_vm_loadclass(vm, c);
1863
} else {
1864
// should never reach this point
1865
assert(0);
1866
}
1867
}
1868
1869
void gravity_vm_initmodule (gravity_vm *vm, gravity_function_t *f) {
1870
size_t n = marray_size(f->cpool);
1871
for (size_t i=0; i<n; i++) {
1872
gravity_value_t v = marray_get(f->cpool, i);
1873
if (VALUE_ISA_CLASS(v)) {
1874
gravity_class_t *c = VALUE_AS_CLASS(v);
1875
gravity_vm_loadclass(vm, c);
1876
}
1877
else if (VALUE_ISA_FUNCTION(v)) {
1878
gravity_vm_initmodule(vm, VALUE_AS_FUNCTION(v));
1879
}
1880
}
1881
}
1882
1883
static void gravity_gc_transfer_object (gravity_vm *vm, gravity_object_t *obj) {
1884
DEBUG_GC("GC TRANSFER %s", gravity_object_debug(obj));
1885
++vm->gccount;
1886
obj->gc.next = vm->gchead;
1887
vm->gchead = obj;
1888
}
1889
1890
static void gravity_gc_transfer (gravity_vm *vm, gravity_object_t *obj) {
1891
if (vm->gcenabled) {
1892
#if GRAVITY_GC_STRESSTEST
1893
// check if ptr is already in the list
1894
gravity_object_t **ptr = &vm->gchead;
1895
while (*ptr) {
1896
if (obj == *ptr) {
1897
printf("Object %s already GC!\n", gravity_object_debug(obj));
1898
assert(0);
1899
}
1900
ptr = &(*ptr)->gc.next;
1901
}
1902
gravity_gc_start(vm);
1903
#else
1904
if (vm->memallocated >= vm->gcthreshold) gravity_gc_start(vm);
1905
#endif
1906
}
1907
1908
gravity_gc_transfer_object(vm, obj);
1909
}
1910
1911
static void gravity_gc_sweep (gravity_vm *vm) {
1912
gravity_object_t **obj = &vm->gchead;
1913
while (*obj) {
1914
if (!(*obj)->gc.isdark) {
1915
// object is unreachable so remove from the list and free it
1916
gravity_object_t *unreached = *obj;
1917
*obj = unreached->gc.next;
1918
gravity_object_free(vm, unreached);
1919
--vm->gccount;
1920
} else {
1921
// object was reached so unmark it for the next GC and move on to the next
1922
(*obj)->gc.isdark = false;
1923
obj = &(*obj)->gc.next;
1924
}
1925
}
1926
}
1927
1928
void gravity_gc_start (gravity_vm *vm) {
1929
if (!vm->fiber) return;
1930
1931
#if GRAVITY_GC_STATS
1932
gravity_int_t membefore = vm->memallocated;
1933
nanotime_t tstart = nanotime();
1934
#endif
1935
1936
// reset memory counter
1937
vm->memallocated = 0;
1938
1939
// mark GC saved temp object
1940
for (uint32_t i=0; i<marray_size(vm->gcsave); ++i) {
1941
gravity_object_t *obj = marray_get(vm->gcsave, i);
1942
gravity_gray_object(vm, obj);
1943
}
1944
1945
// mark all reachable objects starting from current fiber
1946
gravity_gray_object(vm, (gravity_object_t *)vm->fiber);
1947
1948
// mark globals
1949
gravity_hash_iterate(vm->context, gravity_gray_hash, (void*)vm);
1950
1951
// root has been grayed so recursively scan reachable objects
1952
while (marray_size(vm->graylist)) {
1953
gravity_object_t *obj = marray_pop(vm->graylist);
1954
gravity_object_blacken(vm, obj);
1955
}
1956
1957
// check unreachable objects (collect white objects)
1958
gravity_gc_sweep(vm);
1959
1960
// dynamically update gcthreshold
1961
vm->gcthreshold = vm->memallocated + (vm->memallocated * vm->gcratio / 100);
1962
if (vm->gcthreshold < vm->gcminthreshold) vm->gcthreshold = vm->gcminthreshold;
1963
1964
#if GRAVITY_GC_STATS
1965
nanotime_t tend = nanotime();
1966
double gctime = millitime(tstart, tend);
1967
printf("GC %lu before, %lu after (%lu collected - %lu objects), next at %lu. Took %.3fs.\n",
1968
(unsigned long)membefore,
1969
(unsigned long)vm->memallocated,
1970
(membefore > vm->memallocated) ? (unsigned long)(membefore - vm->memallocated) : 0,
1971
(unsigned long)vm->gccount,
1972
(unsigned long)vm->gcthreshold,
1973
gctime);
1974
#endif
1975
}
1976
1977
static void gravity_gc_cleanup (gravity_vm *vm) {
1978
if (!vm->gchead) return;
1979
1980
if (vm->filter) {
1981
// filter case
1982
vm_filter_cb filter = vm->filter;
1983
1984
// we need to remove freed objects from the linked list
1985
// so we need a pointer to the previous object in order
1986
// to be able to point it to obj's next ptr
1987
//
1988
// +--------+ +--------+ +--------+
1989
// --> | prev | --> | obj | --> | next | -->
1990
// +--------+ +--------+ +--------+
1991
// | ^
1992
// | |
1993
// +-------------------------------+
1994
1995
gravity_object_t *obj = vm->gchead;
1996
gravity_object_t *prev = NULL;
1997
1998
while (obj) {
1999
if (!filter(obj)) {
2000
prev = obj;
2001
obj = obj->gc.next;
2002
continue;
2003
}
2004
2005
// REMOVE obj from the linked list
2006
gravity_object_t *next = obj->gc.next;
2007
if (!prev) vm->gchead = next;
2008
else prev->gc.next = next;
2009
2010
gravity_object_free(vm, obj);
2011
--vm->gccount;
2012
obj = next;
2013
}
2014
return;
2015
}
2016
2017
// no filter so free all GC objects
2018
gravity_object_t *obj = vm->gchead;
2019
while (obj) {
2020
gravity_object_t *next = obj->gc.next;
2021
gravity_object_free(vm, obj);
2022
--vm->gccount;
2023
obj = next;
2024
}
2025
vm->gchead = NULL;
2026
2027
// free all temporary allocated objects
2028
while (marray_size(vm->gcsave)) {
2029
gravity_object_t *tobj = marray_pop(vm->gcsave);
2030
gravity_object_free(vm, tobj);
2031
}
2032
}
2033
2034
void gravity_gc_setenabled (gravity_vm *vm, bool enabled) {
2035
vm->gcenabled = enabled;
2036
}
2037
2038
void gravity_gc_push (gravity_vm *vm, gravity_object_t *obj) {
2039
marray_push(gravity_object_t *, vm->gcsave, obj);
2040
}
2041
2042
void gravity_gc_pop (gravity_vm *vm) {
2043
marray_pop(vm->gcsave);
2044
}
2045
2046