1
0
mirror of https://github.com/stedolan/jq.git synced 2024-05-11 05:55:39 +00:00
Files
stedolan-jq/execute.c

539 lines
14 KiB
C
Raw Normal View History

2012-08-16 01:00:30 +01:00
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "execute.h"
2012-08-16 01:00:30 +01:00
#include "opcode.h"
#include "bytecode.h"
#include "forkable_stack.h"
2012-08-21 12:35:36 +01:00
#include "frame_layout.h"
2012-08-16 01:00:30 +01:00
#include "jv_alloc.h"
2013-02-03 00:09:47 -06:00
#include "jq_parser.h"
#include "locfile.h"
2012-08-27 10:11:55 +01:00
#include "jv.h"
#include "jv_aux.h"
#include "parser.h"
#include "builtin.h"
2012-08-16 01:00:30 +01:00
typedef struct {
jv value;
2012-08-16 01:00:30 +01:00
int pathidx;
} stackval;
struct jq_state {
struct forkable_stack data_stk;
struct forkable_stack frame_stk;
struct forkable_stack fork_stk;
jv* pathbuf;
int pathsize; // number of allocated elements
int debug_trace_enabled;
};
2012-08-16 01:00:30 +01:00
int path_push(jq_state *jq, stackval sv, jv val) {
2012-08-16 01:00:30 +01:00
int pos = sv.pathidx;
assert(pos <= jq->pathsize);
2012-08-16 01:00:30 +01:00
assert(pos >= 0);
if (pos == jq->pathsize) {
int oldpathsize = jq->pathsize;
jq->pathsize = oldpathsize ? oldpathsize * 2 : 100;
jq->pathbuf = jv_mem_realloc(jq->pathbuf, sizeof(jq->pathbuf[0]) * jq->pathsize);
for (int i=oldpathsize; i<jq->pathsize; i++) {
jq->pathbuf[i] = jv_invalid();
}
2012-08-16 01:00:30 +01:00
}
jv_free(jq->pathbuf[pos]);
jq->pathbuf[pos] = val;
2012-08-16 01:00:30 +01:00
return pos + 1;
}
stackval stackval_replace(stackval value, jv newjs) {
jv_free(value.value);
2012-08-16 01:00:30 +01:00
stackval s = {newjs, value.pathidx};
return s;
}
// Probably all uses of this function are bugs
stackval stackval_root(jv v) {
2012-08-16 01:00:30 +01:00
stackval s = {v, 0};
return s;
}
typedef struct {
FORKABLE_STACK_HEADER;
stackval sv;
} data_stk_elem;
void stack_push(jq_state *jq, stackval val) {
2012-09-04 15:34:34 +01:00
assert(jv_is_valid(val.value));
data_stk_elem* s = forkable_stack_push(&jq->data_stk, sizeof(data_stk_elem));
2012-08-16 01:00:30 +01:00
s->sv = val;
}
stackval stack_pop(jq_state *jq) {
data_stk_elem* s = forkable_stack_peek(&jq->data_stk);
2012-08-16 01:00:30 +01:00
stackval sv = s->sv;
if (!forkable_stack_pop_will_free(&jq->data_stk)) {
sv.value = jv_copy(sv.value);
}
forkable_stack_pop(&jq->data_stk);
2012-09-04 15:34:34 +01:00
assert(jv_is_valid(sv.value));
2012-08-16 01:00:30 +01:00
return sv;
}
struct forkpoint {
FORKABLE_STACK_HEADER;
struct forkable_stack_state saved_data_stack;
struct forkable_stack_state saved_call_stack;
};
void stack_save(jq_state *jq){
struct forkpoint* fork = forkable_stack_push(&jq->fork_stk, sizeof(struct forkpoint));
forkable_stack_save(&jq->data_stk, &fork->saved_data_stack);
forkable_stack_save(&jq->frame_stk, &fork->saved_call_stack);
2012-08-16 01:00:30 +01:00
}
void stack_switch(jq_state *jq) {
struct forkpoint* fork = forkable_stack_peek(&jq->fork_stk);
forkable_stack_switch(&jq->data_stk, &fork->saved_data_stack);
forkable_stack_switch(&jq->frame_stk, &fork->saved_call_stack);
2012-08-16 01:00:30 +01:00
}
int stack_restore(jq_state *jq){
while (!forkable_stack_empty(&jq->data_stk) &&
forkable_stack_pop_will_free(&jq->data_stk)) {
jv_free(stack_pop(jq).value);
}
while (!forkable_stack_empty(&jq->frame_stk) &&
forkable_stack_pop_will_free(&jq->frame_stk)) {
frame_pop(&jq->frame_stk);
}
if (forkable_stack_empty(&jq->fork_stk)) {
return 0;
} else {
struct forkpoint* fork = forkable_stack_peek(&jq->fork_stk);
forkable_stack_restore(&jq->data_stk, &fork->saved_data_stack);
forkable_stack_restore(&jq->frame_stk, &fork->saved_call_stack);
forkable_stack_pop(&jq->fork_stk);
return 1;
}
2012-08-16 01:00:30 +01:00
}
2012-08-21 18:14:13 +01:00
static struct closure make_closure(struct forkable_stack* stk, frame_ptr fr, uint16_t* pc) {
uint16_t level = *pc++;
uint16_t idx = *pc++;
fr = frame_get_level(stk, fr, level);
if (idx & ARG_NEWCLOSURE) {
int subfn_idx = idx & ~ARG_NEWCLOSURE;
assert(subfn_idx < frame_self(fr)->bc->nsubfunctions);
struct closure cl = {frame_self(fr)->bc->subfunctions[subfn_idx],
forkable_stack_to_idx(stk, fr)};
return cl;
2012-08-21 18:14:13 +01:00
} else {
return *frame_closure_arg(fr, idx);
}
}
2012-08-27 10:11:55 +01:00
void print_error(jv value) {
assert(!jv_is_valid(value));
jv msg = jv_invalid_get_msg(value);
if (jv_get_kind(msg) == JV_KIND_STRING) {
fprintf(stderr, "jq: error: %s\n", jv_string_value(msg));
}
jv_free(msg);
}
2012-08-16 01:00:30 +01:00
#define ON_BACKTRACK(op) ((op)+NUM_OPCODES)
jv jq_next(jq_state *jq) {
jv cfunc_input[MAX_CFUNCTION_ARGS];
2012-08-21 12:35:36 +01:00
assert(!forkable_stack_empty(&jq->frame_stk));
uint16_t* pc = *frame_current_retaddr(&jq->frame_stk);
frame_pop(&jq->frame_stk);
assert(!forkable_stack_empty(&jq->frame_stk));
2012-08-21 12:35:36 +01:00
2012-08-16 01:00:30 +01:00
int backtracking = 0;
while (1) {
uint16_t opcode = *pc;
2012-08-16 01:00:30 +01:00
if (jq->debug_trace_enabled) {
dump_operation(frame_current_bytecode(&jq->frame_stk), pc);
printf("\t");
const struct opcode_description* opdesc = opcode_describe(opcode);
data_stk_elem* param;
int stack_in = opdesc->stack_in;
if (stack_in == -1) stack_in = pc[1];
for (int i=0; i<stack_in; i++) {
if (i == 0) {
param = forkable_stack_peek(&jq->data_stk);
} else {
printf(" | ");
param = forkable_stack_peek_next(&jq->data_stk, param);
}
if (!param) break;
jv_dump(jv_copy(param->sv.value), 0);
printf("<%d>", jv_get_refcnt(param->sv.value));
2012-08-21 12:35:36 +01:00
}
2012-08-16 01:00:30 +01:00
if (backtracking) printf("\t<backtracking>");
printf("\n");
}
2012-08-16 01:00:30 +01:00
if (backtracking) {
opcode = ON_BACKTRACK(opcode);
backtracking = 0;
}
pc++;
2012-08-16 01:00:30 +01:00
switch (opcode) {
default: assert(0 && "invalid instruction");
case LOADK: {
jv v = jv_array_get(jv_copy(frame_current_bytecode(&jq->frame_stk)->constants), *pc++);
assert(jv_is_valid(v));
stack_push(jq, stackval_replace(stack_pop(jq), v));
2012-08-16 01:00:30 +01:00
break;
}
case DUP: {
stackval v = stack_pop(jq);
stackval v2 = v;
v2.value = jv_copy(v.value);
stack_push(jq, v);
stack_push(jq, v2);
2012-08-16 01:00:30 +01:00
break;
}
case DUP2: {
stackval keep = stack_pop(jq);
stackval v = stack_pop(jq);
stackval v2 = v;
v2.value = jv_copy(v.value);
stack_push(jq, v);
stack_push(jq, keep);
stack_push(jq, v2);
break;
}
2012-08-16 01:00:30 +01:00
case SWAP: {
stackval a = stack_pop(jq);
stackval b = stack_pop(jq);
stack_push(jq, a);
stack_push(jq, b);
2012-08-16 01:00:30 +01:00
break;
}
case POP: {
jv_free(stack_pop(jq).value);
2012-08-16 01:00:30 +01:00
break;
}
case APPEND: {
// FIXME paths
jv v = stack_pop(jq).value;
uint16_t level = *pc++;
uint16_t vidx = *pc++;
frame_ptr fp = frame_get_level(&jq->frame_stk, frame_current(&jq->frame_stk), level);
jv* var = frame_local_var(fp, vidx);
assert(jv_get_kind(*var) == JV_KIND_ARRAY);
*var = jv_array_append(*var, v);
2012-08-16 01:00:30 +01:00
break;
}
case INSERT: {
stackval stktop = stack_pop(jq);
jv v = stack_pop(jq).value;
jv k = stack_pop(jq).value;
stackval objv = stack_pop(jq);
assert(jv_get_kind(k) == JV_KIND_STRING);
assert(jv_get_kind(objv.value) == JV_KIND_OBJECT);
objv.value = jv_object_set(objv.value, k, v);
stack_push(jq, objv);
stack_push(jq, stktop);
2012-08-16 01:00:30 +01:00
break;
}
// FIXME: loadv/storev may do too much copying/freeing
2012-08-16 01:00:30 +01:00
case LOADV: {
2012-08-21 18:24:38 +01:00
uint16_t level = *pc++;
2012-08-16 01:00:30 +01:00
uint16_t v = *pc++;
frame_ptr fp = frame_get_level(&jq->frame_stk, frame_current(&jq->frame_stk), level);
jv* var = frame_local_var(fp, v);
if (jq->debug_trace_enabled) {
printf("V%d = ", v);
jv_dump(jv_copy(*var), 0);
printf("\n");
}
stack_push(jq, stackval_replace(stack_pop(jq), jv_copy(*var)));
2012-08-16 01:00:30 +01:00
break;
}
case STOREV: {
2012-08-21 18:24:38 +01:00
uint16_t level = *pc++;
2012-08-16 01:00:30 +01:00
uint16_t v = *pc++;
frame_ptr fp = frame_get_level(&jq->frame_stk, frame_current(&jq->frame_stk), level);
jv* var = frame_local_var(fp, v);
stackval val = stack_pop(jq);
if (jq->debug_trace_enabled) {
printf("V%d = ", v);
jv_dump(jv_copy(val.value), 0);
printf("\n");
}
jv_free(*var);
2012-08-21 12:35:36 +01:00
*var = val.value;
2012-08-16 01:00:30 +01:00
break;
}
case GETPATH: {
stackval path_end = stack_pop(jq);
stackval path_start = stack_pop(jq);
jv_free(path_end.value);
jv path = jv_array();
for (int i=path_start.pathidx; i<path_end.pathidx; i++) {
path = jv_array_set(path, i, jv_copy(jq->pathbuf[i]));
}
stack_push(jq, stackval_replace(path_start, path));
break;
}
2012-08-16 01:00:30 +01:00
case INDEX: {
stackval t = stack_pop(jq);
jv k = stack_pop(jq).value;
int pathidx = path_push(jq, t, jv_copy(k));
jv v = jv_get(t.value, k);
if (jv_is_valid(v)) {
2012-08-27 10:11:55 +01:00
stackval sv;
sv.value = v;
sv.pathidx = pathidx;
stack_push(jq, sv);
2012-08-16 01:00:30 +01:00
} else {
print_error(v);
goto do_backtrack;
2012-08-16 01:00:30 +01:00
}
break;
}
case JUMP: {
uint16_t offset = *pc++;
pc += offset;
break;
}
2012-09-04 15:34:34 +01:00
case JUMP_F: {
uint16_t offset = *pc++;
stackval t = stack_pop(jq);
2012-09-04 15:34:34 +01:00
jv_kind kind = jv_get_kind(t.value);
if (kind == JV_KIND_FALSE || kind == JV_KIND_NULL) {
pc += offset;
}
stack_push(jq, t); // FIXME do this better
2012-09-04 15:34:34 +01:00
break;
}
2012-08-16 01:00:30 +01:00
case EACH:
stack_push(jq, stackval_root(jv_number(-1)));
2012-08-16 01:00:30 +01:00
// fallthrough
case ON_BACKTRACK(EACH): {
int idx = jv_number_value(stack_pop(jq).value);
stackval container = stack_pop(jq);
int keep_going;
jv key, value;
if (jv_get_kind(container.value) == JV_KIND_ARRAY) {
if (opcode == EACH) idx = 0;
else idx = idx + 1;
keep_going = idx < jv_array_length(jv_copy(container.value));
if (keep_going) {
key = jv_number(idx);
value = jv_array_get(jv_copy(container.value), idx);
}
} else if (jv_get_kind(container.value) == JV_KIND_OBJECT) {
if (opcode == EACH) idx = jv_object_iter(container.value);
else idx = jv_object_iter_next(container.value, idx);
keep_going = jv_object_iter_valid(container.value, idx);
if (keep_going) {
key = jv_object_iter_key(container.value, idx);
value = jv_object_iter_value(container.value, idx);
}
} else {
assert(opcode == EACH);
print_error(jv_invalid_with_msg(jv_string_fmt("Cannot iterate over %s",
jv_kind_name(jv_get_kind(container.value)))));
keep_going = 0;
}
if (!keep_going) {
jv_free(container.value);
2012-08-16 01:00:30 +01:00
goto do_backtrack;
} else {
stack_save(jq);
stack_push(jq, container);
stack_push(jq, stackval_root(jv_number(idx)));
frame_push_backtrack(&jq->frame_stk, pc - 1);
stack_switch(jq);
2012-08-16 01:00:30 +01:00
stackval sv = {value,
path_push(jq, container, key)};
stack_push(jq, sv);
2012-08-16 01:00:30 +01:00
}
break;
}
do_backtrack:
case BACKTRACK: {
if (!stack_restore(jq)) {
return jv_invalid();
2012-08-16 01:00:30 +01:00
}
pc = *frame_current_retaddr(&jq->frame_stk);
frame_pop(&jq->frame_stk);
2012-08-16 01:00:30 +01:00
backtracking = 1;
break;
}
case FORK: {
stack_save(jq);
frame_push_backtrack(&jq->frame_stk, pc - 1);
stack_switch(jq);
2012-08-16 01:00:30 +01:00
pc++; // skip offset this time
break;
}
2012-08-27 10:11:55 +01:00
2012-08-16 01:00:30 +01:00
case ON_BACKTRACK(FORK): {
uint16_t offset = *pc++;
pc += offset;
break;
}
case YIELD: {
jv value = stack_pop(jq).value;
frame_push_backtrack(&jq->frame_stk, pc);
2012-08-16 01:00:30 +01:00
return value;
}
case CALL_BUILTIN: {
int nargs = *pc++;
stackval top = stack_pop(jq);
2012-08-16 01:00:30 +01:00
cfunc_input[0] = top.value;
for (int i = 1; i < nargs; i++) {
cfunc_input[i] = stack_pop(jq).value;
}
struct cfunction* func = &frame_current_bytecode(&jq->frame_stk)->globals->cfunctions[*pc++];
top.value = cfunction_invoke(func, cfunc_input);
if (jv_is_valid(top.value)) {
stack_push(jq, top);
} else {
print_error(top.value);
goto do_backtrack;
}
2012-08-16 01:00:30 +01:00
break;
}
2012-08-21 12:35:36 +01:00
case CALL_JQ: {
2012-08-21 18:14:13 +01:00
uint16_t nclosures = *pc++;
uint16_t* retaddr = pc + 2 + nclosures*2;
frame_ptr new_frame = frame_push(&jq->frame_stk,
make_closure(&jq->frame_stk, frame_current(&jq->frame_stk), pc),
retaddr);
2012-08-21 18:14:13 +01:00
pc += 2;
frame_ptr old_frame = forkable_stack_peek_next(&jq->frame_stk, new_frame);
assert(nclosures == frame_self(new_frame)->bc->nclosures);
for (int i=0; i<nclosures; i++) {
*frame_closure_arg(new_frame, i) = make_closure(&jq->frame_stk, old_frame, pc);
2012-08-21 18:14:13 +01:00
pc += 2;
}
pc = frame_current_bytecode(&jq->frame_stk)->code;
2012-08-21 18:14:13 +01:00
break;
}
case RET: {
pc = *frame_current_retaddr(&jq->frame_stk);
frame_pop(&jq->frame_stk);
2012-08-21 18:14:13 +01:00
break;
2012-08-21 12:35:36 +01:00
}
2012-08-16 01:00:30 +01:00
}
}
}
void jq_init(struct bytecode* bc, jv input, jq_state **jq, int flags) {
jq_state *new_jq;
new_jq = jv_mem_alloc(sizeof(*new_jq));
memset(new_jq, 0, sizeof(*new_jq));
forkable_stack_init(&new_jq->data_stk, sizeof(stackval) * 100);
forkable_stack_init(&new_jq->frame_stk, 1024);
forkable_stack_init(&new_jq->fork_stk, 1024);
2012-08-16 01:00:30 +01:00
stack_push(new_jq, stackval_root(input));
struct closure top = {bc, -1};
frame_push(&new_jq->frame_stk, top, 0);
frame_push_backtrack(&new_jq->frame_stk, bc->code);
if (flags & JQ_DEBUG_TRACE) {
new_jq->debug_trace_enabled = 1;
} else {
new_jq->debug_trace_enabled = 0;
}
*jq = new_jq;
2012-08-16 01:00:30 +01:00
}
void jq_teardown(jq_state **jq) {
jq_state *old_jq = *jq;
if (old_jq == NULL)
return;
*jq = NULL;
while (stack_restore(old_jq)) {}
assert(forkable_stack_empty(&old_jq->fork_stk));
assert(forkable_stack_empty(&old_jq->data_stk));
assert(forkable_stack_empty(&old_jq->frame_stk));
forkable_stack_free(&old_jq->fork_stk);
forkable_stack_free(&old_jq->data_stk);
forkable_stack_free(&old_jq->frame_stk);
for (int i=0; i<old_jq->pathsize; i++) {
jv_free(old_jq->pathbuf[i]);
}
jv_mem_free(old_jq->pathbuf);
jv_mem_free(old_jq);
}
struct bytecode* jq_compile_args(const char* str, jv args) {
assert(jv_get_kind(args) == JV_KIND_ARRAY);
struct locfile locations;
locfile_init(&locations, str, strlen(str));
block program;
struct bytecode* bc = 0;
int nerrors = jq_parse(&locations, &program);
if (nerrors == 0) {
for (int i=0; i<jv_array_length(jv_copy(args)); i++) {
jv arg = jv_array_get(jv_copy(args), i);
jv name = jv_object_get(jv_copy(arg), jv_string("name"));
jv value = jv_object_get(arg, jv_string("value"));
program = gen_var_binding(gen_const(value), jv_string_value(name), program);
jv_free(name);
}
jv_free(args);
program = builtins_bind(program);
nerrors = block_compile(program, &locations, &bc);
}
if (nerrors) {
fprintf(stderr, "%d compile %s\n", nerrors, nerrors > 1 ? "errors" : "error");
}
locfile_free(&locations);
return bc;
}
struct bytecode* jq_compile(const char* str) {
return jq_compile_args(str, jv_array());
}