- 16 tasks complete: arena, object model, borrow word, containers, RC + budgeted cycle collector, wob_build, validating loader, interpreter core (dual dispatch), object opcodes, drop-map unwinding, builtins + DB_STUB + TRAP, ICALL, wovm CLI + just recipes - 13 test suites × 2 dispatch flavors (ASan+UBSan) + CLI smoke, all green - .wob v1 format pinned in src/wob.h + docs/plan/oop-vm/00-wob-format.md - wo-rt.c reference event-loop preserved for sub-project 2 This is Iteration 2 of the OOP milestone; compiler front (Iteration 3) is in progress on this branch. They meet at Iteration 4 (emitter+e2e).
95 lines
2.8 KiB
C
95 lines
2.8 KiB
C
/* test_objops — NEW / GETF / SETF / DROP through bytecode, plus the
|
|
* residual runtime checks (field bounds, null receiver) trapping BOUNDS. */
|
|
#include <stdlib.h>
|
|
|
|
#include "loader.h"
|
|
#include "t.h"
|
|
#include "vm.h"
|
|
#include "wob_build.h"
|
|
|
|
static wo_vm VM;
|
|
|
|
static int run_img(uint8_t *img, size_t len, uint64_t *ret, wo_err *err) {
|
|
wo_module mod;
|
|
char lerr[256];
|
|
if (wo_load_buf(&mod, img, len, lerr, sizeof lerr) != 0) {
|
|
fprintf(stderr, "loader rejected test image: %s\n", lerr);
|
|
return -2;
|
|
}
|
|
if (wo_vm_init(&VM, &mod, 1 << 20) != 0) {
|
|
wo_module_free(&mod);
|
|
return -2;
|
|
}
|
|
int rc = wo_vm_call(&VM, 0, NULL, 0, ret, err);
|
|
wo_vm_destroy(&VM);
|
|
wo_module_free(&mod);
|
|
return rc;
|
|
}
|
|
|
|
/* image with class Point{x,y} and a single free fn built from `code` */
|
|
static uint8_t *point_image(const uint32_t *code, uint32_t n, size_t *len) {
|
|
wb_t *b = wb_new();
|
|
uint32_t kp = wb_const_text(b, "Point");
|
|
uint32_t kf = wb_const_text(b, "go");
|
|
wb_const_int(b, 7); /* constant index 2 */
|
|
uint8_t kinds[] = {WO_K_SCALAR, WO_K_SCALAR};
|
|
wb_class(b, kp, 0, kinds, 2);
|
|
wb_method(b, kf, WOB_NONE, 0, 3, code, n, NULL, 0, NULL, 0);
|
|
return wb_finish(b, len);
|
|
}
|
|
|
|
static void test_new_set_get_drop_roundtrip(void) {
|
|
uint32_t code[] = {
|
|
wo_ins_abx(WOP_NEW, 0, 0), /* r0 = new Point */
|
|
wo_ins_abx(WOP_LOADK, 1, 2), /* r1 = 7 */
|
|
wo_ins_abc(WOP_SETF, 0, 0, 1), /* r0.f0 = r1 */
|
|
wo_ins_abc(WOP_GETF, 2, 0, 0), /* r2 = r0.f0 */
|
|
wo_ins_abc(WOP_DROP, 0, 0, 0), /* drop r0 (nulls the register) */
|
|
wo_ins_abc(WOP_RET, 2, 0, 0),
|
|
};
|
|
size_t len;
|
|
uint8_t *img = point_image(code, 6, &len);
|
|
uint64_t ret = 0;
|
|
wo_err err;
|
|
T_EQ(run_img(img, len, &ret, &err), 0);
|
|
T_EQ(ret, 7);
|
|
free(img);
|
|
}
|
|
|
|
static void test_field_index_out_of_range_traps(void) {
|
|
uint32_t code[] = {
|
|
wo_ins_abx(WOP_NEW, 0, 0),
|
|
wo_ins_abc(WOP_GETF, 2, 0, 9), /* Point has 2 fields */
|
|
wo_ins_abc(WOP_RET, 2, 0, 0),
|
|
};
|
|
size_t len;
|
|
uint8_t *img = point_image(code, 3, &len);
|
|
uint64_t ret = 0;
|
|
wo_err err = {0};
|
|
T_EQ(run_img(img, len, &ret, &err), -1);
|
|
T_EQ(err.code, WO_T_BOUNDS);
|
|
T_EQ(wo_vm_depth(&VM), 0);
|
|
free(img);
|
|
}
|
|
|
|
static void test_null_receiver_traps(void) {
|
|
uint32_t code[] = {
|
|
/* r0 was never assigned: zeroed by the frame setup */
|
|
wo_ins_abc(WOP_GETF, 2, 0, 0),
|
|
wo_ins_abc(WOP_RET, 2, 0, 0),
|
|
};
|
|
size_t len;
|
|
uint8_t *img = point_image(code, 2, &len);
|
|
uint64_t ret = 0;
|
|
wo_err err = {0};
|
|
T_EQ(run_img(img, len, &ret, &err), -1);
|
|
T_EQ(err.code, WO_T_BOUNDS);
|
|
free(img);
|
|
}
|
|
|
|
int main(void) {
|
|
test_new_set_get_drop_roundtrip();
|
|
test_field_index_out_of_range_traps();
|
|
test_null_receiver_traps();
|
|
return t_report("test_objops");
|
|
}
|