Page MenuHomePhorge

bytecode.c
No OneTemporary

Size
27 KB
Referenced Files
None
Subscribers
None

bytecode.c

#include "bytecode.h"
#include "ant.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
// ============================================================================
// Function operations
// ============================================================================
bc_func_t *bc_func_new(const char *name, uint8_t arity) {
bc_func_t *func = calloc(1, sizeof(bc_func_t));
if (!func) return NULL;
func->code_cap = 64;
func->code = malloc(sizeof(bc_inst_t) * func->code_cap);
func->const_cap = 16;
func->constants = malloc(sizeof(jsval_t) * func->const_cap);
func->names_cap = 16;
func->names = malloc(sizeof(char *) * func->names_cap);
func->name = name ? strdup(name) : NULL;
func->arity = arity;
func->nlocals = arity; // params are first locals
return func;
}
void bc_func_free(bc_func_t *func) {
if (!func) return;
free(func->code);
free(func->constants);
for (uint32_t i = 0; i < func->names_len; i++) {
free(func->names[i]);
}
free(func->names);
free((void *)func->name);
free(func);
}
uint32_t bc_func_emit(bc_func_t *func, bc_inst_t inst) {
if (func->code_len >= func->code_cap) {
func->code_cap *= 2;
func->code = realloc(func->code, sizeof(bc_inst_t) * func->code_cap);
}
func->code[func->code_len] = inst;
return func->code_len++;
}
uint32_t bc_func_emit_op(bc_func_t *func, bc_opcode_t op) {
return bc_func_emit(func, BC_MAKE(op, 0));
}
uint32_t bc_func_add_const(bc_func_t *func, jsval_t val) {
for (uint32_t i = 0; i < func->const_len; i++) {
if (func->constants[i] == val) return i;
}
if (func->const_len >= func->const_cap) {
func->const_cap *= 2;
func->constants = realloc(func->constants, sizeof(jsval_t) * func->const_cap);
}
func->constants[func->const_len] = val;
return func->const_len++;
}
uint32_t bc_func_add_name(bc_func_t *func, const char *name) {
for (uint32_t i = 0; i < func->names_len; i++) {
if (strcmp(func->names[i], name) == 0) return i;
}
if (func->names_len >= func->names_cap) {
func->names_cap *= 2;
func->names = realloc(func->names, sizeof(char *) * func->names_cap);
}
func->names[func->names_len] = strdup(name);
return func->names_len++;
}
// ============================================================================
// Chunk operations
// ============================================================================
bc_chunk_t *bc_chunk_new(void) {
bc_chunk_t *chunk = calloc(1, sizeof(bc_chunk_t));
if (!chunk) return NULL;
chunk->code_cap = 64;
chunk->code = malloc(sizeof(bc_inst_t) * chunk->code_cap);
chunk->const_cap = 16;
chunk->constants = malloc(sizeof(jsval_t) * chunk->const_cap);
chunk->names_cap = 16;
chunk->names = malloc(sizeof(char *) * chunk->names_cap);
chunk->func_cap = 8;
chunk->functions = malloc(sizeof(bc_func_t *) * chunk->func_cap);
return chunk;
}
void bc_chunk_free(bc_chunk_t *chunk) {
if (!chunk) return;
free(chunk->code);
free(chunk->constants);
for (uint32_t i = 0; i < chunk->names_len; i++) {
free(chunk->names[i]);
}
free(chunk->names);
for (uint32_t i = 0; i < chunk->func_len; i++) {
bc_func_free(chunk->functions[i]);
}
free(chunk->functions);
free(chunk->lines);
free(chunk);
}
uint32_t bc_emit(bc_chunk_t *chunk, bc_inst_t inst) {
if (chunk->code_len >= chunk->code_cap) {
chunk->code_cap *= 2;
chunk->code = realloc(chunk->code, sizeof(bc_inst_t) * chunk->code_cap);
}
chunk->code[chunk->code_len] = inst;
return chunk->code_len++;
}
uint32_t bc_emit_op(bc_chunk_t *chunk, bc_opcode_t op) {
return bc_emit(chunk, BC_MAKE(op, 0));
}
uint32_t bc_add_const(bc_chunk_t *chunk, jsval_t val) {
for (uint32_t i = 0; i < chunk->const_len; i++) {
if (chunk->constants[i] == val) return i;
}
if (chunk->const_len >= chunk->const_cap) {
chunk->const_cap *= 2;
chunk->constants = realloc(chunk->constants, sizeof(jsval_t) * chunk->const_cap);
}
chunk->constants[chunk->const_len] = val;
return chunk->const_len++;
}
uint32_t bc_add_name(bc_chunk_t *chunk, const char *name) {
for (uint32_t i = 0; i < chunk->names_len; i++) {
if (strcmp(chunk->names[i], name) == 0) return i;
}
if (chunk->names_len >= chunk->names_cap) {
chunk->names_cap *= 2;
chunk->names = realloc(chunk->names, sizeof(char *) * chunk->names_cap);
}
chunk->names[chunk->names_len] = strdup(name);
return chunk->names_len++;
}
uint32_t bc_add_func(bc_chunk_t *chunk, bc_func_t *func) {
if (chunk->func_len >= chunk->func_cap) {
chunk->func_cap *= 2;
chunk->functions = realloc(chunk->functions, sizeof(bc_func_t *) * chunk->func_cap);
}
chunk->functions[chunk->func_len] = func;
return chunk->func_len++;
}
void bc_patch(bc_chunk_t *chunk, uint32_t offset, bc_inst_t inst) {
if (offset < chunk->code_len) {
chunk->code[offset] = inst;
}
}
// ============================================================================
// VM operations
// ============================================================================
bc_vm_t *bc_vm_new(struct js *js) {
bc_vm_t *vm = calloc(1, sizeof(bc_vm_t));
if (!vm) return NULL;
vm->js = js;
return vm;
}
void bc_vm_free(bc_vm_t *vm) {
free(vm);
}
void bc_vm_reset(bc_vm_t *vm) {
vm->sp = 0;
vm->fp = 0;
vm->has_error = false;
vm->error = js_mkundef();
}
void bc_vm_push(bc_vm_t *vm, jsval_t val) {
if (vm->sp >= BC_STACK_MAX) {
vm->has_error = true;
vm->error = js_mkerr(vm->js, "Stack overflow");
return;
}
vm->stack[vm->sp++] = val;
}
jsval_t bc_vm_pop(bc_vm_t *vm) {
if (vm->sp == 0) {
vm->has_error = true;
vm->error = js_mkerr(vm->js, "Stack underflow");
return js_mkundef();
}
return vm->stack[--vm->sp];
}
jsval_t bc_vm_peek(bc_vm_t *vm, int distance) {
if (vm->sp <= (uint32_t)distance) return js_mkundef();
return vm->stack[vm->sp - 1 - distance];
}
void bc_vm_set_global(bc_vm_t *vm, uint8_t slot, jsval_t val) {
vm->globals[slot] = val;
if (slot >= vm->nglobals) vm->nglobals = slot + 1;
}
jsval_t bc_vm_get_global(bc_vm_t *vm, uint8_t slot) {
return vm->globals[slot];
}
// Execute bytecode in a function
static jsval_t bc_vm_execute(bc_vm_t *vm, bc_inst_t *code, uint32_t code_len,
jsval_t *constants, uint32_t const_len,
char **names, uint32_t names_len,
bc_func_t **functions, uint32_t func_len,
jsval_t *locals, uint8_t nlocals);
// Call a bytecode function
jsval_t bc_vm_call(bc_vm_t *vm, bc_func_t *func, jsval_t *args, int nargs) {
if (vm->fp >= BC_CALL_MAX) {
return js_mkerr(vm->js, "Call stack overflow");
}
bc_frame_t *frame = &vm->frames[vm->fp++];
frame->func = func;
frame->ip = 0;
frame->bp = vm->sp;
frame->nlocals = func->nlocals;
// Initialize locals with arguments
for (int i = 0; i < func->nlocals; i++) {
frame->locals[i] = (i < nargs && args) ? args[i] : js_mkundef();
}
jsval_t result = bc_vm_execute(vm, func->code, func->code_len,
func->constants, func->const_len,
func->names, func->names_len,
NULL, 0,
frame->locals, frame->nlocals);
vm->fp--;
return result;
}
// Main execution loop - computed goto (threaded code) dispatch
static jsval_t bc_vm_execute(bc_vm_t *vm, bc_inst_t *code, uint32_t code_len,
jsval_t *constants, uint32_t const_len,
char **names, uint32_t names_len,
bc_func_t **functions, uint32_t func_len,
jsval_t *locals, uint8_t nlocals) {
struct js *js = vm->js;
register bc_inst_t *ip = code;
bc_inst_t *ip_end = code + code_len;
// Stack operations - inlined for speed
jsval_t *stack = vm->stack;
register uint32_t sp = vm->sp;
#define PUSH(v) (stack[sp++] = (v))
#define POP() (stack[--sp])
#define PEEK(n) (stack[sp - 1 - (n)])
#define TOP() (stack[sp - 1])
// Dispatch table - only initialize used opcodes, rest are NULL
static const void *dispatch[256] = {
[OP_NOP] = &&L_NOP,
[OP_POP] = &&L_POP,
[OP_DUP] = &&L_DUP,
[OP_SWAP] = &&L_SWAP,
[OP_ROT3] = &&L_ROT3,
[OP_CONST] = &&L_CONST,
[OP_UNDEF] = &&L_UNDEF,
[OP_NULL] = &&L_NULL,
[OP_TRUE] = &&L_TRUE,
[OP_FALSE] = &&L_FALSE,
[OP_ZERO] = &&L_ZERO,
[OP_ONE] = &&L_ONE,
[OP_IMM_I8] = &&L_IMM_I8,
[OP_IMM_I24] = &&L_IMM_I24,
[OP_ADD] = &&L_ADD,
[OP_SUB] = &&L_SUB,
[OP_MUL] = &&L_MUL,
[OP_DIV] = &&L_DIV,
[OP_MOD] = &&L_MOD,
[OP_EXP] = &&L_EXP,
[OP_NEG] = &&L_NEG,
[OP_POS] = &&L_POS,
[OP_INC] = &&L_INC,
[OP_DEC] = &&L_DEC,
[OP_BITOR] = &&L_BITOR,
[OP_BITAND] = &&L_BITAND,
[OP_BITXOR] = &&L_BITXOR,
[OP_BITNOT] = &&L_BITNOT,
[OP_SHL] = &&L_SHL,
[OP_SHR] = &&L_SHR,
[OP_USHR] = &&L_USHR,
[OP_EQ] = &&L_EQ,
[OP_NE] = &&L_NE,
[OP_SEQ] = &&L_SEQ,
[OP_SNE] = &&L_SNE,
[OP_LT] = &&L_LT,
[OP_LE] = &&L_LE,
[OP_GT] = &&L_GT,
[OP_GE] = &&L_GE,
[OP_NOT] = &&L_NOT,
[OP_VOID] = &&L_VOID,
[OP_JMP] = &&L_JMP,
[OP_JMP_REL] = &&L_JMP_REL,
[OP_JT] = &&L_JT,
[OP_JF] = &&L_JF,
[OP_JT_REL] = &&L_JT_REL,
[OP_JF_REL] = &&L_JF_REL,
[OP_LOOP] = &&L_LOOP,
[OP_LOAD_LOCAL] = &&L_LOAD_LOCAL,
[OP_STORE_LOCAL] = &&L_STORE_LOCAL,
[OP_STORE_LOCAL_KEEP] = &&L_STORE_LOCAL_KEEP,
[OP_LOAD_GLOBAL] = &&L_LOAD_GLOBAL,
[OP_STORE_GLOBAL] = &&L_STORE_GLOBAL,
[OP_NEW_OBJ] = &&L_NEW_OBJ,
[OP_NEW_ARR] = &&L_NEW_ARR,
[OP_GET_PROP] = &&L_GET_PROP,
[OP_SET_PROP] = &&L_SET_PROP,
[OP_GET_ELEM] = &&L_GET_ELEM,
[OP_SET_ELEM] = &&L_SET_ELEM,
[OP_CALL] = &&L_CALL,
[OP_CLOSURE] = &&L_CLOSURE,
[OP_RET] = &&L_RET,
[OP_RET_UNDEF] = &&L_RET_UNDEF,
[OP_THIS] = &&L_THIS,
[OP_HALT] = &&L_HALT,
};
bc_inst_t inst;
uint32_t arg;
int32_t sarg;
#define FETCH() do { \
inst = *ip++; \
arg = BC_ARG(inst); \
} while(0)
#define DISPATCH() do { \
if (ip >= ip_end) goto L_DONE; \
FETCH(); \
const void *target = dispatch[BC_OP(inst)]; \
if (target) goto *target; \
goto L_UNKNOWN; \
} while(0)
// Start dispatch
DISPATCH();
// ===== Stack operations =====
L_NOP:
DISPATCH();
L_POP:
--sp;
DISPATCH();
L_DUP:
stack[sp] = stack[sp - 1];
sp++;
DISPATCH();
L_SWAP: {
jsval_t tmp = stack[sp - 1];
stack[sp - 1] = stack[sp - 2];
stack[sp - 2] = tmp;
DISPATCH();
}
L_ROT3: {
jsval_t c = stack[sp - 1];
jsval_t b = stack[sp - 2];
jsval_t a = stack[sp - 3];
stack[sp - 3] = b;
stack[sp - 2] = c;
stack[sp - 1] = a;
DISPATCH();
}
// ===== Constants =====
L_CONST:
PUSH(arg < const_len ? constants[arg] : js_mkundef());
DISPATCH();
L_UNDEF:
PUSH(js_mkundef());
DISPATCH();
L_NULL:
PUSH(js_mknull());
DISPATCH();
L_TRUE:
PUSH(js_mktrue());
DISPATCH();
L_FALSE:
PUSH(js_mkfalse());
DISPATCH();
L_ZERO:
PUSH(js_mknum(0));
DISPATCH();
L_ONE:
PUSH(js_mknum(1));
DISPATCH();
L_IMM_I8:
PUSH(js_mknum((int8_t)(arg & 0xFF)));
DISPATCH();
L_IMM_I24:
sarg = BC_SARG(inst);
PUSH(js_mknum(sarg));
DISPATCH();
// ===== Local/Global variables =====
L_LOAD_LOCAL:
PUSH(arg < nlocals ? locals[arg] : js_mkundef());
DISPATCH();
L_STORE_LOCAL:
if (arg < nlocals) locals[arg] = POP();
else --sp;
DISPATCH();
L_STORE_LOCAL_KEEP:
if (arg < nlocals) locals[arg] = TOP();
DISPATCH();
L_LOAD_GLOBAL:
PUSH(vm->globals[arg]);
DISPATCH();
L_STORE_GLOBAL:
vm->globals[arg] = POP();
if (arg >= vm->nglobals) vm->nglobals = (uint8_t)(arg + 1);
DISPATCH();
// ===== Arithmetic =====
L_ADD: {
jsval_t b = POP();
jsval_t a = POP();
PUSH(js_mknum(js_getnum(a) + js_getnum(b)));
DISPATCH();
}
L_SUB: {
jsval_t b = POP();
jsval_t a = POP();
PUSH(js_mknum(js_getnum(a) - js_getnum(b)));
DISPATCH();
}
L_MUL: {
jsval_t b = POP();
jsval_t a = POP();
PUSH(js_mknum(js_getnum(a) * js_getnum(b)));
DISPATCH();
}
L_DIV: {
jsval_t b = POP();
jsval_t a = POP();
PUSH(js_mknum(js_getnum(a) / js_getnum(b)));
DISPATCH();
}
L_MOD: {
jsval_t b = POP();
jsval_t a = POP();
PUSH(js_mknum(fmod(js_getnum(a), js_getnum(b))));
DISPATCH();
}
L_EXP: {
jsval_t b = POP();
jsval_t a = POP();
PUSH(js_mknum(pow(js_getnum(a), js_getnum(b))));
DISPATCH();
}
L_NEG:
stack[sp - 1] = js_mknum(-js_getnum(stack[sp - 1]));
DISPATCH();
L_POS:
stack[sp - 1] = js_mknum(js_getnum(stack[sp - 1]));
DISPATCH();
L_INC:
stack[sp - 1] = js_mknum(js_getnum(stack[sp - 1]) + 1);
DISPATCH();
L_DEC:
stack[sp - 1] = js_mknum(js_getnum(stack[sp - 1]) - 1);
DISPATCH();
// ===== Bitwise =====
L_BITOR: {
jsval_t b = POP(); jsval_t a = POP();
PUSH(js_mknum((double)(js_to_int32(js_getnum(a)) | js_to_int32(js_getnum(b)))));
DISPATCH();
}
L_BITAND: {
jsval_t b = POP(); jsval_t a = POP();
PUSH(js_mknum((double)(js_to_int32(js_getnum(a)) & js_to_int32(js_getnum(b)))));
DISPATCH();
}
L_BITXOR: {
jsval_t b = POP(); jsval_t a = POP();
PUSH(js_mknum((double)(js_to_int32(js_getnum(a)) ^ js_to_int32(js_getnum(b)))));
DISPATCH();
}
L_BITNOT: {
jsval_t a = POP();
PUSH(js_mknum((double)(~js_to_int32(js_getnum(a)))));
DISPATCH();
}
L_SHL: {
jsval_t b = POP(); jsval_t a = POP();
int32_t shift = js_to_uint32(js_getnum(b)) & 0x1F;
PUSH(js_mknum((double)(js_to_int32(js_getnum(a)) << shift)));
DISPATCH();
}
L_SHR: {
jsval_t b = POP(); jsval_t a = POP();
int32_t shift = js_to_uint32(js_getnum(b)) & 0x1F;
PUSH(js_mknum((double)(js_to_int32(js_getnum(a)) >> shift)));
DISPATCH();
}
L_USHR: {
jsval_t b = POP(); jsval_t a = POP();
uint32_t shift = js_to_uint32(js_getnum(b)) & 0x1F;
PUSH(js_mknum((double)(js_to_uint32(js_getnum(a)) >> shift)));
DISPATCH();
}
// ===== Comparisons =====
L_LT: {
jsval_t b = POP(); jsval_t a = POP();
PUSH(js_getnum(a) < js_getnum(b) ? js_mktrue() : js_mkfalse());
DISPATCH();
}
L_LE: {
jsval_t b = POP(); jsval_t a = POP();
PUSH(js_getnum(a) <= js_getnum(b) ? js_mktrue() : js_mkfalse());
DISPATCH();
}
L_GT: {
jsval_t b = POP(); jsval_t a = POP();
PUSH(js_getnum(a) > js_getnum(b) ? js_mktrue() : js_mkfalse());
DISPATCH();
}
L_GE: {
jsval_t b = POP(); jsval_t a = POP();
PUSH(js_getnum(a) >= js_getnum(b) ? js_mktrue() : js_mkfalse());
DISPATCH();
}
L_EQ:
L_SEQ: {
jsval_t b = POP(); jsval_t a = POP();
bool eq = (vtype(a) == vtype(b));
if (eq) {
if (vtype(a) == 5) eq = js_getnum(a) == js_getnum(b);
else eq = (a == b);
}
PUSH(eq ? js_mktrue() : js_mkfalse());
DISPATCH();
}
L_NE:
L_SNE: {
jsval_t b = POP(); jsval_t a = POP();
bool eq = (vtype(a) == vtype(b));
if (eq) {
if (vtype(a) == 5) eq = js_getnum(a) == js_getnum(b);
else eq = (a == b);
}
PUSH(!eq ? js_mktrue() : js_mkfalse());
DISPATCH();
}
// ===== Logical =====
L_NOT: {
jsval_t a = POP();
PUSH(!js_truthy(js, a) ? js_mktrue() : js_mkfalse());
DISPATCH();
}
L_VOID:
--sp;
PUSH(js_mkundef());
DISPATCH();
// ===== Control flow =====
L_JMP:
ip = code + arg;
DISPATCH();
L_JMP_REL:
sarg = BC_SARG(inst);
ip += sarg;
DISPATCH();
L_LOOP:
ip = code + arg;
DISPATCH();
L_JT: {
jsval_t cond = POP();
if (js_truthy(js, cond)) ip = code + arg;
DISPATCH();
}
L_JF: {
jsval_t cond = POP();
if (!js_truthy(js, cond)) ip = code + arg;
DISPATCH();
}
L_JT_REL: {
jsval_t cond = POP();
sarg = BC_SARG(inst);
if (js_truthy(js, cond)) ip += sarg;
DISPATCH();
}
L_JF_REL: {
jsval_t cond = POP();
sarg = BC_SARG(inst);
if (!js_truthy(js, cond)) ip += sarg;
DISPATCH();
}
// ===== Function calls =====
L_CALL: {
int nargs_call = (int)arg;
jsval_t *call_args = (nargs_call > 0) ? &stack[sp - nargs_call] : NULL;
jsval_t func_val = stack[sp - nargs_call - 1];
sp -= (nargs_call + 1);
vm->sp = sp; // sync before call
jsval_t result;
if (vtype(func_val) == 5) { // T_NUM - bytecode function index
int func_idx = (int)js_getnum(func_val);
if (functions && (uint32_t)func_idx < func_len) {
result = bc_vm_call(vm, functions[func_idx], call_args, nargs_call);
} else {
result = js_mkundef();
}
} else {
result = js_call(js, func_val, call_args, nargs_call);
}
sp = vm->sp; // sync after call
PUSH(result);
DISPATCH();
}
L_CLOSURE:
PUSH(js_mknum((double)arg));
DISPATCH();
// ===== Return =====
L_RET: {
jsval_t result = POP();
vm->sp = sp;
return result;
}
L_RET_UNDEF:
vm->sp = sp;
return js_mkundef();
// ===== Objects =====
L_NEW_OBJ:
vm->sp = sp;
PUSH(js_mkobj(js));
DISPATCH();
L_NEW_ARR:
vm->sp = sp;
PUSH(js_mkarr(js));
DISPATCH();
L_GET_PROP: {
jsval_t obj = POP();
vm->sp = sp;
jsval_t val = (arg < names_len) ? js_get(js, obj, names[arg]) : js_mkundef();
PUSH(val);
DISPATCH();
}
L_SET_PROP: {
jsval_t val = POP();
jsval_t obj = POP();
vm->sp = sp;
if (arg < names_len) js_set(js, obj, names[arg], val);
PUSH(val);
DISPATCH();
}
L_GET_ELEM: {
jsval_t key = POP();
jsval_t obj = POP();
vm->sp = sp;
if (vtype(key) == 5) {
char buf[32];
snprintf(buf, sizeof(buf), "%.0f", js_getnum(key));
PUSH(js_get(js, obj, buf));
} else {
PUSH(js_mkundef());
}
DISPATCH();
}
L_SET_ELEM: {
jsval_t val = POP();
jsval_t key = POP();
jsval_t obj = POP();
vm->sp = sp;
if (vtype(key) == 5) {
char buf[32];
snprintf(buf, sizeof(buf), "%.0f", js_getnum(key));
js_set(js, obj, buf, val);
}
PUSH(val);
DISPATCH();
}
L_THIS:
PUSH(js_getthis(js));
DISPATCH();
L_HALT:
goto L_DONE;
L_UNKNOWN:
fprintf(stderr, "Unknown opcode: 0x%02X\n", BC_OP(inst));
vm->sp = sp;
return js_mkerr(js, "Unknown opcode");
L_DONE:
vm->sp = sp;
return (sp > 0) ? stack[sp - 1] : js_mkundef();
#undef PUSH
#undef POP
#undef PEEK
#undef TOP
#undef FETCH
#undef DISPATCH
}
// Run a chunk (top-level code)
jsval_t bc_vm_run(bc_vm_t *vm, bc_chunk_t *chunk) {
if (!vm || !chunk || !chunk->code) {
return js_mkerr(vm->js, "Invalid bytecode");
}
bc_frame_t *frame = &vm->frames[0];
frame->func = NULL;
frame->chunk = chunk;
frame->ip = 0;
frame->bp = vm->sp;
frame->nlocals = chunk->nlocals;
// Initialize top-level locals
for (int i = 0; i < chunk->nlocals; i++) {
frame->locals[i] = js_mkundef();
}
vm->fp = 1;
return bc_vm_execute(vm, chunk->code, chunk->code_len,
chunk->constants, chunk->const_len,
chunk->names, chunk->names_len,
chunk->functions, chunk->func_len,
frame->locals, frame->nlocals);
}
// ============================================================================
// Disassembler
// ============================================================================
const char *bc_opcode_name(bc_opcode_t op) {
static const char *names[] = {
// Stack operations
[OP_NOP] = "NOP", [OP_POP] = "POP", [OP_DUP] = "DUP",
[OP_SWAP] = "SWAP", [OP_ROT3] = "ROT3",
[OP_DUP2] = "DUP2", [OP_POPN] = "POPN",
// Constants
[OP_CONST] = "CONST", [OP_UNDEF] = "UNDEF", [OP_NULL] = "NULL",
[OP_TRUE] = "TRUE", [OP_FALSE] = "FALSE",
[OP_ZERO] = "ZERO", [OP_ONE] = "ONE",
[OP_IMM_I8] = "IMM_I8", [OP_IMM_I24] = "IMM_I24",
[OP_GLOBAL_THIS] = "GLOBAL_THIS",
// Arithmetic
[OP_ADD] = "ADD", [OP_SUB] = "SUB", [OP_MUL] = "MUL",
[OP_DIV] = "DIV", [OP_MOD] = "MOD", [OP_EXP] = "EXP",
[OP_NEG] = "NEG", [OP_POS] = "POS", [OP_INC] = "INC", [OP_DEC] = "DEC",
[OP_INC_LOCAL] = "INC_LOCAL", [OP_DEC_LOCAL] = "DEC_LOCAL",
[OP_POST_INC_LOCAL] = "POST_INC_LOCAL", [OP_POST_DEC_LOCAL] = "POST_DEC_LOCAL",
// Bitwise
[OP_BITOR] = "BITOR", [OP_BITAND] = "BITAND", [OP_BITXOR] = "BITXOR",
[OP_BITNOT] = "BITNOT", [OP_SHL] = "SHL", [OP_SHR] = "SHR", [OP_USHR] = "USHR",
// Comparison
[OP_EQ] = "EQ", [OP_NE] = "NE", [OP_SEQ] = "SEQ", [OP_SNE] = "SNE",
[OP_LT] = "LT", [OP_LE] = "LE", [OP_GT] = "GT", [OP_GE] = "GE",
// Logical
[OP_NOT] = "NOT", [OP_TYPEOF] = "TYPEOF",
[OP_INSTANCEOF] = "INSTANCEOF", [OP_IN] = "IN", [OP_VOID] = "VOID",
[OP_DELETE] = "DELETE",
// Control flow
[OP_JMP] = "JMP", [OP_JMP_REL] = "JMP_REL", [OP_JT] = "JT", [OP_JF] = "JF",
[OP_JT_REL] = "JT_REL", [OP_JF_REL] = "JF_REL",
[OP_JNULLISH] = "JNULLISH", [OP_LOOP] = "LOOP",
[OP_JT_KEEP] = "JT_KEEP", [OP_JF_KEEP] = "JF_KEEP",
[OP_CASE] = "CASE", [OP_JNULLISH_POP] = "JNULLISH_POP",
// Variables
[OP_LOAD_LOCAL] = "LOAD_LOCAL", [OP_STORE_LOCAL] = "STORE_LOCAL",
[OP_LOAD_GLOBAL] = "LOAD_GLOBAL", [OP_STORE_GLOBAL] = "STORE_GLOBAL",
[OP_LOAD_UPVAL] = "LOAD_UPVAL", [OP_STORE_UPVAL] = "STORE_UPVAL",
[OP_LOAD_NAME] = "LOAD_NAME", [OP_STORE_NAME] = "STORE_NAME",
[OP_DEF_VAR] = "DEF_VAR", [OP_DEF_LET] = "DEF_LET", [OP_DEF_CONST] = "DEF_CONST",
[OP_DEL_NAME] = "DEL_NAME",
[OP_STORE_LOCAL_KEEP] = "STORE_LOCAL_KEEP", [OP_STORE_NAME_KEEP] = "STORE_NAME_KEEP",
// Objects & Arrays
[OP_NEW_OBJ] = "NEW_OBJ", [OP_NEW_ARR] = "NEW_ARR",
[OP_GET_PROP] = "GET_PROP", [OP_SET_PROP] = "SET_PROP",
[OP_GET_ELEM] = "GET_ELEM", [OP_SET_ELEM] = "SET_ELEM",
[OP_DEL_PROP] = "DEL_PROP", [OP_DEL_ELEM] = "DEL_ELEM",
[OP_INIT_PROP] = "INIT_PROP", [OP_INIT_ELEM] = "INIT_ELEM",
[OP_SPREAD] = "SPREAD", [OP_GET_SUPER] = "GET_SUPER",
[OP_SET_SUPER] = "SET_SUPER",
[OP_GET_PROP_OPT] = "GET_PROP_OPT", [OP_GET_ELEM_OPT] = "GET_ELEM_OPT",
[OP_INIT_COMPUTED] = "INIT_COMPUTED",
// Functions
[OP_CALL] = "CALL", [OP_CALL_METHOD] = "CALL_METHOD", [OP_NEW] = "NEW",
[OP_RET] = "RET", [OP_RET_UNDEF] = "RET_UNDEF", [OP_CLOSURE] = "CLOSURE",
[OP_THIS] = "THIS", [OP_ARGS] = "ARGS", [OP_REST] = "REST",
[OP_SUPER] = "SUPER", [OP_NEW_TARGET] = "NEW_TARGET",
[OP_CALL_OPT] = "CALL_OPT", [OP_SUPER_CALL] = "SUPER_CALL",
[OP_SPREAD_ARG] = "SPREAD_ARG",
// Iterators & Generators
[OP_GET_ITER] = "GET_ITER", [OP_ITER_NEXT] = "ITER_NEXT",
[OP_FOR_IN] = "FOR_IN", [OP_FOR_OF] = "FOR_OF",
[OP_YIELD] = "YIELD", [OP_YIELD_STAR] = "YIELD_STAR", [OP_AWAIT] = "AWAIT",
[OP_ITER_CLOSE] = "ITER_CLOSE", [OP_ASYNC_ITER] = "ASYNC_ITER",
// Exception handling
[OP_THROW] = "THROW", [OP_TRY_START] = "TRY_START", [OP_TRY_END] = "TRY_END",
[OP_CATCH] = "CATCH", [OP_FINALLY] = "FINALLY", [OP_RETHROW] = "RETHROW",
[OP_EXCEPTION] = "EXCEPTION",
// Scope
[OP_PUSH_SCOPE] = "PUSH_SCOPE", [OP_POP_SCOPE] = "POP_SCOPE",
[OP_PUSH_WITH] = "PUSH_WITH", [OP_POP_WITH] = "POP_WITH",
// Classes
[OP_CLASS] = "CLASS", [OP_CLASS_EXTEND] = "CLASS_EXTEND",
[OP_INIT_METHOD] = "INIT_METHOD", [OP_INIT_GETTER] = "INIT_GETTER",
[OP_INIT_SETTER] = "INIT_SETTER", [OP_INIT_STATIC] = "INIT_STATIC",
[OP_INIT_STATIC_GET] = "INIT_STATIC_GET", [OP_INIT_STATIC_SET] = "INIT_STATIC_SET",
[OP_INIT_FIELD] = "INIT_FIELD", [OP_INIT_PRIVATE] = "INIT_PRIVATE",
// Template literals
[OP_TEMPLATE] = "TEMPLATE", [OP_TAGGED_TEMPLATE] = "TAGGED_TEMPLATE",
// Destructuring
[OP_UNPACK_ARR] = "UNPACK_ARR", [OP_UNPACK_OBJ] = "UNPACK_OBJ",
[OP_UNPACK_REST] = "UNPACK_REST",
// Misc
[OP_DEBUG] = "DEBUG", [OP_LINE] = "LINE", [OP_COL] = "COL",
[OP_FILENAME] = "FILENAME", [OP_HALT] = "HALT",
};
if (op < sizeof(names) / sizeof(names[0]) && names[op]) return names[op];
return "???";
}
static void disasm_inst(bc_inst_t inst, uint32_t offset, char **names, uint32_t names_len) {
uint8_t op = BC_OP(inst);
uint32_t arg = BC_ARG(inst);
int32_t sarg = BC_SARG(inst);
printf("%04u %-16s", offset, bc_opcode_name(op));
switch (op) {
// Constant pool reference
case OP_CONST: printf(" @%u", arg); break;
// Immediates
case OP_IMM_I8: case OP_IMM_I24: printf(" %d", sarg); break;
case OP_POPN: case OP_TEMPLATE: printf(" %u", arg); break;
// Absolute jumps
case OP_JMP: case OP_JT: case OP_JF: case OP_LOOP:
case OP_JT_KEEP: case OP_JF_KEEP: case OP_JNULLISH: case OP_JNULLISH_POP:
case OP_CASE:
printf(" -> %u", arg); break;
// Relative jumps
case OP_JMP_REL: case OP_JT_REL: case OP_JF_REL:
printf(" %+d -> %u", sarg, (uint32_t)((int32_t)offset + 1 + sarg)); break;
// Local variable slots
case OP_LOAD_LOCAL: case OP_STORE_LOCAL: case OP_STORE_LOCAL_KEEP:
case OP_INC_LOCAL: case OP_DEC_LOCAL:
case OP_POST_INC_LOCAL: case OP_POST_DEC_LOCAL:
case OP_CATCH: case OP_REST:
printf(" $%u", arg); break;
// Global/upvalue slots
case OP_LOAD_GLOBAL: case OP_STORE_GLOBAL:
case OP_LOAD_UPVAL: case OP_STORE_UPVAL:
printf(" $%u", arg); break;
// Name table references
case OP_GET_PROP: case OP_SET_PROP: case OP_GET_PROP_OPT:
case OP_LOAD_NAME: case OP_STORE_NAME: case OP_STORE_NAME_KEEP:
case OP_DEL_PROP: case OP_DEL_NAME:
case OP_INIT_PROP: case OP_INIT_METHOD: case OP_INIT_GETTER: case OP_INIT_SETTER:
case OP_INIT_STATIC: case OP_INIT_STATIC_GET: case OP_INIT_STATIC_SET:
case OP_GET_SUPER: case OP_SET_SUPER:
case OP_DEF_VAR: case OP_DEF_LET: case OP_DEF_CONST:
printf(" '%s'", (arg < names_len) ? names[arg] : "?"); break;
// Call with arg count
case OP_CALL: case OP_CALL_METHOD: case OP_CALL_OPT:
case OP_NEW: case OP_SUPER_CALL:
printf(" (%u args)", arg); break;
// Function pool reference
case OP_CLOSURE: case OP_CLASS:
printf(" func[%u]", arg); break;
// Exception handling
case OP_TRY_START:
printf(" catch-> %u", arg); break;
// Destructuring
case OP_UNPACK_ARR: case OP_UNPACK_OBJ:
printf(" %u elements", arg); break;
// Source info
case OP_LINE: case OP_COL: case OP_FILENAME:
printf(" %u", arg); break;
default: if (arg) printf(" %u", arg); break;
}
printf("\n");
}
void bc_disassemble_func_inst(bc_func_t *func, uint32_t offset) {
if (offset < func->code_len) {
disasm_inst(func->code[offset], offset, func->names, func->names_len);
}
}
void bc_disassemble_inst(bc_chunk_t *chunk, uint32_t offset) {
if (offset < chunk->code_len) {
disasm_inst(chunk->code[offset], offset, chunk->names, chunk->names_len);
}
}
void bc_disassemble_func(bc_func_t *func) {
printf("--- function %s (arity=%d, locals=%d) ---\n",
func->name ? func->name : "<anonymous>", func->arity, func->nlocals);
for (uint32_t i = 0; i < func->code_len; i++) {
bc_disassemble_func_inst(func, i);
}
printf("\n");
}
void bc_disassemble(bc_chunk_t *chunk, const char *name) {
printf("=== %s ===\n", name ? name : "chunk");
if (chunk->func_len > 0) {
printf("Functions:\n");
for (uint32_t i = 0; i < chunk->func_len; i++) {
printf(" [%u] %s\n", i, chunk->functions[i]->name ? chunk->functions[i]->name : "<anon>");
}
}
printf("Code (locals=%d):\n", chunk->nlocals);
for (uint32_t i = 0; i < chunk->code_len; i++) {
bc_disassemble_inst(chunk, i);
}
printf("\n");
for (uint32_t i = 0; i < chunk->func_len; i++) {
bc_disassemble_func(chunk->functions[i]);
}
}

File Metadata

Mime Type
text/x-c
Expires
Thu, Mar 26, 9:12 PM (1 d, 22 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
512250
Default Alt Text
bytecode.c (27 KB)

Event Timeline