diff --git a/librz/analysis/op.c b/librz/analysis/op.c index 5041952d89..9def8d35dd 100644 --- a/librz/analysis/op.c +++ b/librz/analysis/op.c @@ -54,8 +54,7 @@ RZ_API bool rz_analysis_op_fini(RzAnalysisOp *op) { op->switch_op = NULL; RZ_FREE(op->mnemonic); if (op->rzil_op) { - // TODO free root_node once it is implemented - rz_pvector_free(op->rzil_op->ops); + rz_il_op_effect_free(op->rzil_op->op); RZ_FREE(op->rzil_op); } return true; diff --git a/librz/analysis/p/analysis_bf.c b/librz/analysis/p/analysis_bf.c index 93a29ff4c3..40c310a3af 100644 --- a/librz/analysis/p/analysis_bf.c +++ b/librz/analysis/p/analysis_bf.c @@ -77,49 +77,45 @@ ut64 parse_label_id(char *lbl_name) { return addr; } -RzPVector *bf_right_arrow(RzILVM *vm, ut64 id) { +RzILOpEffect *bf_right_arrow(RzILVM *vm, ut64 id) { // (set ptr (+ (val ptr) (int 1))) RzILOpBitVector *add = rz_il_op_new_add(bf_il_ptr(), bf_il_one(BF_ADDR_SIZE)); - return rz_il_make_oplist(1, bf_il_set_ptr(add)); + return bf_il_set_ptr(add); } -RzPVector *bf_left_arrow(RzILVM *vm, ut64 id) { +RzILOpEffect *bf_left_arrow(RzILVM *vm, ut64 id) { // (set ptr (- (val ptr) (int 1))) RzILOpBitVector *sub = rz_il_op_new_sub(bf_il_ptr(), bf_il_one(BF_ADDR_SIZE)); - return rz_il_make_oplist(1, bf_il_set_ptr(sub)); + return bf_il_set_ptr(sub); } -RzPVector *bf_inc(RzILVM *vm, ut64 id) { +RzILOpEffect *bf_inc(RzILVM *vm, ut64 id) { // (store mem (var ptr) (+ (load (var ptr)) (int 1))) // mem == 0 because is the only mem in bf RzILOpBitVector *load = rz_il_op_new_load(0, bf_il_ptr()); RzILOpBitVector *add = rz_il_op_new_add(load, bf_il_one(BF_BYTE_SIZE)); - RzILOpEffect *store = rz_il_op_new_store(0, bf_il_ptr(), add); - return rz_il_make_oplist(1, store); + return rz_il_op_new_store(0, bf_il_ptr(), add); } -RzPVector *bf_dec(RzILVM *vm, ut64 id) { +RzILOpEffect *bf_dec(RzILVM *vm, ut64 id) { // (store mem (var ptr) (- (load (var ptr)) (int 1))) // mem == 0 because is the only mem in bf RzILOpBitVector *load = rz_il_op_new_load(0, bf_il_ptr()); RzILOpBitVector *sub = rz_il_op_new_sub(load, bf_il_one(BF_BYTE_SIZE)); - RzILOpEffect *store = rz_il_op_new_store(0, bf_il_ptr(), sub); - return rz_il_make_oplist(1, store); + return rz_il_op_new_store(0, bf_il_ptr(), sub); } -RzPVector *bf_out(RzILVM *vm, ut64 id) { +RzILOpEffect *bf_out(RzILVM *vm, ut64 id) { // (goto write) - RzILOpEffect *goto_ = rz_il_op_new_goto("write"); - return rz_il_make_oplist(1, goto_); + return rz_il_op_new_goto("write"); } -RzPVector *bf_in(RzILVM *vm, ut64 id) { +RzILOpEffect *bf_in(RzILVM *vm, ut64 id) { // (goto hook_read) - RzILOpEffect *goto_ = rz_il_op_new_goto("read"); - return rz_il_make_oplist(1, goto_); + return rz_il_op_new_goto("read"); } -RzPVector *bf_llimit(RzILVM *vm, BfContext *ctx, ut64 id, ut64 addr) { +RzILOpEffect *bf_llimit(RzILVM *vm, BfContext *ctx, ut64 id, ut64 addr) { // (perform (branch (load mem (var ptr)) // (do nothing) // (goto ])) @@ -159,13 +155,10 @@ RzPVector *bf_llimit(RzILVM *vm, BfContext *ctx, ut64 id, ut64 addr) { RzILOpEffect *goto_ = rz_il_op_new_goto(dst_label->label_id); // branch if (load mem (var ptr)) is false then goto ] - RzILOpEffect *branch = rz_il_op_new_branch(cond, NULL, goto_); - - // perform - return rz_il_make_oplist(1, branch); + return rz_il_op_new_branch(cond, NULL, goto_); } -RzPVector *bf_rlimit(RzILVM *vm, BfContext *ctx, ut64 id, ut64 addr) { +RzILOpEffect *bf_rlimit(RzILVM *vm, BfContext *ctx, ut64 id, ut64 addr) { // (perform (branch (load mem (var ptr)) // (goto [) // (do nothing)) @@ -202,7 +195,7 @@ RzPVector *bf_rlimit(RzILVM *vm, BfContext *ctx, ut64 id, ut64 addr) { RzILOpEffect *branch = rz_il_op_new_branch(cond, goto_, NULL); free(to_free); - return rz_il_make_oplist(1, branch); + return branch; } static bool bf_specific_init(RzAnalysisRzil *rzil) { @@ -320,7 +313,7 @@ static int bf_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *b BfContext *ctx = analysis->rzil->user; RzILVM *vm = analysis->rzil->vm; - RzPVector *oplist = NULL; + RzILOpEffect *ilop = NULL; op->rzil_op = RZ_NEW0(RzAnalysisRzilOp); if (!op->rzil_op) { RZ_LOG_ERROR("Fail to init rzil op\n"); @@ -330,7 +323,7 @@ static int bf_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *b switch (buf[0]) { case '[': - oplist = bf_llimit(vm, ctx, op->id, addr); + ilop = bf_llimit(vm, ctx, op->id, addr); op->type = RZ_ANALYSIS_OP_TYPE_CJMP; op->fail = addr + 1; buf = rz_mem_dup((void *)buf, len); @@ -377,31 +370,31 @@ static int bf_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *b free((ut8 *)buf); break; case ']': - oplist = bf_rlimit(vm, ctx, op->id, addr); + ilop = bf_rlimit(vm, ctx, op->id, addr); op->type = RZ_ANALYSIS_OP_TYPE_UJMP; break; case '>': op->type = RZ_ANALYSIS_OP_TYPE_ADD; - oplist = bf_right_arrow(vm, op->id); + ilop = bf_right_arrow(vm, op->id); break; case '<': op->type = RZ_ANALYSIS_OP_TYPE_SUB; - oplist = bf_left_arrow(vm, op->id); + ilop = bf_left_arrow(vm, op->id); break; case '+': op->type = RZ_ANALYSIS_OP_TYPE_ADD; - oplist = bf_inc(vm, op->id); + ilop = bf_inc(vm, op->id); break; case '-': op->type = RZ_ANALYSIS_OP_TYPE_SUB; - oplist = bf_dec(vm, op->id); + ilop = bf_dec(vm, op->id); break; case '.': - oplist = bf_out(vm, op->id); + ilop = bf_out(vm, op->id); op->type = RZ_ANALYSIS_OP_TYPE_STORE; break; case ',': - oplist = bf_in(vm, op->id); + ilop = bf_in(vm, op->id); op->type = RZ_ANALYSIS_OP_TYPE_LOAD; break; case 0x00: @@ -410,10 +403,11 @@ static int bf_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *b break; default: op->type = RZ_ANALYSIS_OP_TYPE_NOP; + ilop = rz_il_op_new_nop(); break; } - if (oplist) { - op->rzil_op->ops = oplist; + if (ilop) { + op->rzil_op->op = ilop; } ctx->op_count++; return op->size; diff --git a/librz/core/cil.c b/librz/core/cil.c index c324a86b29..ab96ab7412 100644 --- a/librz/core/cil.c +++ b/librz/core/cil.c @@ -636,8 +636,6 @@ RZ_IPI void rz_core_analysis_rzil_vm_status(RzCore *core, const char *var_name, // step a list of ct_opcode at a given address RZ_IPI void rz_core_rzil_step(RzCore *core) { - RzPVector *oplist; - if (!core->analysis || !core->analysis->rzil) { RZ_LOG_ERROR("RzIL: Run 'aezi' first to initialize the VM\n"); return; @@ -662,10 +660,10 @@ RZ_IPI void rz_core_rzil_step(RzCore *core) { // analysis current data to trigger rzil_set_op_code (void)rz_io_read_at_mapped(core->io, addr, code, sizeof(code)); int size = rz_analysis_op(analysis, &op, addr, code, sizeof(code), RZ_ANALYSIS_OP_MASK_ESIL | RZ_ANALYSIS_OP_MASK_HINT); - oplist = op.rzil_op ? op.rzil_op->ops : NULL; + RzILOpEffect *ilop = op.rzil_op ? op.rzil_op->op : NULL; - if (oplist) { - rz_il_vm_list_step(vm, oplist, size > 0 ? size : 1); + if (ilop) { + rz_il_vm_list_step(vm, ilop, size > 0 ? size : 1); } else { RZ_LOG_ERROR("RzIL: invalid instruction detected or reach the end of code at address 0x%08" PFMT64x "\n", addr); } diff --git a/librz/core/cmd/cmd_analysis.c b/librz/core/cmd/cmd_analysis.c index 74651180e8..7c5e7e1348 100644 --- a/librz/core/cmd/cmd_analysis.c +++ b/librz/core/cmd/cmd_analysis.c @@ -1052,9 +1052,9 @@ static void core_analysis_bytes(RzCore *core, const ut8 *buf, int len, int nops, pj_ks(pj, "esil", jesil); } if (op.rzil_op) { - if (op.rzil_op->ops) { + if (op.rzil_op->op) { pj_k(pj, "rzil"); - rz_il_oplist_json(op.rzil_op->ops, pj); + rz_il_op_effect_json(op.rzil_op->op, pj); } else { pj_knull(pj, "rzil"); } @@ -1235,15 +1235,11 @@ static void core_analysis_bytes(RzCore *core, const ut8 *buf, int len, int nops, } else if (RZ_STR_ISNOTEMPTY(esilstr)) { printline("esil", "%s\n", esilstr); } - if (op.rzil_op) { - if (op.rzil_op->ops) { - RzStrBuf *sbil = rz_strbuf_new(""); - rz_il_oplist_stringify(op.rzil_op->ops, sbil); - printline("rzil", "%s\n", rz_strbuf_get(sbil)); - rz_strbuf_free(sbil); - } else { - printline_noarg("rzil", "[]"); - } + if (op.rzil_op && op.rzil_op->op) { + RzStrBuf *sbil = rz_strbuf_new(""); + rz_il_op_effect_stringify(op.rzil_op->op, sbil); + printline("rzil", "%s\n", rz_strbuf_get(sbil)); + rz_strbuf_free(sbil); } if (hint && hint->jump != UT64_MAX) { op.jump = hint->jump; diff --git a/librz/il/rzil_export.c b/librz/il/rzil_export.c index ce16f0aa75..e04952fc4b 100644 --- a/librz/il/rzil_export.c +++ b/librz/il/rzil_export.c @@ -665,45 +665,6 @@ RZ_API void rz_il_op_effect_json(RZ_NONNULL RzILOpEffect *op, RZ_NONNULL PJ *pj) il_op_effect_resolve(op, NULL, pj); } -/** - * Generates the string representation of the IL statements - * \param op_list RzPVector*, array of IL statements - * \param sb RzStrBuf*, a pointer to the string buffer - */ -RZ_API void rz_il_oplist_stringify(RZ_NONNULL RzPVector *op_list, RZ_NONNULL RzStrBuf *sb) { - rz_return_if_fail(op_list && sb); - - ut32 i = 0; - void **iter; - rz_strbuf_append(sb, "["); - rz_pvector_foreach (op_list, iter) { - RzILOpEffect *ilop = *iter; - if (i > 0) { - rz_strbuf_append(sb, ", "); - } - rz_il_op_effect_stringify(ilop, sb); - i++; - } - rz_strbuf_append(sb, "]"); -} - -/** - * Generates the JSON representation of the IL statements - * \param code RzPVector*, array of IL statements - * \param pj PJ*, a pointer to the JSON buffer - */ -RZ_API void rz_il_oplist_json(RZ_NONNULL RzPVector *op_list, RZ_NONNULL PJ *pj) { - rz_return_if_fail(op_list && pj); - - pj_a(pj); - void **iter; - rz_pvector_foreach (op_list, iter) { - RzILOpEffect *ilop = *iter; - rz_il_op_effect_json(ilop, pj); - } - pj_end(pj); -} - RZ_API void rz_il_event_stringify(RZ_NONNULL RzILEvent *evt, RZ_NONNULL RzStrBuf *sb) { rz_return_if_fail(evt && sb); char *tmp0 = NULL, *tmp1 = NULL, *tmp2 = NULL; diff --git a/librz/il/rzil_opcodes.c b/librz/il/rzil_opcodes.c index 226682aebc..b6cbc098ee 100644 --- a/librz/il/rzil_opcodes.c +++ b/librz/il/rzil_opcodes.c @@ -560,6 +560,64 @@ RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_seq(RZ_NONNULL RzILOpEffect *x, RZ_NONN return ret; } +/** + * Chain \p n opcodes given as varargs in sequence using seq if necessary + * + * It works exactly like this seq helper from BAP: + * let rec seq = function + * | [] -> CT.perform Theory.Effect.Sort.bot + * | [x] -> x + * | x :: xs -> CT.seq x @@ seq xs + * + * \param n number of total opcodes given + * \param ... \p num RzILOpEffect * ops to be executed in sequence + */ +RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_seqn(ut32 n, ...) { + if (!n) { + return rz_il_op_new_nop(); + } + RzILOpEffect *root = NULL; + RzILOpEffect *prev_seq = NULL; + va_list args; + va_start(args, n); + for (ut32 i = 0; i < n; ++i) { + RzILOpEffect *cur_op = va_arg(args, RzILOpEffect *); + if (i == n - 1) { + // last one + if (prev_seq) { + prev_seq->op.seq->y = cur_op; + } else { + // n == 1, no need for seq at all + root = cur_op; + } + break; + } + RzILOpEffect *seq = RZ_NEW0(RzILOpEffect); + if (!seq) { + break; + } + seq->op.seq = RZ_NEW0(RzILOpArgsSeq); + if (!seq->op.seq) { + free(seq); + break; + } + seq->code = RZIL_OP_SEQ; + seq->op.seq->x = cur_op; + if (prev_seq) { + // not the first one + // We let the seq recurse in the second op because that + // can enable tail call elimination in the evaluation. + prev_seq->op.seq->y = seq; + } else { + // first one + root = seq; + } + prev_seq = seq; + } + va_end(args); + return root; +} + /** * \brief op structure for `blk` (label -> data eff -> ctrl eff -> unit eff) * diff --git a/librz/il/rzil_vm.c b/librz/il/rzil_vm.c index ed1b8fce1f..c1557e1396 100644 --- a/librz/il/rzil_vm.c +++ b/librz/il/rzil_vm.c @@ -386,27 +386,6 @@ RZ_API RZ_BORROW RzILEffectLabel *rz_il_vm_update_label(RZ_NONNULL RzILVM *vm, R return lbl; } -/** - * Make a core theory opcode vector - * \param num int, number of total opcodes - * \param ... op RzILOp, op will be pushed to vector one by one - * \return oplist RzPVector*, pointer to the opcode - */ -RZ_API RZ_OWN RzPVector *rz_il_make_oplist(ut32 num, ...) { - va_list args; - RzILOpEffect *cur_op; - RzPVector *oplist = rz_pvector_new((RzPVectorFree)rz_il_op_effect_free); - - va_start(args, num); - for (ut32 i = 0; i < num; ++i) { - cur_op = va_arg(args, RzILOpEffect *); - rz_pvector_push(oplist, cur_op); - } - va_end(args); - - return oplist; -} - static void *eval_pure(RZ_NONNULL RzILVM *vm, RZ_NONNULL RzILOpPure *op, RZ_NONNULL RzILPureType *type) { rz_return_val_if_fail(vm && op && type, NULL); RzILOpPureHandler handler = vm->op_handler_pure_table[op->code]; diff --git a/librz/il/vm_layer.c b/librz/il/vm_layer.c index f984262001..ac217e2016 100644 --- a/librz/il/vm_layer.c +++ b/librz/il/vm_layer.c @@ -400,20 +400,12 @@ RZ_API void rz_il_vm_event_add(RzILVM *vm, RzILEvent *evt) { * \param op_list, a list of op roots. * \param op_size, how much the pc value has to increate of. */ -RZ_API bool rz_il_vm_list_step(RzILVM *vm, RzPVector *op_list, ut32 op_size) { - rz_return_val_if_fail(vm && op_list, false); +RZ_API bool rz_il_vm_list_step(RzILVM *vm, RzILOpEffect *op, ut32 op_size) { + rz_return_val_if_fail(vm && op, false); rz_list_purge(vm->events); - bool succ = true; - void **iter; - rz_pvector_foreach (op_list, iter) { - RzILOpEffect *root = *iter; - if (!rz_il_evaluate_effect(vm, root)) { - succ = false; - break; - } - } + bool succ = rz_il_evaluate_effect(vm, op); RzBitVector *step = rz_bv_new_from_ut64(vm->pc->len, op_size); RzBitVector *next_pc = rz_bv_add(vm->pc, step, NULL); diff --git a/librz/include/rz_analysis.h b/librz/include/rz_analysis.h index 6a408a765f..9723eae559 100644 --- a/librz/include/rz_analysis.h +++ b/librz/include/rz_analysis.h @@ -807,9 +807,8 @@ typedef enum rz_analysis_data_type_t { RZ_ANALYSIS_DATATYPE_FLOAT, } RzAnalysisDataType; -// For switchting to AST-like approach in the future typedef struct rz_analysis_rzil_op_t { - RzPVector *ops; + RzILOpEffect *op; } RzAnalysisRzilOp; typedef struct rz_analysis_op_t { diff --git a/librz/include/rz_il/rzil_opcodes.h b/librz/include/rz_il/rzil_opcodes.h index 2972174310..8e8f6e7d5f 100644 --- a/librz/include/rz_il/rzil_opcodes.h +++ b/librz/include/rz_il/rzil_opcodes.h @@ -524,6 +524,7 @@ RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_let(RZ_NONNULL const char *var, RZ_NONN RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_jmp(RZ_NONNULL RzILOpBitVector *dst); RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_goto(RZ_NONNULL const char *label); RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_seq(RZ_NONNULL RzILOpEffect *x, RZ_NONNULL RzILOpEffect *y); +RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_seqn(ut32 n, ...); RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_blk(RZ_NONNULL RzILOpEffect *data_effect, RZ_NONNULL RzILOpEffect *ctrl_effect); RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_repeat(RZ_NONNULL RzILOpBool *condition, RZ_NONNULL RzILOpEffect *data_effect); RZ_API RZ_OWN RzILOpEffect *rz_il_op_new_branch(RZ_NONNULL RzILOpBool *condition, RZ_NULLABLE RzILOpEffect *true_effect, RZ_NULLABLE RzILOpEffect *false_effect); diff --git a/librz/include/rz_il/rzil_vm.h b/librz/include/rz_il/rzil_vm.h index 7cc006faa3..73cc2a7b37 100644 --- a/librz/include/rz_il/rzil_vm.h +++ b/librz/include/rz_il/rzil_vm.h @@ -94,17 +94,11 @@ RZ_API RZ_BORROW RzILVal *rz_il_vm_fortify_bool(RZ_NONNULL RzILVM *vm, bool val) RZ_API void rz_il_vm_add_reg(RZ_NONNULL RzILVM *vm, RZ_NONNULL const char *name, ut32 length); RZ_API void rz_il_vm_add_bit_reg(RZ_NONNULL RzILVM *vm, RZ_NONNULL const char *name, bool value); -// VM store and load core theory opcodes -RZ_API RZ_OWN RzPVector *rz_il_make_oplist(ut32 num, ...); -#define rz_il_make_nop_list() rz_il_make_oplist(0, NULL) - RZ_API void rz_il_op_pure_stringify(RZ_NONNULL RzILOpPure *op, RZ_NONNULL RzStrBuf *sb); RZ_API void rz_il_op_effect_stringify(RZ_NONNULL RzILOpEffect *op, RZ_NONNULL RzStrBuf *sb); -RZ_API void rz_il_oplist_stringify(RZ_NONNULL RzPVector *oplist, RZ_NONNULL RzStrBuf *sb); RZ_API void rz_il_op_pure_json(RZ_NONNULL RzILOpPure *op, RZ_NONNULL PJ *pj); RZ_API void rz_il_op_effect_json(RZ_NONNULL RzILOpEffect *op, RZ_NONNULL PJ *pj); -RZ_API void rz_il_oplist_json(RZ_NONNULL RzPVector *oplist, RZ_NONNULL PJ *pj); RZ_API void rz_il_event_stringify(RZ_NONNULL RzILEvent *evt, RZ_NONNULL RzStrBuf *sb); RZ_API void rz_il_event_json(RZ_NONNULL RzILEvent *evt, RZ_NONNULL PJ *pj); diff --git a/librz/include/rz_il/vm_layer.h b/librz/include/rz_il/vm_layer.h index f7efc81cea..b95f54df16 100644 --- a/librz/include/rz_il/vm_layer.h +++ b/librz/include/rz_il/vm_layer.h @@ -17,7 +17,7 @@ RZ_API bool rz_il_vm_init(RzILVM *vm, ut64 start_addr, ut32 addr_size, bool big_ RZ_API void rz_il_vm_fini(RzILVM *vm); RZ_API void rz_il_vm_add_mem(RzILVM *vm, RzILMemIndex index, RZ_OWN RzILMem *mem); RZ_API RzILMem *rz_il_vm_get_mem(RzILVM *vm, RzILMemIndex index); -RZ_API bool rz_il_vm_list_step(RzILVM *vm, RzPVector *op_list, ut32 op_size); +RZ_API bool rz_il_vm_list_step(RzILVM *vm, RzILOpEffect *op, ut32 op_size); // VM Event operations RZ_API void rz_il_vm_event_add(RzILVM *vm, RzILEvent *evt); diff --git a/test/db/rzil/bf b/test/db/rzil/bf index 00e274fb5a..5c90ab20a2 100644 --- a/test/db/rzil/bf +++ b/test/db/rzil/bf @@ -78,29 +78,29 @@ aoj @ 0x6a ~{.rzil} EOF EXPECT=< just a nop + RzILOpEffect *s = rz_il_op_new_seqn(0); + mu_assert_notnull(s, "seqn 0"); + mu_assert_eq(s->code, RZIL_OP_NOP, "seqn 0 nop"); + rz_il_op_effect_free(s); + + // n = 1 ==> just the op + RzILOpEffect *e0 = rz_il_op_new_goto("beach"); + s = rz_il_op_new_seqn(1, e0); + mu_assert_notnull(s, "seqn 1"); + mu_assert_ptreq(s, e0, "seqn 1 op"); + rz_il_op_effect_free(s); + + // n = 2 ==> single seq + // (seq e0 e1) + e0 = rz_il_op_new_goto("beach"); + RzILOpEffect *e1 = rz_il_op_new_goto("beach2"); + s = rz_il_op_new_seqn(2, e0, e1); + mu_assert_notnull(s, "seqn 2"); + mu_assert_eq(s->code, RZIL_OP_SEQ, "seqn 2 seq"); + mu_assert_ptreq(s->op.seq->x, e0, "seqn 2 first"); + mu_assert_ptreq(s->op.seq->y, e1, "seqn 2 second"); + rz_il_op_effect_free(s); + + // n = 3 ==> nested seq with recursion in the second op: + // (seq e0 (seq e1 e2)) + e0 = rz_il_op_new_goto("beach"); + e1 = rz_il_op_new_goto("beach2"); + RzILOpEffect *e2 = rz_il_op_new_goto("beach3"); + s = rz_il_op_new_seqn(3, e0, e1, e2); + mu_assert_notnull(s, "seqn 3"); + mu_assert_eq(s->code, RZIL_OP_SEQ, "seqn 3 seq"); + mu_assert_ptreq(s->op.seq->x, e0, "seqn 3 first"); + mu_assert_eq(s->op.seq->y->code, RZIL_OP_SEQ, "seqn 3 second seq"); + mu_assert_ptreq(s->op.seq->y->op.seq->x, e1, "seqn 3 second"); + mu_assert_ptreq(s->op.seq->y->op.seq->y, e2, "seqn 3 third"); + rz_il_op_effect_free(s); + + // n = 4 ==> nested seq with recursion in the second op and no confusion: + // (seq e0 (seq e1 (seq e2 e3))) + e0 = rz_il_op_new_goto("beach"); + e1 = rz_il_op_new_goto("beach2"); + e2 = rz_il_op_new_goto("beach3"); + RzILOpEffect *e3 = rz_il_op_new_goto("beach3"); + s = rz_il_op_new_seqn(4, e0, e1, e2, e3); + mu_assert_notnull(s, "seqn 4"); + mu_assert_eq(s->code, RZIL_OP_SEQ, "seqn 4 seq"); + mu_assert_ptreq(s->op.seq->x, e0, "seqn 4 first"); + mu_assert_eq(s->op.seq->y->code, RZIL_OP_SEQ, "seqn 4 second seq"); + mu_assert_ptreq(s->op.seq->y->op.seq->x, e1, "seqn 4 second"); + mu_assert_eq(s->op.seq->y->op.seq->y->code, RZIL_OP_SEQ, "seqn 4 third seq"); + mu_assert_ptreq(s->op.seq->y->op.seq->y->op.seq->x, e2, "seqn 4 third"); + mu_assert_ptreq(s->op.seq->y->op.seq->y->op.seq->y, e3, "seqn 4 fourth"); + rz_il_op_effect_free(s); + + mu_end; +} + bool all_tests() { mu_run_test(test_rzil_bool_init); mu_run_test(test_rzil_bool_logic); @@ -241,6 +300,7 @@ bool all_tests() { mu_run_test(test_rzil_mem_store); mu_run_test(test_rzil_mem_loadw); mu_run_test(test_rzil_mem_storew); + mu_run_test(test_rzil_seqn); return tests_passed != tests_run; }