fa_signal_t fa_signal_copy(fa_signal_t signal) { switch (signal->tag) { case time_signal: return fa_signal_time(); case random_signal: return fa_signal_random(); case constant_signal: return copy_constant(signal); case lift_signal: return copy_lift(signal); case lift2_signal: return copy_lift2(signal); case loop_signal: return copy_loop(signal); case delay_signal: return copy_delay(signal); case insert_signal: return copy_insert(signal); case custom_signal: return copy_custom(signal); case input_signal: return copy_input(signal); case output_signal: return copy_output(signal); default: assert(false); } }
void execute_function(VM *vm) { restart: { Frame *frame = vm->current; Closure *closure = frame->closure; Chunk *chunk = closure->chunk; StackObject *registers = frame->registers; while (frame->pc < chunk->numinstructions) { int instruction = chunk->instructions[frame->pc]; OpCode o = GET_O(instruction); int a = GET_A(instruction); int b = GET_B(instruction); int c = GET_C(instruction); switch (o) { case OP_MOVE: { if (b < 256) { copy_object(®isters[a], ®isters[b]); } else { copy_constant(vm, ®isters[a], chunk->constants[b - 256]); } } break; case OP_GETUPVAR: { Upval *upval = closure->upvals[b]; if (!upval->open) { // upval is closed copy_object(®isters[a], upval->data.o); } else { // still on stack copy_object(®isters[a], &upval->data.ref.frame->registers[upval->data.ref.slot]); } } break; case OP_SETUPVAR: { Upval *upval = closure->upvals[b]; if (!upval->open) { // upval is closed copy_object(upval->data.o, ®isters[a]); } else { // still on stack copy_object(&upval->data.ref.frame->registers[upval->data.ref.slot], ®isters[a]); } } break; case OP_ADD: { // TODO - make string coercion better // TODO - make string type with special operators if (IS_STR(b) || IS_STR(c)) { char *arg1 = TO_STR(b); char *arg2 = TO_STR(c); char *arg3 = malloc((strlen(arg1) + strlen(arg2) + 1) + sizeof *arg3); strcpy(arg3, arg1); strcat(arg3, arg2); registers[a].value.o = make_string_ref(vm, arg3); registers[a].type = OBJECT_REFERENCE; // put this after free(arg1); free(arg2); } else { if (!(IS_INT(b) || IS_REAL(b)) || !(IS_INT(c) || IS_REAL(c))) { fatal("Cannot add types."); } if (IS_INT(b) && IS_INT(c)) { int arg1 = AS_INT(b); int arg2 = AS_INT(c); registers[a].type = OBJECT_INT; registers[a].value.i = arg1 + arg2; } else { double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_REAL; registers[a].value.d = arg1 + arg2; } } } break; case OP_SUB: { if (!(IS_INT(b) || IS_REAL(b)) || !(IS_INT(c) || IS_REAL(c))) { fatal("Tried to sub non-numbers."); } if (IS_INT(b) && IS_INT(c)) { int arg1 = AS_INT(b); int arg2 = AS_INT(c); registers[a].type = OBJECT_INT; registers[a].value.i = arg1 - arg2; } else { double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_REAL; registers[a].value.d = arg1 - arg2; } } break; case OP_MUL: { if (!(IS_INT(b) || IS_REAL(b)) || !(IS_INT(c) || IS_REAL(c))) { fatal("Tried to mul non-numbers."); } if (IS_INT(b) && IS_INT(c)) { int arg1 = AS_INT(b); int arg2 = AS_INT(c); registers[a].type = OBJECT_INT; registers[a].value.i = arg1 * arg2; } else { double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_REAL; registers[a].value.d = arg1 * arg2; } } break; case OP_DIV: { if (!(IS_INT(b) || IS_REAL(b)) || !(IS_INT(c) || IS_REAL(c))) { fatal("Tried to div non-numbers."); } if ((IS_INT(c) && AS_INT(c) == 0) || (IS_REAL(c) && AS_REAL(c) == 0)) { fatal("Div by 0."); } if (IS_INT(b) && IS_INT(c)) { int arg1 = AS_INT(b); int arg2 = AS_INT(c); registers[a].type = OBJECT_INT; registers[a].value.i = arg1 / arg2; } else { double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_REAL; registers[a].value.d = arg1 / arg2; } } break; case OP_MOD: { if (!(IS_INT(b) || IS_REAL(b)) || !(IS_INT(c) || IS_REAL(c))) { fatal("Tried to div non-numbers."); } if ((IS_INT(c) && AS_INT(c) == 0) || (IS_REAL(c) && AS_REAL(c) == 0)) { fatal("Mod by 0."); } if (IS_INT(b) && IS_INT(c)) { int arg1 = AS_INT(b); int arg2 = AS_INT(c); registers[a].type = OBJECT_INT; registers[a].value.i = arg1 % arg2; } else { double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_REAL; registers[a].value.i = fmod(arg1, arg2); } } break; case OP_POW: { if (!(IS_INT(b) || IS_REAL(b)) || !(IS_INT(c) || IS_REAL(c))) { fatal("Tried to div non-numbers."); } if (IS_INT(b) && IS_INT(c)) { int arg1 = AS_INT(b); int arg2 = AS_INT(c); registers[a].type = OBJECT_INT; registers[a].value.i = (int) pow(arg1, arg2); } else { double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_REAL; registers[a].value.d = pow(arg1, arg2); } } break; case OP_NEG: { if (IS_INT(b)) { registers[a].type = OBJECT_INT; registers[a].value.i = -AS_INT(b); } else if (IS_REAL(b)) { registers[a].type = OBJECT_INT; registers[a].value.i = -AS_REAL(b); } else { fatal("Tried to negate non-numeric type."); } } break; case OP_NOT: { if (registers[a].type != OBJECT_BOOL) { fatal("Expected boolean type, not %d.", registers[a].type); } registers[a].value.i = registers[a].value.i == 1 ? 0 : 1; } break; case OP_EQ: { if ((IS_INT(b) || IS_REAL(b)) && (IS_INT(c) || IS_REAL(c))) { double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_BOOL; registers[a].value.i = arg1 == arg2; } else { fatal("Comparison of reference types not yet supported."); } } break; case OP_LT: { if (!(IS_INT(b) || IS_REAL(b)) || !(IS_INT(c) || IS_REAL(c))) { fatal("Tried to compare non-numbers."); } double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_BOOL; registers[a].value.i = arg1 < arg2; } break; case OP_LE: { if (!(IS_INT(b) || IS_REAL(b)) || !(IS_INT(c) || IS_REAL(c))) { fatal("Tried to compare non-numbers."); } double arg1 = IS_INT(b) ? (double) AS_INT(b) : AS_REAL(b); double arg2 = IS_INT(c) ? (double) AS_INT(c) : AS_REAL(c); registers[a].type = OBJECT_BOOL; registers[a].value.i = arg1 <= arg2; } break; case OP_CLOSURE: { Closure *child = make_closure(chunk->children[b]); int i; for (i = 0; i < chunk->children[b]->numupvars; i++) { int inst = chunk->instructions[++frame->pc]; OpCode oc = GET_O(inst); int ac = GET_A(inst); int bc = GET_B(inst); int cc = GET_C(inst); if (oc == OP_MOVE) { // first upval for this variable child->upvals[ac] = make_upval(vm, bc); } else { // share upval child->upvals[ac] = closure->upvals[bc]; child->upvals[ac]->refcount++; } } registers[a].value.o = make_closure_ref(vm, child); registers[a].type = OBJECT_REFERENCE; // put this after } break; case OP_CALL: { if (registers[b].type != OBJECT_REFERENCE || registers[b].value.o->type != OBJECT_CLOSURE) { fatal("Tried to call non-closure."); } // TODO - safety issue (see compile.c for notes) Closure *child = registers[b].value.o->value.c; Frame *subframe = make_frame(frame, child); int i; for (i = 0; i < child->chunk->numparams; i++) { copy_object(&subframe->registers[i + 1], ®isters[c + i]); } vm->current = subframe; goto restart; } break; case OP_RETURN: { UpvalNode *head; for (head = vm->open; head != NULL; ) { Upval *u = head->upval; if (u->data.ref.frame == frame) { StackObject *o = malloc(sizeof *o); if (!o) { fatal("Out of memory."); } u->open = 0; copy_object(o, ®isters[u->data.ref.slot]); u->data.o = o; if (vm->open == head) { vm->open = head->next; } else { head->next->prev = head->prev; head->prev->next = head->next; } UpvalNode *temp = head; head = head->next; free(temp); } else { head = head->next; } } if (vm->current->parent != NULL) { Frame *p = vm->current->parent; StackObject *target = &p->registers[GET_A(p->closure->chunk->instructions[p->pc++])]; if (b < 256) { // debug char *d = obj_to_str(®isters[b]); printf("Return value: %s\n", d); free(d); copy_object(target, ®isters[b]); } else { copy_constant(vm, target, chunk->constants[b - 256]); } free_frame(frame); vm->current = p; goto restart; } else { // debug char *d = obj_to_str(®isters[b]); printf("Return value: %s\n", d); free(d); free_frame(frame); vm->current = NULL; return; } } break; case OP_JUMP: frame->pc += c ? -b : b; break; case OP_JUMP_TRUE: { if (registers[a].type != OBJECT_BOOL) { fatal("Expected boolean type, not %d.", registers[a].type); } if (registers[a].value.i == 1) { frame->pc += c ? -b : b; } } break; case OP_JUMP_FALSE: { if (registers[a].type != OBJECT_BOOL) { fatal("Expected boolean type, not %d.", registers[a].type); } if (registers[a].value.i == 0) { frame->pc += c ? -b : b; } } break; case OP_ENTER_TRY: { vm->catchframe = make_catch_frame(frame, vm->catchframe, frame->pc + b); } break; case OP_LEAVE_TRY: { CatchFrame *temp = vm->catchframe; vm->catchframe = vm->catchframe->parent; free_catch_frame(temp); } break; case OP_THROW: { // TODO - replace unwinding of stack with an exceptions // table per-chunk. It will have an instructions range, // the starting instruction of a handler, and the type of // exception that it may handle. // Exception table: // From To Target Type // 0 4 5 Class TestExc1 // 0 12 12 Class TestExc2 // TODO - implement a way to expect an exception // of a given type instead of a generic catch-all. char *s = obj_to_str(®isters[a]); printf("Exception value: %s!\n", s); free(s); // TODO - this is probably wrong. Not sure how complicated // it will be to handle upvalues and frame destruction here, // so we're just doing it a shitty way for now :D [GO LAZE]. if (!vm->catchframe) { // TODO - print a stack trace [ requires debug symbols :( ] fatal("Exception thrown outside of handler."); } while (vm->current != vm->catchframe->frame) { // TODO - destruct frame vm->current = vm->current->parent; } vm->current->pc = vm->catchframe->target; CatchFrame *temp = vm->catchframe; vm->catchframe = vm->catchframe->parent; free_catch_frame(temp); goto restart; } break; } frame->pc++; } fatal("VM left instruction-space."); } }