- 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).
116 lines
3.5 KiB
C
116 lines
3.5 KiB
C
/* test_loader — happy path from a builder image, then a rejection battery.
|
|
* Every reject must produce a nonempty error and leak nothing (ASan). */
|
|
#include <stdlib.h>
|
|
|
|
#include "loader.h"
|
|
#include "t.h"
|
|
#include "wob_build.h"
|
|
|
|
/* a valid two-const, one-class, one-method image */
|
|
static uint8_t *valid_image(size_t *len) {
|
|
wb_t *b = wb_new();
|
|
uint32_t k42 = wb_const_int(b, 42);
|
|
uint32_t kname = wb_const_text(b, "main");
|
|
uint8_t kinds[] = {WO_K_SCALAR, WO_K_SCALAR};
|
|
wb_class(b, kname, 0, kinds, 2);
|
|
uint32_t code[] = {
|
|
wo_ins_abx(WOP_LOADK, 0, (uint16_t)k42),
|
|
wo_ins_abc(WOP_RET, 0, 0, 0),
|
|
};
|
|
uint32_t lines[] = {0, 10, 1, 11};
|
|
uint32_t m = wb_method(b, kname, WOB_NONE, 0, 2, code, 2, lines, 2, NULL, 0);
|
|
wb_entry(b, m);
|
|
return wb_finish(b, len);
|
|
}
|
|
|
|
static void test_happy_path(void) {
|
|
size_t len;
|
|
uint8_t *img = valid_image(&len);
|
|
wo_module m;
|
|
char err[256] = "";
|
|
T_EQ(wo_load_buf(&m, img, len, err, sizeof err), 0);
|
|
T_EQ(m.const_cnt, 2);
|
|
T_EQ(m.consts[0].tag, WOB_K_INT);
|
|
T_EQ(m.consts[0].i, 42);
|
|
T_EQ(m.consts[1].tag, WOB_K_TEXT);
|
|
T_CHECK(m.consts[1].s != NULL);
|
|
T_CHECK(m.consts[1].s->h.flags & WO_F_CONST); /* interned */
|
|
T_EQ(m.consts[1].s->len, 4);
|
|
T_CHECK(memcmp(m.consts[1].s->data, "main", 4) == 0);
|
|
T_EQ(m.class_cnt, 1);
|
|
T_EQ(m.classes[0].field_cnt, 2);
|
|
T_EQ(m.method_cnt, 1);
|
|
T_EQ(m.methods[0].reg_cnt, 2);
|
|
T_EQ(m.methods[0].ninstr, 2);
|
|
T_EQ(m.methods[0].line_cnt, 2);
|
|
T_EQ(m.methods[0].lines[1].pc, 1);
|
|
T_EQ(m.methods[0].lines[1].line, 11);
|
|
T_EQ(m.entry, 0);
|
|
wo_module_free(&m);
|
|
free(img);
|
|
}
|
|
|
|
/* build an image with one patched method-code instruction */
|
|
static uint8_t *image_with_code(uint32_t i0, uint32_t i1, size_t *len) {
|
|
wb_t *b = wb_new();
|
|
wb_const_int(b, 42);
|
|
uint32_t kname = wb_const_text(b, "main");
|
|
uint32_t code[] = {i0, i1};
|
|
wb_method(b, kname, WOB_NONE, 0, 2, code, 2, NULL, 0, NULL, 0);
|
|
return wb_finish(b, len);
|
|
}
|
|
|
|
static void expect_reject(uint8_t *img, size_t len, const char *what) {
|
|
wo_module m;
|
|
char err[256] = "";
|
|
int rc = wo_load_buf(&m, img, len, err, sizeof err);
|
|
T_EQ(rc, -1);
|
|
if (rc != 0 && err[0] == '\0')
|
|
fprintf(stderr, "empty error for reject case: %s\n", what);
|
|
T_CHECK(err[0] != '\0');
|
|
}
|
|
|
|
static void test_rejects(void) {
|
|
size_t len;
|
|
|
|
/* corrupted magic */
|
|
uint8_t *img = valid_image(&len);
|
|
img[0] ^= 0xFF;
|
|
expect_reject(img, len, "magic");
|
|
free(img);
|
|
|
|
/* unknown opcode */
|
|
img = image_with_code(wo_ins_abc(200, 0, 0, 0),
|
|
wo_ins_abc(WOP_RET, 0, 0, 0), &len);
|
|
expect_reject(img, len, "opcode");
|
|
free(img);
|
|
|
|
/* constant index out of range */
|
|
img = image_with_code(wo_ins_abx(WOP_LOADK, 0, 99),
|
|
wo_ins_abc(WOP_RET, 0, 0, 0), &len);
|
|
expect_reject(img, len, "const oob");
|
|
free(img);
|
|
|
|
/* register out of range (regc = 2) */
|
|
img = image_with_code(wo_ins_abc(WOP_MOVE, 5, 0, 0),
|
|
wo_ins_abc(WOP_RET, 0, 0, 0), &len);
|
|
expect_reject(img, len, "reg oob");
|
|
free(img);
|
|
|
|
/* last instruction is not a terminator */
|
|
img = image_with_code(wo_ins_abc(WOP_RET, 0, 0, 0),
|
|
wo_ins_abx(WOP_LOADK, 0, 0), &len);
|
|
expect_reject(img, len, "non-terminator tail");
|
|
free(img);
|
|
|
|
/* truncated buffer */
|
|
img = valid_image(&len);
|
|
expect_reject(img, len / 2, "truncated");
|
|
free(img);
|
|
}
|
|
|
|
int main(void) {
|
|
test_happy_path();
|
|
test_rejects();
|
|
return t_report("test_loader");
|
|
}
|