mirror of
https://github.com/stedolan/jq.git
synced 2024-05-11 05:55:39 +00:00
161 lines
4.5 KiB
C
161 lines
4.5 KiB
C
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "bytecode.h"
|
|
#include "jv_alloc.h"
|
|
|
|
// flags, length
|
|
#define NONE 0, 1
|
|
#define CONSTANT OP_HAS_CONSTANT, 2
|
|
#define VARIABLE (OP_HAS_VARIABLE | OP_HAS_BINDING), 3
|
|
#define BRANCH OP_HAS_BRANCH, 2
|
|
#define CFUNC (OP_HAS_CFUNC | OP_HAS_BINDING), 3
|
|
#define UFUNC (OP_HAS_UFUNC | OP_HAS_BINDING | OP_IS_CALL_PSEUDO), 4
|
|
#define DEFINITION (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 0
|
|
#define CLOSURE_REF_IMM (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 2
|
|
|
|
#define OP(name, imm, in, out) \
|
|
{name, #name, imm, in, out},
|
|
|
|
static const struct opcode_description opcode_descriptions[] = {
|
|
#include "opcode_list.h"
|
|
};
|
|
|
|
static const struct opcode_description invalid_opcode_description = {
|
|
-1, "#INVALID", 0, 0, 0, 0
|
|
};
|
|
|
|
|
|
const struct opcode_description* opcode_describe(opcode op) {
|
|
if ((int)op >= 0 && (int)op < NUM_OPCODES) {
|
|
return &opcode_descriptions[op];
|
|
} else {
|
|
return &invalid_opcode_description;
|
|
}
|
|
}
|
|
|
|
|
|
static int bytecode_operation_length(uint16_t* codeptr) {
|
|
int length = opcode_describe(*codeptr)->length;
|
|
if (*codeptr == CALL_JQ) {
|
|
length += codeptr[1] * 2;
|
|
}
|
|
return length;
|
|
}
|
|
|
|
static void dump_code(int indent, struct bytecode* bc) {
|
|
int pc = 0;
|
|
while (pc < bc->codelen) {
|
|
printf("%*s", indent, "");
|
|
dump_operation(bc, bc->code + pc);
|
|
printf("\n");
|
|
pc += bytecode_operation_length(bc->code + pc);
|
|
}
|
|
}
|
|
|
|
static void symbol_table_free(struct symbol_table* syms) {
|
|
jv_mem_free(syms->cfunctions);
|
|
jv_free(syms->cfunc_names);
|
|
jv_mem_free(syms);
|
|
}
|
|
|
|
void dump_disassembly(int indent, struct bytecode* bc) {
|
|
if (bc->nclosures > 0) {
|
|
printf("%*s[params: ", indent, "");
|
|
jv params = jv_object_get(jv_copy(bc->debuginfo), jv_string("params"));
|
|
for (int i=0; i<bc->nclosures; i++) {
|
|
if (i) printf(", ");
|
|
jv name = jv_array_get(jv_copy(params), i);
|
|
printf("%s", jv_string_value(name));
|
|
jv_free(name);
|
|
}
|
|
jv_free(params);
|
|
printf("]\n");
|
|
}
|
|
dump_code(indent, bc);
|
|
for (int i=0; i<bc->nsubfunctions; i++) {
|
|
struct bytecode* subfn = bc->subfunctions[i];
|
|
jv name = jv_object_get(jv_copy(subfn->debuginfo), jv_string("name"));
|
|
printf("%*s%s:%d:\n", indent, "", jv_string_value(name), i);
|
|
jv_free(name);
|
|
dump_disassembly(indent+2, subfn);
|
|
}
|
|
}
|
|
|
|
static struct bytecode* getlevel(struct bytecode* bc, int level) {
|
|
while (level > 0) {
|
|
bc = bc->parent;
|
|
level--;
|
|
}
|
|
return bc;
|
|
}
|
|
|
|
void dump_operation(struct bytecode* bc, uint16_t* codeptr) {
|
|
int pc = codeptr - bc->code;
|
|
printf("%04d ", pc);
|
|
const struct opcode_description* op = opcode_describe(bc->code[pc++]);
|
|
printf("%s", op->name);
|
|
if (op->length > 1) {
|
|
uint16_t imm = bc->code[pc++];
|
|
if (op->op == CALL_JQ) {
|
|
for (int i=0; i<imm+1; i++) {
|
|
uint16_t level = bc->code[pc++];
|
|
uint16_t idx = bc->code[pc++];
|
|
jv name;
|
|
if (idx & ARG_NEWCLOSURE) {
|
|
idx &= ~ARG_NEWCLOSURE;
|
|
name = jv_object_get(jv_copy(getlevel(bc,level)->subfunctions[idx]->debuginfo),
|
|
jv_string("name"));
|
|
} else {
|
|
name = jv_array_get(jv_object_get(jv_copy(getlevel(bc,level)->debuginfo),
|
|
jv_string("params")), idx);
|
|
}
|
|
printf(" %s:%d",
|
|
jv_string_value(name),
|
|
idx);
|
|
jv_free(name);
|
|
if (level) {
|
|
printf("^%d", level);
|
|
}
|
|
}
|
|
} else if (op->op == CALL_BUILTIN) {
|
|
int func = bc->code[pc++];
|
|
jv name = jv_array_get(jv_copy(bc->globals->cfunc_names), func);
|
|
printf(" %s", jv_string_value(name));
|
|
jv_free(name);
|
|
} else if (op->flags & OP_HAS_BRANCH) {
|
|
printf(" %04d", pc + imm);
|
|
} else if (op->flags & OP_HAS_CONSTANT) {
|
|
printf(" ");
|
|
jv_dump(jv_array_get(jv_copy(bc->constants), imm), 0);
|
|
} else if (op->flags & OP_HAS_VARIABLE) {
|
|
uint16_t v = bc->code[pc++];
|
|
jv name = jv_array_get(jv_object_get(jv_copy(getlevel(bc,imm)->debuginfo), jv_string("locals")), v);
|
|
printf(" $%s:%d",
|
|
jv_string_value(name),
|
|
v);
|
|
jv_free(name);
|
|
if (imm) {
|
|
printf("^%d", imm);
|
|
}
|
|
} else {
|
|
printf(" %d", imm);
|
|
}
|
|
}
|
|
}
|
|
|
|
void bytecode_free(struct bytecode* bc) {
|
|
if (!bc)
|
|
return;
|
|
jv_mem_free(bc->code);
|
|
jv_free(bc->constants);
|
|
for (int i=0; i<bc->nsubfunctions; i++)
|
|
bytecode_free(bc->subfunctions[i]);
|
|
if (!bc->parent)
|
|
symbol_table_free(bc->globals);
|
|
jv_mem_free(bc->subfunctions);
|
|
jv_free(bc->debuginfo);
|
|
jv_mem_free(bc);
|
|
}
|