summaryrefslogtreecommitdiffstats
path: root/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/codegen.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/codegen.c3026
1 files changed, 3026 insertions, 0 deletions
diff --git a/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/codegen.c b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/codegen.c
new file mode 100644
index 00000000..8f15a9b1
--- /dev/null
+++ b/web/server/h2o/libh2o/deps/mruby/mrbgems/mruby-compiler/core/codegen.c
@@ -0,0 +1,3026 @@
+/*
+** codegen.c - mruby code generator
+**
+** See Copyright Notice in mruby.h
+*/
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mruby.h>
+#include <mruby/compile.h>
+#include <mruby/proc.h>
+#include <mruby/numeric.h>
+#include <mruby/string.h>
+#include <mruby/debug.h>
+#include "node.h"
+#include <mruby/opcode.h>
+#include <mruby/re.h>
+#include <mruby/throw.h>
+
+#ifndef MRB_CODEGEN_LEVEL_MAX
+#define MRB_CODEGEN_LEVEL_MAX 1024
+#endif
+
+typedef mrb_ast_node node;
+typedef struct mrb_parser_state parser_state;
+
+enum looptype {
+ LOOP_NORMAL,
+ LOOP_BLOCK,
+ LOOP_FOR,
+ LOOP_BEGIN,
+ LOOP_RESCUE,
+};
+
+struct loopinfo {
+ enum looptype type;
+ int pc1, pc2, pc3, acc;
+ int ensure_level;
+ struct loopinfo *prev;
+};
+
+typedef struct scope {
+ mrb_state *mrb;
+ mrb_pool *mpool;
+ struct mrb_jmpbuf jmp;
+
+ struct scope *prev;
+
+ node *lv;
+
+ int sp;
+ int pc;
+ int lastlabel;
+ int ainfo:15;
+ mrb_bool mscope:1;
+
+ struct loopinfo *loop;
+ int ensure_level;
+ char const *filename;
+ uint16_t lineno;
+
+ mrb_code *iseq;
+ uint16_t *lines;
+ int icapa;
+
+ mrb_irep *irep;
+ int pcapa, scapa, rcapa;
+
+ uint16_t nlocals;
+ uint16_t nregs;
+ int ai;
+
+ int debug_start_pos;
+ uint16_t filename_index;
+ parser_state* parser;
+
+ int rlev; /* recursion levels */
+} codegen_scope;
+
+static codegen_scope* scope_new(mrb_state *mrb, codegen_scope *prev, node *lv);
+static void scope_finish(codegen_scope *s);
+static struct loopinfo *loop_push(codegen_scope *s, enum looptype t);
+static void loop_break(codegen_scope *s, node *tree);
+static void loop_pop(codegen_scope *s, int val);
+
+static void gen_assignment(codegen_scope *s, node *tree, int sp, int val);
+static void gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val);
+
+static void codegen(codegen_scope *s, node *tree, int val);
+static void raise_error(codegen_scope *s, const char *msg);
+
+static void
+codegen_error(codegen_scope *s, const char *message)
+{
+ if (!s) return;
+ while (s->prev) {
+ codegen_scope *tmp = s->prev;
+ mrb_free(s->mrb, s->iseq);
+ mrb_pool_close(s->mpool);
+ s = tmp;
+ }
+#ifndef MRB_DISABLE_STDIO
+ if (s->filename && s->lineno) {
+ fprintf(stderr, "codegen error:%s:%d: %s\n", s->filename, s->lineno, message);
+ }
+ else {
+ fprintf(stderr, "codegen error: %s\n", message);
+ }
+#endif
+ MRB_THROW(&s->jmp);
+}
+
+static void*
+codegen_palloc(codegen_scope *s, size_t len)
+{
+ void *p = mrb_pool_alloc(s->mpool, len);
+
+ if (!p) codegen_error(s, "pool memory allocation");
+ return p;
+}
+
+static void*
+codegen_malloc(codegen_scope *s, size_t len)
+{
+ void *p = mrb_malloc_simple(s->mrb, len);
+
+ if (!p) codegen_error(s, "mrb_malloc");
+ return p;
+}
+
+static void*
+codegen_realloc(codegen_scope *s, void *p, size_t len)
+{
+ p = mrb_realloc_simple(s->mrb, p, len);
+
+ if (!p && len > 0) codegen_error(s, "mrb_realloc");
+ return p;
+}
+
+static int
+new_label(codegen_scope *s)
+{
+ s->lastlabel = s->pc;
+ return s->pc;
+}
+
+static inline int
+genop(codegen_scope *s, mrb_code i)
+{
+ if (s->pc == s->icapa) {
+ s->icapa *= 2;
+ s->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->icapa);
+ if (s->lines) {
+ s->lines = (uint16_t*)codegen_realloc(s, s->lines, sizeof(short)*s->icapa);
+ s->irep->lines = s->lines;
+ }
+ }
+ s->iseq[s->pc] = i;
+ if (s->lines) {
+ s->lines[s->pc] = s->lineno;
+ }
+ return s->pc++;
+}
+
+#define NOVAL 0
+#define VAL 1
+
+static mrb_bool
+no_optimize(codegen_scope *s)
+{
+ if (s && s->parser && s->parser->no_optimize)
+ return TRUE;
+ return FALSE;
+}
+
+static int
+genop_peep(codegen_scope *s, mrb_code i, int val)
+{
+ /* peephole optimization */
+ if (!no_optimize(s) && s->lastlabel != s->pc && s->pc > 0) {
+ mrb_code i0 = s->iseq[s->pc-1];
+ int c1 = GET_OPCODE(i);
+ int c0 = GET_OPCODE(i0);
+
+ switch (c1) {
+ case OP_MOVE:
+ if (GETARG_A(i) == GETARG_B(i)) {
+ /* skip useless OP_MOVE */
+ return 0;
+ }
+ if (val) break;
+ switch (c0) {
+ case OP_MOVE:
+ if (GETARG_A(i) == GETARG_A(i0)) {
+ /* skip overriden OP_MOVE */
+ s->pc--;
+ s->iseq[s->pc] = i;
+ }
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i) == GETARG_B(i0)) {
+ /* skip swapping OP_MOVE */
+ return 0;
+ }
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
+ s->pc--;
+ return genop_peep(s, MKOP_AB(OP_MOVE, GETARG_A(i), GETARG_B(i0)), val);
+ }
+ break;
+ case OP_LOADI:
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
+ s->iseq[s->pc-1] = MKOP_AsBx(OP_LOADI, GETARG_A(i), GETARG_sBx(i0));
+ return 0;
+ }
+ break;
+ case OP_ARRAY:
+ case OP_HASH:
+ case OP_RANGE:
+ case OP_AREF:
+ case OP_GETUPVAR:
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
+ s->iseq[s->pc-1] = MKOP_ABC(c0, GETARG_A(i), GETARG_B(i0), GETARG_C(i0));
+ return 0;
+ }
+ break;
+ case OP_LOADSYM:
+ case OP_GETGLOBAL:
+ case OP_GETIV:
+ case OP_GETCV:
+ case OP_GETCONST:
+ case OP_GETSPECIAL:
+ case OP_LOADL:
+ case OP_STRING:
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
+ s->iseq[s->pc-1] = MKOP_ABx(c0, GETARG_A(i), GETARG_Bx(i0));
+ return 0;
+ }
+ break;
+ case OP_SCLASS:
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
+ s->iseq[s->pc-1] = MKOP_AB(c0, GETARG_A(i), GETARG_B(i0));
+ return 0;
+ }
+ break;
+ case OP_LOADNIL:
+ case OP_LOADSELF:
+ case OP_LOADT:
+ case OP_LOADF:
+ case OP_OCLASS:
+ if (GETARG_B(i) == GETARG_A(i0) && GETARG_A(i0) >= s->nlocals) {
+ s->iseq[s->pc-1] = MKOP_A(c0, GETARG_A(i));
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ case OP_SETIV:
+ case OP_SETCV:
+ case OP_SETCONST:
+ case OP_SETMCNST:
+ case OP_SETGLOBAL:
+ if (val) break;
+ if (c0 == OP_MOVE) {
+ if (GETARG_A(i) == GETARG_A(i0)) {
+ s->iseq[s->pc-1] = MKOP_ABx(c1, GETARG_B(i0), GETARG_Bx(i));
+ return 0;
+ }
+ }
+ break;
+ case OP_SETUPVAR:
+ if (val) break;
+ if (c0 == OP_MOVE) {
+ if (GETARG_A(i) == GETARG_A(i0)) {
+ s->iseq[s->pc-1] = MKOP_ABC(c1, GETARG_B(i0), GETARG_B(i), GETARG_C(i));
+ return 0;
+ }
+ }
+ break;
+ case OP_EPOP:
+ if (c0 == OP_EPOP) {
+ s->iseq[s->pc-1] = MKOP_A(OP_EPOP, GETARG_A(i0)+GETARG_A(i));
+ return 0;
+ }
+ break;
+ case OP_POPERR:
+ if (c0 == OP_POPERR) {
+ s->iseq[s->pc-1] = MKOP_A(OP_POPERR, GETARG_A(i0)+GETARG_A(i));
+ return 0;
+ }
+ break;
+ case OP_RETURN:
+ switch (c0) {
+ case OP_RETURN:
+ return 0;
+ case OP_MOVE:
+ if (GETARG_A(i0) >= s->nlocals) {
+ s->iseq[s->pc-1] = MKOP_AB(OP_RETURN, GETARG_B(i0), OP_R_NORMAL);
+ return 0;
+ }
+ break;
+ case OP_SETIV:
+ case OP_SETCV:
+ case OP_SETCONST:
+ case OP_SETMCNST:
+ case OP_SETUPVAR:
+ case OP_SETGLOBAL:
+ s->pc--;
+ genop_peep(s, i0, NOVAL);
+ i0 = s->iseq[s->pc-1];
+ return genop(s, MKOP_AB(OP_RETURN, GETARG_A(i0), OP_R_NORMAL));
+#if 0
+ case OP_SEND:
+ if (GETARG_B(i) == OP_R_NORMAL && GETARG_A(i) == GETARG_A(i0)) {
+ s->iseq[s->pc-1] = MKOP_ABC(OP_TAILCALL, GETARG_A(i0), GETARG_B(i0), GETARG_C(i0));
+ return;
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+ break;
+ case OP_ADD:
+ case OP_SUB:
+ if (c0 == OP_LOADI) {
+ int c = GETARG_sBx(i0);
+
+ if (c1 == OP_SUB) c = -c;
+ if (c > 127 || c < -127) break;
+ if (0 <= c)
+ s->iseq[s->pc-1] = MKOP_ABC(OP_ADDI, GETARG_A(i), GETARG_B(i), c);
+ else
+ s->iseq[s->pc-1] = MKOP_ABC(OP_SUBI, GETARG_A(i), GETARG_B(i), -c);
+ return 0;
+ }
+ case OP_STRCAT:
+ if (c0 == OP_STRING) {
+ mrb_value v = s->irep->pool[GETARG_Bx(i0)];
+
+ if (mrb_string_p(v) && RSTRING_LEN(v) == 0) {
+ s->pc--;
+ return 0;
+ }
+ }
+ if (c0 == OP_LOADNIL) {
+ if (GETARG_B(i) == GETARG_A(i0)) {
+ s->pc--;
+ return 0;
+ }
+ }
+ break;
+ case OP_JMPIF:
+ case OP_JMPNOT:
+ if (c0 == OP_MOVE && GETARG_A(i) == GETARG_A(i0)) {
+ s->iseq[s->pc-1] = MKOP_AsBx(c1, GETARG_B(i0), GETARG_sBx(i));
+ return s->pc-1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return genop(s, i);
+}
+
+static void
+scope_error(codegen_scope *s)
+{
+ exit(EXIT_FAILURE);
+}
+
+static inline void
+dispatch(codegen_scope *s, int pc)
+{
+ int diff = s->pc - pc;
+ mrb_code i = s->iseq[pc];
+ int c = GET_OPCODE(i);
+
+ s->lastlabel = s->pc;
+ switch (c) {
+ case OP_JMP:
+ case OP_JMPIF:
+ case OP_JMPNOT:
+ case OP_ONERR:
+ break;
+ default:
+#ifndef MRB_DISABLE_STDIO
+ fprintf(stderr, "bug: dispatch on non JMP op\n");
+#endif
+ scope_error(s);
+ break;
+ }
+ if (diff > MAXARG_sBx) {
+ codegen_error(s, "too distant jump address");
+ }
+ s->iseq[pc] = MKOP_AsBx(c, GETARG_A(i), diff);
+}
+
+static void
+dispatch_linked(codegen_scope *s, int pc)
+{
+ mrb_code i;
+ int pos;
+
+ if (!pc) return;
+ for (;;) {
+ i = s->iseq[pc];
+ pos = GETARG_sBx(i);
+ dispatch(s, pc);
+ if (!pos) break;
+ pc = pos;
+ }
+}
+
+#define nregs_update do {if (s->sp > s->nregs) s->nregs = s->sp;} while (0)
+static void
+push_(codegen_scope *s)
+{
+ if (s->sp > 511) {
+ codegen_error(s, "too complex expression");
+ }
+ s->sp++;
+ nregs_update;
+}
+
+static void
+push_n_(codegen_scope *s, int n)
+{
+ if (s->sp+n > 511) {
+ codegen_error(s, "too complex expression");
+ }
+ s->sp+=n;
+ nregs_update;
+}
+
+#define push() push_(s)
+#define push_n(n) push_n_(s,n)
+#define pop_(s) ((s)->sp--)
+#define pop() pop_(s)
+#define pop_n(n) (s->sp-=(n))
+#define cursp() (s->sp)
+
+static inline int
+new_lit(codegen_scope *s, mrb_value val)
+{
+ int i;
+ mrb_value *pv;
+
+ switch (mrb_type(val)) {
+ case MRB_TT_STRING:
+ for (i=0; i<s->irep->plen; i++) {
+ mrb_int len;
+ pv = &s->irep->pool[i];
+
+ if (mrb_type(*pv) != MRB_TT_STRING) continue;
+ if ((len = RSTRING_LEN(*pv)) != RSTRING_LEN(val)) continue;
+ if (memcmp(RSTRING_PTR(*pv), RSTRING_PTR(val), len) == 0)
+ return i;
+ }
+ break;
+ case MRB_TT_FLOAT:
+ for (i=0; i<s->irep->plen; i++) {
+ pv = &s->irep->pool[i];
+ if (mrb_type(*pv) != MRB_TT_FLOAT) continue;
+ if (mrb_float(*pv) == mrb_float(val)) return i;
+ }
+ break;
+ case MRB_TT_FIXNUM:
+ for (i=0; i<s->irep->plen; i++) {
+ pv = &s->irep->pool[i];
+ if (!mrb_fixnum_p(*pv)) continue;
+ if (mrb_fixnum(*pv) == mrb_fixnum(val)) return i;
+ }
+ break;
+ default:
+ /* should not happen */
+ return 0;
+ }
+
+ if (s->irep->plen == s->pcapa) {
+ s->pcapa *= 2;
+ s->irep->pool = (mrb_value *)codegen_realloc(s, s->irep->pool, sizeof(mrb_value)*s->pcapa);
+ }
+
+ pv = &s->irep->pool[s->irep->plen];
+ i = s->irep->plen++;
+
+ switch (mrb_type(val)) {
+ case MRB_TT_STRING:
+ *pv = mrb_str_pool(s->mrb, val);
+ break;
+
+ case MRB_TT_FLOAT:
+#ifdef MRB_WORD_BOXING
+ *pv = mrb_float_pool(s->mrb, mrb_float(val));
+ break;
+#endif
+ case MRB_TT_FIXNUM:
+ *pv = val;
+ break;
+
+ default:
+ /* should not happen */
+ break;
+ }
+ return i;
+}
+
+/* method symbols should be fit in 9 bits */
+#define MAXMSYMLEN 512
+/* maximum symbol numbers */
+#define MAXSYMLEN 65536
+
+static int
+new_msym(codegen_scope *s, mrb_sym sym)
+{
+ int i, len;
+
+ mrb_assert(s->irep);
+
+ len = s->irep->slen;
+ if (len > MAXMSYMLEN) len = MAXMSYMLEN;
+ for (i=0; i<len; i++) {
+ if (s->irep->syms[i] == sym) return i;
+ if (s->irep->syms[i] == 0) break;
+ }
+ if (i == MAXMSYMLEN) {
+ codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXMSYMLEN) ")");
+ }
+ s->irep->syms[i] = sym;
+ if (i == s->irep->slen) s->irep->slen++;
+ return i;
+}
+
+static int
+new_sym(codegen_scope *s, mrb_sym sym)
+{
+ int i;
+
+ for (i=0; i<s->irep->slen; i++) {
+ if (s->irep->syms[i] == sym) return i;
+ }
+ if (s->irep->slen == MAXSYMLEN) {
+ codegen_error(s, "too many symbols (max " MRB_STRINGIZE(MAXSYMLEN) ")");
+ }
+
+ if (s->irep->slen > MAXMSYMLEN/2 && s->scapa == MAXMSYMLEN) {
+ s->scapa = MAXSYMLEN;
+ s->irep->syms = (mrb_sym *)codegen_realloc(s, s->irep->syms, sizeof(mrb_sym)*MAXSYMLEN);
+ for (i = s->irep->slen; i < MAXMSYMLEN; i++) {
+ static const mrb_sym mrb_sym_zero = { 0 };
+ s->irep->syms[i] = mrb_sym_zero;
+ }
+ s->irep->slen = MAXMSYMLEN;
+ }
+ s->irep->syms[s->irep->slen] = sym;
+ return s->irep->slen++;
+}
+
+static int
+node_len(node *tree)
+{
+ int n = 0;
+
+ while (tree) {
+ n++;
+ tree = tree->cdr;
+ }
+ return n;
+}
+
+#define nsym(x) ((mrb_sym)(intptr_t)(x))
+#define lv_name(lv) nsym((lv)->car)
+static int
+lv_idx(codegen_scope *s, mrb_sym id)
+{
+ node *lv = s->lv;
+ int n = 1;
+
+ while (lv) {
+ if (lv_name(lv) == id) return n;
+ n++;
+ lv = lv->cdr;
+ }
+ return 0;
+}
+
+static void
+for_body(codegen_scope *s, node *tree)
+{
+ codegen_scope *prev = s;
+ int idx;
+ struct loopinfo *lp;
+ node *n2;
+ mrb_code c;
+
+ /* generate receiver */
+ codegen(s, tree->cdr->car, VAL);
+ /* generate loop-block */
+ s = scope_new(s->mrb, s, NULL);
+ if (s == NULL) {
+ raise_error(prev, "unexpected scope");
+ }
+
+ push(); /* push for a block parameter */
+
+ /* generate loop variable */
+ n2 = tree->car;
+ genop(s, MKOP_Ax(OP_ENTER, 0x40000));
+ if (n2->car && !n2->car->cdr && !n2->cdr) {
+ gen_assignment(s, n2->car->car, 1, NOVAL);
+ }
+ else {
+ gen_vmassignment(s, n2, 1, VAL);
+ }
+ /* construct loop */
+ lp = loop_push(s, LOOP_FOR);
+ lp->pc2 = new_label(s);
+
+ /* loop body */
+ codegen(s, tree->cdr->cdr->car, VAL);
+ pop();
+ if (s->pc > 0) {
+ c = s->iseq[s->pc-1];
+ if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel)
+ genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
+ }
+ loop_pop(s, NOVAL);
+ scope_finish(s);
+ s = prev;
+ genop(s, MKOP_Abc(OP_LAMBDA, cursp(), s->irep->rlen-1, OP_L_BLOCK));
+ push();pop(); /* space for a block */
+ pop();
+ idx = new_msym(s, mrb_intern_lit(s->mrb, "each"));
+ genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, 0));
+}
+
+static int
+lambda_body(codegen_scope *s, node *tree, int blk)
+{
+ mrb_code c;
+ codegen_scope *parent = s;
+ s = scope_new(s->mrb, s, tree->car);
+ if (s == NULL) {
+ raise_error(parent, "unexpected scope");
+ }
+
+ s->mscope = !blk;
+
+ if (blk) {
+ struct loopinfo *lp = loop_push(s, LOOP_BLOCK);
+ lp->pc1 = new_label(s);
+ }
+ tree = tree->cdr;
+ if (tree->car) {
+ mrb_aspec a;
+ int ma, oa, ra, pa, ka, kd, ba;
+ int pos, i;
+ node *n, *opt;
+
+ ma = node_len(tree->car->car);
+ n = tree->car->car;
+ while (n) {
+ n = n->cdr;
+ }
+ oa = node_len(tree->car->cdr->car);
+ ra = tree->car->cdr->cdr->car ? 1 : 0;
+ pa = node_len(tree->car->cdr->cdr->cdr->car);
+ ka = kd = 0;
+ ba = tree->car->cdr->cdr->cdr->cdr ? 1 : 0;
+
+ if (ma > 0x1f || oa > 0x1f || pa > 0x1f || ka > 0x1f) {
+ codegen_error(s, "too many formal arguments");
+ }
+ a = ((mrb_aspec)(ma & 0x1f) << 18)
+ | ((mrb_aspec)(oa & 0x1f) << 13)
+ | ((ra & 1) << 12)
+ | ((pa & 0x1f) << 7)
+ | ((ka & 0x1f) << 2)
+ | ((kd & 1)<< 1)
+ | (ba & 1);
+ s->ainfo = (((ma+oa) & 0x3f) << 6) /* (12bits = 6:1:5) */
+ | ((ra & 1) << 5)
+ | (pa & 0x1f);
+ genop(s, MKOP_Ax(OP_ENTER, a));
+ pos = new_label(s);
+ for (i=0; i<oa; i++) {
+ new_label(s);
+ genop(s, MKOP_sBx(OP_JMP, 0));
+ }
+ if (oa > 0) {
+ genop(s, MKOP_sBx(OP_JMP, 0));
+ }
+ opt = tree->car->cdr->car;
+ i = 0;
+ while (opt) {
+ int idx;
+
+ dispatch(s, pos+i);
+ codegen(s, opt->car->cdr, VAL);
+ idx = lv_idx(s, nsym(opt->car->car));
+ pop();
+ genop_peep(s, MKOP_AB(OP_MOVE, idx, cursp()), NOVAL);
+ i++;
+ opt = opt->cdr;
+ }
+ if (oa > 0) {
+ dispatch(s, pos+i);
+ }
+ }
+ codegen(s, tree->cdr->car, VAL);
+ pop();
+ if (s->pc > 0) {
+ c = s->iseq[s->pc-1];
+ if (GET_OPCODE(c) != OP_RETURN || GETARG_B(c) != OP_R_NORMAL || s->pc == s->lastlabel) {
+ if (s->nregs == 0) {
+ genop(s, MKOP_A(OP_LOADNIL, 0));
+ genop(s, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
+ }
+ else {
+ genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
+ }
+ }
+ }
+ if (blk) {
+ loop_pop(s, NOVAL);
+ }
+ scope_finish(s);
+ return parent->irep->rlen - 1;
+}
+
+static int
+scope_body(codegen_scope *s, node *tree, int val)
+{
+ codegen_scope *scope = scope_new(s->mrb, s, tree->car);
+ if (scope == NULL) {
+ raise_error(s, "unexpected scope");
+ }
+
+ codegen(scope, tree->cdr, VAL);
+ if (!s->iseq) {
+ genop(scope, MKOP_A(OP_STOP, 0));
+ }
+ else if (!val) {
+ genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
+ }
+ else {
+ if (scope->nregs == 0) {
+ genop(scope, MKOP_A(OP_LOADNIL, 0));
+ genop(scope, MKOP_AB(OP_RETURN, 0, OP_R_NORMAL));
+ }
+ else {
+ genop_peep(scope, MKOP_AB(OP_RETURN, scope->sp-1, OP_R_NORMAL), NOVAL);
+ }
+ }
+ scope_finish(scope);
+ if (!s->irep) {
+ /* should not happen */
+ return 0;
+ }
+ return s->irep->rlen - 1;
+}
+
+#define nint(x) ((int)(intptr_t)(x))
+#define nchar(x) ((char)(intptr_t)(x))
+
+static mrb_bool
+nosplat(node *t)
+{
+ while (t) {
+ if (nint(t->car->car) == NODE_SPLAT) return FALSE;
+ t = t->cdr;
+ }
+ return TRUE;
+}
+
+static mrb_sym
+attrsym(codegen_scope *s, mrb_sym a)
+{
+ const char *name;
+ mrb_int len;
+ char *name2;
+
+ name = mrb_sym2name_len(s->mrb, a, &len);
+ name2 = (char *)codegen_palloc(s,
+ (size_t)len
+ + 1 /* '=' */
+ + 1 /* '\0' */
+ );
+ mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
+ memcpy(name2, name, (size_t)len);
+ name2[len] = '=';
+ name2[len+1] = '\0';
+
+ return mrb_intern(s->mrb, name2, len+1);
+}
+
+#define CALL_MAXARGS 127
+
+static int
+gen_values(codegen_scope *s, node *t, int val, int extra)
+{
+ int n = 0;
+ int is_splat;
+
+ while (t) {
+ is_splat = nint(t->car->car) == NODE_SPLAT; /* splat mode */
+ if (
+ n+extra >= CALL_MAXARGS - 1 /* need to subtract one because vm.c expects an array if n == CALL_MAXARGS */
+ || is_splat) {
+ if (val) {
+ if (is_splat && n == 0 && nint(t->car->cdr->car) == NODE_ARRAY) {
+ codegen(s, t->car->cdr, VAL);
+ pop();
+ }
+ else {
+ pop_n(n);
+ genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
+ push();
+ codegen(s, t->car, VAL);
+ pop(); pop();
+ if (is_splat) {
+ genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+ }
+ else {
+ genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+ }
+ }
+ t = t->cdr;
+ while (t) {
+ push();
+ codegen(s, t->car, VAL);
+ pop(); pop();
+ if (nint(t->car->car) == NODE_SPLAT) {
+ genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+ }
+ else {
+ genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+ }
+ t = t->cdr;
+ }
+ }
+ else {
+ while (t) {
+ codegen(s, t->car, NOVAL);
+ t = t->cdr;
+ }
+ }
+ return -1;
+ }
+ /* normal (no splat) mode */
+ codegen(s, t->car, val);
+ n++;
+ t = t->cdr;
+ }
+ return n;
+}
+
+static void
+gen_call(codegen_scope *s, node *tree, mrb_sym name, int sp, int val, int safe)
+{
+ mrb_sym sym = name ? name : nsym(tree->cdr->car);
+ int idx, skip = 0;
+ int n = 0, noop = 0, sendv = 0, blk = 0;
+
+ codegen(s, tree->car, VAL); /* receiver */
+ if (safe) {
+ int recv = cursp()-1;
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ genop(s, MKOP_AB(OP_MOVE, cursp(), recv));
+ push_n(2); pop_n(2); /* space for one arg and a block */
+ pop();
+ idx = new_msym(s, mrb_intern_lit(s->mrb, "=="));
+ genop(s, MKOP_ABC(OP_EQ, cursp(), idx, 1));
+ skip = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0));
+ }
+ idx = new_msym(s, sym);
+ tree = tree->cdr->cdr->car;
+ if (tree) {
+ n = gen_values(s, tree->car, VAL, sp?1:0);
+ if (n < 0) {
+ n = noop = sendv = 1;
+ push();
+ }
+ }
+ if (sp) {
+ if (sendv) {
+ pop();
+ genop(s, MKOP_AB(OP_ARYPUSH, cursp(), sp));
+ push();
+ }
+ else {
+ genop(s, MKOP_AB(OP_MOVE, cursp(), sp));
+ push();
+ n++;
+ }
+ }
+ if (tree && tree->cdr) {
+ noop = 1;
+ codegen(s, tree->cdr, VAL);
+ pop();
+ }
+ else {
+ blk = cursp();
+ }
+ push();pop();
+ pop_n(n+1);
+ {
+ mrb_int symlen;
+ const char *symname = mrb_sym2name_len(s->mrb, sym, &symlen);
+
+ if (!noop && symlen == 1 && symname[0] == '+' && n == 1) {
+ genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, n), val);
+ }
+ else if (!noop && symlen == 1 && symname[0] == '-' && n == 1) {
+ genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, n), val);
+ }
+ else if (!noop && symlen == 1 && symname[0] == '*' && n == 1) {
+ genop(s, MKOP_ABC(OP_MUL, cursp(), idx, n));
+ }
+ else if (!noop && symlen == 1 && symname[0] == '/' && n == 1) {
+ genop(s, MKOP_ABC(OP_DIV, cursp(), idx, n));
+ }
+ else if (!noop && symlen == 1 && symname[0] == '<' && n == 1) {
+ genop(s, MKOP_ABC(OP_LT, cursp(), idx, n));
+ }
+ else if (!noop && symlen == 2 && symname[0] == '<' && symname[1] == '=' && n == 1) {
+ genop(s, MKOP_ABC(OP_LE, cursp(), idx, n));
+ }
+ else if (!noop && symlen == 1 && symname[0] == '>' && n == 1) {
+ genop(s, MKOP_ABC(OP_GT, cursp(), idx, n));
+ }
+ else if (!noop && symlen == 2 && symname[0] == '>' && symname[1] == '=' && n == 1) {
+ genop(s, MKOP_ABC(OP_GE, cursp(), idx, n));
+ }
+ else if (!noop && symlen == 2 && symname[0] == '=' && symname[1] == '=' && n == 1) {
+ genop(s, MKOP_ABC(OP_EQ, cursp(), idx, n));
+ }
+ else {
+ if (sendv) n = CALL_MAXARGS;
+ if (blk > 0) { /* no block */
+ genop(s, MKOP_ABC(OP_SEND, cursp(), idx, n));
+ }
+ else {
+ genop(s, MKOP_ABC(OP_SENDB, cursp(), idx, n));
+ }
+ }
+ }
+ if (safe) {
+ dispatch(s, skip);
+ }
+ if (val) {
+ push();
+ }
+}
+
+static void
+gen_assignment(codegen_scope *s, node *tree, int sp, int val)
+{
+ int idx;
+ int type = nint(tree->car);
+
+ tree = tree->cdr;
+ switch (type) {
+ case NODE_GVAR:
+ idx = new_sym(s, nsym(tree));
+ genop_peep(s, MKOP_ABx(OP_SETGLOBAL, sp, idx), val);
+ break;
+ case NODE_LVAR:
+ idx = lv_idx(s, nsym(tree));
+ if (idx > 0) {
+ if (idx != sp) {
+ genop_peep(s, MKOP_AB(OP_MOVE, idx, sp), val);
+ }
+ break;
+ }
+ else { /* upvar */
+ int lv = 0;
+ codegen_scope *up = s->prev;
+
+ while (up) {
+ idx = lv_idx(up, nsym(tree));
+ if (idx > 0) {
+ genop_peep(s, MKOP_ABC(OP_SETUPVAR, sp, idx, lv), val);
+ break;
+ }
+ lv++;
+ up = up->prev;
+ }
+ }
+ break;
+ case NODE_IVAR:
+ idx = new_sym(s, nsym(tree));
+ genop_peep(s, MKOP_ABx(OP_SETIV, sp, idx), val);
+ break;
+ case NODE_CVAR:
+ idx = new_sym(s, nsym(tree));
+ genop_peep(s, MKOP_ABx(OP_SETCV, sp, idx), val);
+ break;
+ case NODE_CONST:
+ idx = new_sym(s, nsym(tree));
+ genop_peep(s, MKOP_ABx(OP_SETCONST, sp, idx), val);
+ break;
+ case NODE_COLON2:
+ idx = new_sym(s, nsym(tree->cdr));
+ genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), NOVAL);
+ push();
+ codegen(s, tree->car, VAL);
+ pop_n(2);
+ genop_peep(s, MKOP_ABx(OP_SETMCNST, cursp(), idx), val);
+ break;
+
+ case NODE_CALL:
+ case NODE_SCALL:
+ push();
+ gen_call(s, tree, attrsym(s, nsym(tree->cdr->car)), sp, NOVAL,
+ type == NODE_SCALL);
+ pop();
+ if (val) {
+ genop_peep(s, MKOP_AB(OP_MOVE, cursp(), sp), val);
+ }
+ break;
+
+ case NODE_MASGN:
+ gen_vmassignment(s, tree->car, sp, val);
+ break;
+
+ /* splat without assignment */
+ case NODE_NIL:
+ break;
+
+ default:
+#ifndef MRB_DISABLE_STDIO
+ fprintf(stderr, "unknown lhs %d\n", type);
+#endif
+ break;
+ }
+ if (val) push();
+}
+
+static void
+gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val)
+{
+ int n = 0, post = 0;
+ node *t, *p;
+
+ if (tree->car) { /* pre */
+ t = tree->car;
+ n = 0;
+ while (t) {
+ genop(s, MKOP_ABC(OP_AREF, cursp(), rhs, n));
+ gen_assignment(s, t->car, cursp(), NOVAL);
+ n++;
+ t = t->cdr;
+ }
+ }
+ t = tree->cdr;
+ if (t) {
+ if (t->cdr) { /* post count */
+ p = t->cdr->car;
+ while (p) {
+ post++;
+ p = p->cdr;
+ }
+ }
+ if (val) {
+ genop(s, MKOP_AB(OP_MOVE, cursp(), rhs));
+ }
+ else {
+ pop();
+ }
+ push_n(post);
+ pop_n(post);
+ genop(s, MKOP_ABC(OP_APOST, cursp(), n, post));
+ n = 1;
+ if (t->car) { /* rest */
+ gen_assignment(s, t->car, cursp(), NOVAL);
+ }
+ if (t->cdr && t->cdr->car) {
+ t = t->cdr->car;
+ while (t) {
+ gen_assignment(s, t->car, cursp()+n, NOVAL);
+ t = t->cdr;
+ n++;
+ }
+ }
+ if (!val) {
+ push();
+ }
+ }
+}
+
+static void
+gen_send_intern(codegen_scope *s)
+{
+ push();pop(); /* space for a block */
+ pop();
+ genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "intern")), 0));
+ push();
+}
+static void
+gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
+{
+ if (val) {
+ int i = 0, j = 0;
+
+ while (tree) {
+ switch (nint(tree->car->car)) {
+ case NODE_STR:
+ if ((tree->cdr == NULL) && (nint(tree->car->cdr->cdr) == 0))
+ break;
+ /* fall through */
+ case NODE_BEGIN:
+ codegen(s, tree->car, VAL);
+ ++j;
+ break;
+
+ case NODE_LITERAL_DELIM:
+ if (j > 0) {
+ j = 0;
+ ++i;
+ if (sym)
+ gen_send_intern(s);
+ }
+ break;
+ }
+ if (j >= 2) {
+ pop(); pop();
+ genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ push();
+ j = 1;
+ }
+ tree = tree->cdr;
+ }
+ if (j > 0) {
+ ++i;
+ if (sym)
+ gen_send_intern(s);
+ }
+ pop_n(i);
+ genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), i));
+ push();
+ }
+ else {
+ while (tree) {
+ switch (nint(tree->car->car)) {
+ case NODE_BEGIN: case NODE_BLOCK:
+ codegen(s, tree->car, NOVAL);
+ }
+ tree = tree->cdr;
+ }
+ }
+}
+
+static void
+raise_error(codegen_scope *s, const char *msg)
+{
+ int idx = new_lit(s, mrb_str_new_cstr(s->mrb, msg));
+
+ genop(s, MKOP_ABx(OP_ERR, 1, idx));
+}
+
+static double
+readint_float(codegen_scope *s, const char *p, int base)
+{
+ const char *e = p + strlen(p);
+ double f = 0;
+ int n;
+
+ if (*p == '+') p++;
+ while (p < e) {
+ char c = *p;
+ c = tolower((unsigned char)c);
+ for (n=0; n<base; n++) {
+ if (mrb_digitmap[n] == c) {
+ f *= base;
+ f += n;
+ break;
+ }
+ }
+ if (n == base) {
+ codegen_error(s, "malformed readint input");
+ }
+ p++;
+ }
+ return f;
+}
+
+static mrb_int
+readint_mrb_int(codegen_scope *s, const char *p, int base, mrb_bool neg, mrb_bool *overflow)
+{
+ const char *e = p + strlen(p);
+ mrb_int result = 0;
+ int n;
+
+ mrb_assert(base >= 2 && base <= 36);
+ if (*p == '+') p++;
+ while (p < e) {
+ char c = *p;
+ c = tolower((unsigned char)c);
+ for (n=0; n<base; n++) {
+ if (mrb_digitmap[n] == c) {
+ break;
+ }
+ }
+ if (n == base) {
+ codegen_error(s, "malformed readint input");
+ }
+
+ if (neg) {
+ if ((MRB_INT_MIN + n)/base > result) {
+ *overflow = TRUE;
+ return 0;
+ }
+ result *= base;
+ result -= n;
+ }
+ else {
+ if ((MRB_INT_MAX - n)/base < result) {
+ *overflow = TRUE;
+ return 0;
+ }
+ result *= base;
+ result += n;
+ }
+ p++;
+ }
+ *overflow = FALSE;
+ return result;
+}
+
+static void
+gen_retval(codegen_scope *s, node *tree)
+{
+ if (nint(tree->car) == NODE_SPLAT) {
+ genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), 0));
+ push();
+ codegen(s, tree, VAL);
+ pop(); pop();
+ genop(s, MKOP_AB(OP_ARYCAT, cursp(), cursp()+1));
+ }
+ else {
+ codegen(s, tree, VAL);
+ pop();
+ }
+}
+
+static void
+codegen(codegen_scope *s, node *tree, int val)
+{
+ int nt;
+ int rlev = s->rlev;
+
+ if (!tree) {
+ if (val) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ }
+ return;
+ }
+
+ s->rlev++;
+ if (s->rlev > MRB_CODEGEN_LEVEL_MAX) {
+ codegen_error(s, "too complex expression");
+ }
+ if (s->irep && s->filename_index != tree->filename_index) {
+ s->irep->filename = mrb_parser_get_filename(s->parser, s->filename_index);
+ mrb_debug_info_append_file(s->mrb, s->irep, s->debug_start_pos, s->pc);
+ s->debug_start_pos = s->pc;
+ s->filename_index = tree->filename_index;
+ s->filename = mrb_parser_get_filename(s->parser, tree->filename_index);
+ }
+
+ nt = nint(tree->car);
+ s->lineno = tree->lineno;
+ tree = tree->cdr;
+ switch (nt) {
+ case NODE_BEGIN:
+ if (val && !tree) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ }
+ while (tree) {
+ codegen(s, tree->car, tree->cdr ? NOVAL : val);
+ tree = tree->cdr;
+ }
+ break;
+
+ case NODE_RESCUE:
+ {
+ int onerr, noexc, exend, pos1, pos2, tmp;
+ struct loopinfo *lp;
+
+ if (tree->car == NULL) goto exit;
+ onerr = genop(s, MKOP_Bx(OP_ONERR, 0));
+ lp = loop_push(s, LOOP_BEGIN);
+ lp->pc1 = onerr;
+ codegen(s, tree->car, VAL);
+ pop();
+ lp->type = LOOP_RESCUE;
+ noexc = genop(s, MKOP_Bx(OP_JMP, 0));
+ dispatch(s, onerr);
+ tree = tree->cdr;
+ exend = 0;
+ pos1 = 0;
+ if (tree->car) {
+ node *n2 = tree->car;
+ int exc = cursp();
+
+ genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0));
+ push();
+ while (n2) {
+ node *n3 = n2->car;
+ node *n4 = n3->car;
+
+ if (pos1) dispatch(s, pos1);
+ pos2 = 0;
+ do {
+ if (n4 && n4->car && nint(n4->car->car) == NODE_SPLAT) {
+ codegen(s, n4->car, VAL);
+ genop(s, MKOP_AB(OP_MOVE, cursp(), exc));
+ push_n(2); pop_n(2); /* space for one arg and a block */
+ pop();
+ genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
+ }
+ else {
+ if (n4) {
+ codegen(s, n4->car, VAL);
+ }
+ else {
+ genop(s, MKOP_ABx(OP_GETCONST, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "StandardError"))));
+ push();
+ }
+ pop();
+ genop(s, MKOP_ABC(OP_RESCUE, exc, cursp(), 1));
+ }
+ tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
+ pos2 = tmp;
+ if (n4) {
+ n4 = n4->cdr;
+ }
+ } while (n4);
+ pos1 = genop(s, MKOP_sBx(OP_JMP, 0));
+ dispatch_linked(s, pos2);
+
+ pop();
+ if (n3->cdr->car) {
+ gen_assignment(s, n3->cdr->car, exc, NOVAL);
+ }
+ if (n3->cdr->cdr->car) {
+ codegen(s, n3->cdr->cdr->car, val);
+ if (val) pop();
+ }
+ tmp = genop(s, MKOP_sBx(OP_JMP, exend));
+ exend = tmp;
+ n2 = n2->cdr;
+ push();
+ }
+ if (pos1) {
+ dispatch(s, pos1);
+ genop(s, MKOP_A(OP_RAISE, exc));
+ }
+ }
+ pop();
+ tree = tree->cdr;
+ dispatch(s, noexc);
+ genop(s, MKOP_A(OP_POPERR, 1));
+ if (tree->car) {
+ codegen(s, tree->car, val);
+ }
+ else if (val) {
+ push();
+ }
+ dispatch_linked(s, exend);
+ loop_pop(s, NOVAL);
+ }
+ break;
+
+ case NODE_ENSURE:
+ if (!tree->cdr || !tree->cdr->cdr ||
+ (nint(tree->cdr->cdr->car) == NODE_BEGIN &&
+ tree->cdr->cdr->cdr)) {
+ int idx;
+ int epush = s->pc;
+
+ genop(s, MKOP_Bx(OP_EPUSH, 0));
+ s->ensure_level++;
+ codegen(s, tree->car, val);
+ idx = scope_body(s, tree->cdr, NOVAL);
+ s->iseq[epush] = MKOP_Bx(OP_EPUSH, idx);
+ s->ensure_level--;
+ genop_peep(s, MKOP_A(OP_EPOP, 1), NOVAL);
+ }
+ else { /* empty ensure ignored */
+ codegen(s, tree->car, val);
+ }
+ break;
+
+ case NODE_LAMBDA:
+ if (val) {
+ int idx = lambda_body(s, tree, 1);
+
+ genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_LAMBDA));
+ push();
+ }
+ break;
+
+ case NODE_BLOCK:
+ if (val) {
+ int idx = lambda_body(s, tree, 1);
+
+ genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_BLOCK));
+ push();
+ }
+ break;
+
+ case NODE_IF:
+ {
+ int pos1, pos2;
+ node *e = tree->cdr->cdr->car;
+
+ if (!tree->car) {
+ codegen(s, e, val);
+ goto exit;
+ }
+ switch (nint(tree->car->car)) {
+ case NODE_TRUE:
+ case NODE_INT:
+ case NODE_STR:
+ codegen(s, tree->cdr->car, val);
+ goto exit;
+ case NODE_FALSE:
+ case NODE_NIL:
+ codegen(s, e, val);
+ goto exit;
+ }
+ codegen(s, tree->car, VAL);
+ pop();
+ pos1 = genop_peep(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0), NOVAL);
+
+ codegen(s, tree->cdr->car, val);
+ if (e) {
+ if (val) pop();
+ pos2 = genop(s, MKOP_sBx(OP_JMP, 0));
+ dispatch(s, pos1);
+ codegen(s, e, val);
+ dispatch(s, pos2);
+ }
+ else {
+ if (val) {
+ pop();
+ pos2 = genop(s, MKOP_sBx(OP_JMP, 0));
+ dispatch(s, pos1);
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ dispatch(s, pos2);
+ push();
+ }
+ else {
+ dispatch(s, pos1);
+ }
+ }
+ }
+ break;
+
+ case NODE_AND:
+ {
+ int pos;
+
+ codegen(s, tree->car, VAL);
+ pop();
+ pos = genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), 0));
+ codegen(s, tree->cdr, val);
+ dispatch(s, pos);
+ }
+ break;
+
+ case NODE_OR:
+ {
+ int pos;
+
+ codegen(s, tree->car, VAL);
+ pop();
+ pos = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), 0));
+ codegen(s, tree->cdr, val);
+ dispatch(s, pos);
+ }
+ break;
+
+ case NODE_WHILE:
+ {
+ struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
+
+ lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0));
+ lp->pc2 = new_label(s);
+ codegen(s, tree->cdr, NOVAL);
+ dispatch(s, lp->pc1);
+ codegen(s, tree->car, VAL);
+ pop();
+ genop(s, MKOP_AsBx(OP_JMPIF, cursp(), lp->pc2 - s->pc));
+
+ loop_pop(s, val);
+ }
+ break;
+
+ case NODE_UNTIL:
+ {
+ struct loopinfo *lp = loop_push(s, LOOP_NORMAL);
+
+ lp->pc1 = genop(s, MKOP_sBx(OP_JMP, 0));
+ lp->pc2 = new_label(s);
+ codegen(s, tree->cdr, NOVAL);
+ dispatch(s, lp->pc1);
+ codegen(s, tree->car, VAL);
+ pop();
+ genop(s, MKOP_AsBx(OP_JMPNOT, cursp(), lp->pc2 - s->pc));
+
+ loop_pop(s, val);
+ }
+ break;
+
+ case NODE_FOR:
+ for_body(s, tree);
+ if (val) push();
+ break;
+
+ case NODE_CASE:
+ {
+ int head = 0;
+ int pos1, pos2, pos3, tmp;
+ node *n;
+
+ pos3 = 0;
+ if (tree->car) {
+ head = cursp();
+ codegen(s, tree->car, VAL);
+ }
+ tree = tree->cdr;
+ while (tree) {
+ n = tree->car->car;
+ pos1 = pos2 = 0;
+ while (n) {
+ codegen(s, n->car, VAL);
+ if (head) {
+ genop(s, MKOP_AB(OP_MOVE, cursp(), head));
+ push_n(2); pop_n(2); /* space for one arg and a block */
+ pop();
+ if (nint(n->car->car) == NODE_SPLAT) {
+ genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__case_eqq")), 1));
+ }
+ else {
+ genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "===")), 1));
+ }
+ }
+ else {
+ pop();
+ }
+ tmp = genop(s, MKOP_AsBx(OP_JMPIF, cursp(), pos2));
+ pos2 = tmp;
+ n = n->cdr;
+ }
+ if (tree->car->car) {
+ pos1 = genop(s, MKOP_sBx(OP_JMP, 0));
+ dispatch_linked(s, pos2);
+ }
+ codegen(s, tree->car->cdr, val);
+ if (val) pop();
+ tmp = genop(s, MKOP_sBx(OP_JMP, pos3));
+ pos3 = tmp;
+ if (pos1) dispatch(s, pos1);
+ tree = tree->cdr;
+ }
+ if (val) {
+ int pos = cursp();
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ if (pos3) dispatch_linked(s, pos3);
+ if (head) pop();
+ if (cursp() != pos) {
+ genop(s, MKOP_AB(OP_MOVE, cursp(), pos));
+ }
+ push();
+ }
+ else {
+ if (pos3) {
+ dispatch_linked(s, pos3);
+ }
+ if (head) {
+ pop();
+ }
+ }
+ }
+ break;
+
+ case NODE_SCOPE:
+ scope_body(s, tree, NOVAL);
+ break;
+
+ case NODE_FCALL:
+ case NODE_CALL:
+ gen_call(s, tree, 0, 0, val, 0);
+ break;
+ case NODE_SCALL:
+ gen_call(s, tree, 0, 0, val, 1);
+ break;
+
+ case NODE_DOT2:
+ codegen(s, tree->car, val);
+ codegen(s, tree->cdr, val);
+ if (val) {
+ pop(); pop();
+ genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), FALSE));
+ push();
+ }
+ break;
+
+ case NODE_DOT3:
+ codegen(s, tree->car, val);
+ codegen(s, tree->cdr, val);
+ if (val) {
+ pop(); pop();
+ genop(s, MKOP_ABC(OP_RANGE, cursp(), cursp(), TRUE));
+ push();
+ }
+ break;
+
+ case NODE_COLON2:
+ {
+ int sym = new_sym(s, nsym(tree->cdr));
+
+ codegen(s, tree->car, VAL);
+ pop();
+ genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ if (val) push();
+ }
+ break;
+
+ case NODE_COLON3:
+ {
+ int sym = new_sym(s, nsym(tree));
+
+ genop(s, MKOP_A(OP_OCLASS, cursp()));
+ genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ if (val) push();
+ }
+ break;
+
+ case NODE_ARRAY:
+ {
+ int n;
+
+ n = gen_values(s, tree, val, 0);
+ if (n >= 0) {
+ if (val) {
+ pop_n(n);
+ genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), n));
+ push();
+ }
+ }
+ else if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_HASH:
+ {
+ int len = 0;
+ mrb_bool update = FALSE;
+
+ while (tree) {
+ codegen(s, tree->car->car, val);
+ codegen(s, tree->car->cdr, val);
+ len++;
+ tree = tree->cdr;
+ if (val && len == 126) {
+ pop_n(len*2);
+ genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len));
+ if (update) {
+ pop();
+ genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1));
+ }
+ push();
+ update = TRUE;
+ len = 0;
+ }
+ }
+ if (val) {
+ pop_n(len*2);
+ genop(s, MKOP_ABC(OP_HASH, cursp(), cursp(), len));
+ if (update) {
+ pop();
+ genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "__update")), 1));
+ }
+ push();
+ }
+ }
+ break;
+
+ case NODE_SPLAT:
+ codegen(s, tree, val);
+ break;
+
+ case NODE_ASGN:
+ codegen(s, tree->cdr, VAL);
+ pop();
+ gen_assignment(s, tree->car, cursp(), val);
+ break;
+
+ case NODE_MASGN:
+ {
+ int len = 0, n = 0, post = 0;
+ node *t = tree->cdr, *p;
+ int rhs = cursp();
+
+ if (nint(t->car) == NODE_ARRAY && t->cdr && nosplat(t->cdr)) {
+ /* fixed rhs */
+ t = t->cdr;
+ while (t) {
+ codegen(s, t->car, VAL);
+ len++;
+ t = t->cdr;
+ }
+ tree = tree->car;
+ if (tree->car) { /* pre */
+ t = tree->car;
+ n = 0;
+ while (t) {
+ if (n < len) {
+ gen_assignment(s, t->car, rhs+n, NOVAL);
+ n++;
+ }
+ else {
+ genop(s, MKOP_A(OP_LOADNIL, rhs+n));
+ gen_assignment(s, t->car, rhs+n, NOVAL);
+ }
+ t = t->cdr;
+ }
+ }
+ t = tree->cdr;
+ if (t) {
+ if (t->cdr) { /* post count */
+ p = t->cdr->car;
+ while (p) {
+ post++;
+ p = p->cdr;
+ }
+ }
+ if (t->car) { /* rest (len - pre - post) */
+ int rn;
+
+ if (len < post + n) {
+ rn = 0;
+ }
+ else {
+ rn = len - post - n;
+ }
+ genop(s, MKOP_ABC(OP_ARRAY, cursp(), rhs+n, rn));
+ gen_assignment(s, t->car, cursp(), NOVAL);
+ n += rn;
+ }
+ if (t->cdr && t->cdr->car) {
+ t = t->cdr->car;
+ while (n<len) {
+ gen_assignment(s, t->car, rhs+n, NOVAL);
+ t = t->cdr;
+ n++;
+ }
+ }
+ }
+ pop_n(len);
+ if (val) {
+ genop(s, MKOP_ABC(OP_ARRAY, rhs, rhs, len));
+ push();
+ }
+ }
+ else {
+ /* variable rhs */
+ codegen(s, t, VAL);
+ gen_vmassignment(s, tree->car, rhs, val);
+ if (!val) {
+ pop();
+ }
+ }
+ }
+ break;
+
+ case NODE_OP_ASGN:
+ {
+ mrb_sym sym = nsym(tree->cdr->car);
+ mrb_int len;
+ const char *name = mrb_sym2name_len(s->mrb, sym, &len);
+ int idx, callargs = -1, vsp = -1;
+
+ if ((len == 2 && name[0] == '|' && name[1] == '|') &&
+ (nint(tree->car->car) == NODE_CONST ||
+ nint(tree->car->car) == NODE_CVAR)) {
+ int onerr, noexc, exc;
+ struct loopinfo *lp;
+
+ onerr = genop(s, MKOP_Bx(OP_ONERR, 0));
+ lp = loop_push(s, LOOP_BEGIN);
+ lp->pc1 = onerr;
+ exc = cursp();
+ codegen(s, tree->car, VAL);
+ lp->type = LOOP_RESCUE;
+ genop(s, MKOP_A(OP_POPERR, 1));
+ noexc = genop(s, MKOP_Bx(OP_JMP, 0));
+ dispatch(s, onerr);
+ genop(s, MKOP_ABC(OP_RESCUE, exc, 0, 0));
+ genop(s, MKOP_A(OP_LOADF, exc));
+ dispatch(s, noexc);
+ loop_pop(s, NOVAL);
+ }
+ else if (nint(tree->car->car) == NODE_CALL) {
+ node *n = tree->car->cdr;
+ int base, i, nargs = 0;
+ callargs = 0;
+
+ if (val) {
+ vsp = cursp();
+ push();
+ }
+ codegen(s, n->car, VAL); /* receiver */
+ idx = new_msym(s, nsym(n->cdr->car));
+ base = cursp()-1;
+ if (n->cdr->cdr->car) {
+ nargs = gen_values(s, n->cdr->cdr->car->car, VAL, 1);
+ if (nargs >= 0) {
+ callargs = nargs;
+ }
+ else { /* varargs */
+ push();
+ nargs = 1;
+ callargs = CALL_MAXARGS;
+ }
+ }
+ /* copy receiver and arguments */
+ genop(s, MKOP_AB(OP_MOVE, cursp(), base));
+ for (i=0; i<nargs; i++) {
+ genop(s, MKOP_AB(OP_MOVE, cursp()+i+1, base+i+1));
+ }
+ push_n(nargs+2);pop_n(nargs+2); /* space for receiver, arguments and a block */
+ genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+ push();
+ }
+ else {
+ codegen(s, tree->car, VAL);
+ }
+ if (len == 2 &&
+ ((name[0] == '|' && name[1] == '|') ||
+ (name[0] == '&' && name[1] == '&'))) {
+ int pos;
+
+ pop();
+ if (val) {
+ if (vsp >= 0) {
+ genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+ }
+ pos = genop(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0));
+ }
+ else {
+ pos = genop_peep(s, MKOP_AsBx(name[0]=='|'?OP_JMPIF:OP_JMPNOT, cursp(), 0), NOVAL);
+ }
+ codegen(s, tree->cdr->cdr->car, VAL);
+ pop();
+ if (val && vsp >= 0) {
+ genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+ }
+ if (nint(tree->car->car) == NODE_CALL) {
+ if (callargs == CALL_MAXARGS) {
+ pop();
+ genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+ }
+ else {
+ pop_n(callargs);
+ callargs++;
+ }
+ pop();
+ idx = new_msym(s, attrsym(s, nsym(tree->car->cdr->cdr->car)));
+ genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+ }
+ else {
+ gen_assignment(s, tree->car, cursp(), val);
+ }
+ dispatch(s, pos);
+ goto exit;
+ }
+ codegen(s, tree->cdr->cdr->car, VAL);
+ push(); pop();
+ pop(); pop();
+
+ idx = new_msym(s, sym);
+ if (len == 1 && name[0] == '+') {
+ genop_peep(s, MKOP_ABC(OP_ADD, cursp(), idx, 1), val);
+ }
+ else if (len == 1 && name[0] == '-') {
+ genop_peep(s, MKOP_ABC(OP_SUB, cursp(), idx, 1), val);
+ }
+ else if (len == 1 && name[0] == '*') {
+ genop(s, MKOP_ABC(OP_MUL, cursp(), idx, 1));
+ }
+ else if (len == 1 && name[0] == '/') {
+ genop(s, MKOP_ABC(OP_DIV, cursp(), idx, 1));
+ }
+ else if (len == 1 && name[0] == '<') {
+ genop(s, MKOP_ABC(OP_LT, cursp(), idx, 1));
+ }
+ else if (len == 2 && name[0] == '<' && name[1] == '=') {
+ genop(s, MKOP_ABC(OP_LE, cursp(), idx, 1));
+ }
+ else if (len == 1 && name[0] == '>') {
+ genop(s, MKOP_ABC(OP_GT, cursp(), idx, 1));
+ }
+ else if (len == 2 && name[0] == '>' && name[1] == '=') {
+ genop(s, MKOP_ABC(OP_GE, cursp(), idx, 1));
+ }
+ else {
+ genop(s, MKOP_ABC(OP_SEND, cursp(), idx, 1));
+ }
+ if (callargs < 0) {
+ gen_assignment(s, tree->car, cursp(), val);
+ }
+ else {
+ if (val && vsp >= 0) {
+ genop(s, MKOP_AB(OP_MOVE, vsp, cursp()));
+ }
+ if (callargs == CALL_MAXARGS) {
+ pop();
+ genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+ }
+ else {
+ pop_n(callargs);
+ callargs++;
+ }
+ pop();
+ idx = new_msym(s, attrsym(s,nsym(tree->car->cdr->cdr->car)));
+ genop(s, MKOP_ABC(OP_SEND, cursp(), idx, callargs));
+ }
+ }
+ break;
+
+ case NODE_SUPER:
+ {
+ codegen_scope *s2 = s;
+ int lv = 0;
+ int n = 0, noop = 0, sendv = 0;
+
+ push(); /* room for receiver */
+ while (!s2->mscope) {
+ lv++;
+ s2 = s2->prev;
+ if (!s2) break;
+ }
+ genop(s, MKOP_ABx(OP_ARGARY, cursp(), (lv & 0xf)));
+ push(); push(); /* ARGARY pushes two values */
+ pop(); pop();
+ if (tree) {
+ node *args = tree->car;
+ if (args) {
+ n = gen_values(s, args, VAL, 0);
+ if (n < 0) {
+ n = noop = sendv = 1;
+ push();
+ }
+ }
+ }
+ if (tree && tree->cdr) {
+ codegen(s, tree->cdr, VAL);
+ pop();
+ }
+ else {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push(); pop();
+ }
+ pop_n(n+1);
+ if (sendv) n = CALL_MAXARGS;
+ genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, n));
+ if (val) push();
+ }
+ break;
+
+ case NODE_ZSUPER:
+ {
+ codegen_scope *s2 = s;
+ int lv = 0, ainfo = 0;
+
+ push(); /* room for receiver */
+ while (!s2->mscope) {
+ lv++;
+ s2 = s2->prev;
+ if (!s2) break;
+ }
+ if (s2) ainfo = s2->ainfo;
+ genop(s, MKOP_ABx(OP_ARGARY, cursp(), (ainfo<<4)|(lv & 0xf)));
+ push(); push(); pop(); /* ARGARY pushes two values */
+ if (tree && tree->cdr) {
+ codegen(s, tree->cdr, VAL);
+ pop();
+ }
+ pop(); pop();
+ genop(s, MKOP_ABC(OP_SUPER, cursp(), 0, CALL_MAXARGS));
+ if (val) push();
+ }
+ break;
+
+ case NODE_RETURN:
+ if (tree) {
+ gen_retval(s, tree);
+ }
+ else {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ }
+ if (s->loop) {
+ genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_RETURN));
+ }
+ else {
+ genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
+ }
+ if (val) push();
+ break;
+
+ case NODE_YIELD:
+ {
+ codegen_scope *s2 = s;
+ int lv = 0, ainfo = 0;
+ int n = 0, sendv = 0;
+
+ while (!s2->mscope) {
+ lv++;
+ s2 = s2->prev;
+ if (!s2) break;
+ }
+ if (s2) ainfo = s2->ainfo;
+ push();
+ if (tree) {
+ n = gen_values(s, tree, VAL, 0);
+ if (n < 0) {
+ n = sendv = 1;
+ push();
+ }
+ }
+ push();pop(); /* space for a block */
+ pop_n(n+1);
+ genop(s, MKOP_ABx(OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf)));
+ if (sendv) n = CALL_MAXARGS;
+ genop(s, MKOP_ABC(OP_SEND, cursp(), new_msym(s, mrb_intern_lit(s->mrb, "call")), n));
+ if (val) push();
+ }
+ break;
+
+ case NODE_BREAK:
+ loop_break(s, tree);
+ if (val) push();
+ break;
+
+ case NODE_NEXT:
+ if (!s->loop) {
+ raise_error(s, "unexpected next");
+ }
+ else if (s->loop->type == LOOP_NORMAL) {
+ if (s->ensure_level > s->loop->ensure_level) {
+ genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
+ }
+ codegen(s, tree, NOVAL);
+ genop(s, MKOP_sBx(OP_JMP, s->loop->pc1 - s->pc));
+ }
+ else {
+ if (tree) {
+ codegen(s, tree, VAL);
+ pop();
+ }
+ else {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ }
+ genop_peep(s, MKOP_AB(OP_RETURN, cursp(), OP_R_NORMAL), NOVAL);
+ }
+ if (val) push();
+ break;
+
+ case NODE_REDO:
+ if (!s->loop || s->loop->type == LOOP_BEGIN || s->loop->type == LOOP_RESCUE) {
+ raise_error(s, "unexpected redo");
+ }
+ else {
+ if (s->ensure_level > s->loop->ensure_level) {
+ genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
+ }
+ genop(s, MKOP_sBx(OP_JMP, s->loop->pc2 - s->pc));
+ }
+ if (val) push();
+ break;
+
+ case NODE_RETRY:
+ {
+ const char *msg = "unexpected retry";
+
+ if (!s->loop) {
+ raise_error(s, msg);
+ }
+ else {
+ struct loopinfo *lp = s->loop;
+ int n = 0;
+
+ while (lp && lp->type != LOOP_RESCUE) {
+ if (lp->type == LOOP_BEGIN) {
+ n++;
+ }
+ lp = lp->prev;
+ }
+ if (!lp) {
+ raise_error(s, msg);
+ }
+ else {
+ if (n > 0) {
+ genop_peep(s, MKOP_A(OP_POPERR, n), NOVAL);
+ }
+ if (s->ensure_level > lp->ensure_level) {
+ genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - lp->ensure_level), NOVAL);
+ }
+ genop(s, MKOP_sBx(OP_JMP, lp->pc1 - s->pc));
+ }
+ }
+ if (val) push();
+ }
+ break;
+
+ case NODE_LVAR:
+ if (val) {
+ int idx = lv_idx(s, nsym(tree));
+
+ if (idx > 0) {
+ genop_peep(s, MKOP_AB(OP_MOVE, cursp(), idx), NOVAL);
+ }
+ else {
+ int lv = 0;
+ codegen_scope *up = s->prev;
+
+ while (up) {
+ idx = lv_idx(up, nsym(tree));
+ if (idx > 0) {
+ genop(s, MKOP_ABC(OP_GETUPVAR, cursp(), idx, lv));
+ break;
+ }
+ lv++;
+ up = up->prev;
+ }
+ }
+ push();
+ }
+ break;
+
+ case NODE_GVAR:
+ if (val) {
+ int sym = new_sym(s, nsym(tree));
+
+ genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_IVAR:
+ if (val) {
+ int sym = new_sym(s, nsym(tree));
+
+ genop(s, MKOP_ABx(OP_GETIV, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_CVAR:
+ if (val) {
+ int sym = new_sym(s, nsym(tree));
+
+ genop(s, MKOP_ABx(OP_GETCV, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_CONST:
+ {
+ int sym = new_sym(s, nsym(tree));
+
+ genop(s, MKOP_ABx(OP_GETCONST, cursp(), sym));
+ if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_DEFINED:
+ codegen(s, tree, VAL);
+ break;
+
+ case NODE_BACK_REF:
+ if (val) {
+ char buf[3];
+ int sym;
+
+ buf[0] = '$';
+ buf[1] = nchar(tree);
+ buf[2] = 0;
+ sym = new_sym(s, mrb_intern_cstr(s->mrb, buf));
+ genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_NTH_REF:
+ if (val) {
+ mrb_state *mrb = s->mrb;
+ mrb_value str;
+ int sym;
+
+ str = mrb_format(mrb, "$%S", mrb_fixnum_value(nint(tree)));
+ sym = new_sym(s, mrb_intern_str(mrb, str));
+ genop(s, MKOP_ABx(OP_GETGLOBAL, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_ARG:
+ /* should not happen */
+ break;
+
+ case NODE_BLOCK_ARG:
+ codegen(s, tree, VAL);
+ break;
+
+ case NODE_INT:
+ if (val) {
+ char *p = (char*)tree->car;
+ int base = nint(tree->cdr->car);
+ mrb_int i;
+ mrb_code co;
+ mrb_bool overflow;
+
+ i = readint_mrb_int(s, p, base, FALSE, &overflow);
+ if (overflow) {
+ double f = readint_float(s, p, base);
+ int off = new_lit(s, mrb_float_value(s->mrb, f));
+
+ genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ }
+ else {
+ if (i < MAXARG_sBx && i > -MAXARG_sBx) {
+ co = MKOP_AsBx(OP_LOADI, cursp(), i);
+ }
+ else {
+ int off = new_lit(s, mrb_fixnum_value(i));
+ co = MKOP_ABx(OP_LOADL, cursp(), off);
+ }
+ genop(s, co);
+ }
+ push();
+ }
+ break;
+
+ case NODE_FLOAT:
+ if (val) {
+ char *p = (char*)tree;
+ mrb_float f = mrb_float_read(p, NULL);
+ int off = new_lit(s, mrb_float_value(s->mrb, f));
+
+ genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ push();
+ }
+ break;
+
+ case NODE_NEGATE:
+ {
+ nt = nint(tree->car);
+ tree = tree->cdr;
+ switch (nt) {
+ case NODE_FLOAT:
+ if (val) {
+ char *p = (char*)tree;
+ mrb_float f = mrb_float_read(p, NULL);
+ int off = new_lit(s, mrb_float_value(s->mrb, -f));
+
+ genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ push();
+ }
+ break;
+
+ case NODE_INT:
+ if (val) {
+ char *p = (char*)tree->car;
+ int base = nint(tree->cdr->car);
+ mrb_int i;
+ mrb_code co;
+ mrb_bool overflow;
+
+ i = readint_mrb_int(s, p, base, TRUE, &overflow);
+ if (overflow) {
+ double f = readint_float(s, p, base);
+ int off = new_lit(s, mrb_float_value(s->mrb, -f));
+
+ genop(s, MKOP_ABx(OP_LOADL, cursp(), off));
+ }
+ else {
+ if (i < MAXARG_sBx && i > -MAXARG_sBx) {
+ co = MKOP_AsBx(OP_LOADI, cursp(), i);
+ }
+ else {
+ int off = new_lit(s, mrb_fixnum_value(i));
+ co = MKOP_ABx(OP_LOADL, cursp(), off);
+ }
+ genop(s, co);
+ }
+ push();
+ }
+ break;
+
+ default:
+ if (val) {
+ int sym = new_msym(s, mrb_intern_lit(s->mrb, "-"));
+
+ genop(s, MKOP_ABx(OP_LOADI, cursp(), 0));
+ push();
+ codegen(s, tree, VAL);
+ pop(); pop();
+ genop(s, MKOP_ABC(OP_SUB, cursp(), sym, 2));
+ }
+ else {
+ codegen(s, tree, NOVAL);
+ }
+ break;
+ }
+ }
+ break;
+
+ case NODE_STR:
+ if (val) {
+ char *p = (char*)tree->car;
+ size_t len = (intptr_t)tree->cdr;
+ int ai = mrb_gc_arena_save(s->mrb);
+ int off = new_lit(s, mrb_str_new(s->mrb, p, len));
+
+ mrb_gc_arena_restore(s->mrb, ai);
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ push();
+ }
+ break;
+
+ case NODE_HEREDOC:
+ tree = ((struct mrb_parser_heredoc_info *)tree)->doc;
+ /* fall through */
+ case NODE_DSTR:
+ if (val) {
+ node *n = tree;
+
+ if (!n) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ break;
+ }
+ codegen(s, n->car, VAL);
+ n = n->cdr;
+ while (n) {
+ codegen(s, n->car, VAL);
+ pop(); pop();
+ genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ push();
+ n = n->cdr;
+ }
+ }
+ else {
+ node *n = tree;
+
+ while (n) {
+ if (nint(n->car->car) != NODE_STR) {
+ codegen(s, n->car, NOVAL);
+ }
+ n = n->cdr;
+ }
+ }
+ break;
+
+ case NODE_WORDS:
+ gen_literal_array(s, tree, FALSE, val);
+ break;
+
+ case NODE_SYMBOLS:
+ gen_literal_array(s, tree, TRUE, val);
+ break;
+
+ case NODE_DXSTR:
+ {
+ node *n;
+ int ai = mrb_gc_arena_save(s->mrb);
+ int sym = new_sym(s, mrb_intern_lit(s->mrb, "Kernel"));
+
+ genop(s, MKOP_A(OP_LOADSELF, cursp()));
+ push();
+ codegen(s, tree->car, VAL);
+ n = tree->cdr;
+ while (n) {
+ if (nint(n->car->car) == NODE_XSTR) {
+ n->car->car = (struct mrb_ast_node*)(intptr_t)NODE_STR;
+ mrb_assert(!n->cdr); /* must be the end */
+ }
+ codegen(s, n->car, VAL);
+ pop(); pop();
+ genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ push();
+ n = n->cdr;
+ }
+ push(); /* for block */
+ pop_n(3);
+ sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
+ genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1));
+ if (val) push();
+ mrb_gc_arena_restore(s->mrb, ai);
+ }
+ break;
+
+ case NODE_XSTR:
+ {
+ char *p = (char*)tree->car;
+ size_t len = (intptr_t)tree->cdr;
+ int ai = mrb_gc_arena_save(s->mrb);
+ int off = new_lit(s, mrb_str_new(s->mrb, p, len));
+ int sym;
+
+ genop(s, MKOP_A(OP_LOADSELF, cursp()));
+ push();
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ push(); push();
+ pop_n(3);
+ sym = new_sym(s, mrb_intern_lit(s->mrb, "`"));
+ genop(s, MKOP_ABC(OP_SEND, cursp(), sym, 1));
+ if (val) push();
+ mrb_gc_arena_restore(s->mrb, ai);
+ }
+ break;
+
+ case NODE_REGX:
+ if (val) {
+ char *p1 = (char*)tree->car;
+ char *p2 = (char*)tree->cdr->car;
+ char *p3 = (char*)tree->cdr->cdr;
+ int ai = mrb_gc_arena_save(s->mrb);
+ int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS));
+ int off = new_lit(s, mrb_str_new_cstr(s->mrb, p1));
+ int argc = 1;
+
+ genop(s, MKOP_A(OP_OCLASS, cursp()));
+ genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ push();
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ push();
+ if (p2 || p3) {
+ if (p2) { /* opt */
+ off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ }
+ else {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ }
+ push();
+ argc++;
+ if (p3) { /* enc */
+ off = new_lit(s, mrb_str_new(s->mrb, p3, 1));
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ push();
+ argc++;
+ }
+ }
+ push(); /* space for a block */
+ pop_n(argc+2);
+ sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
+ genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc));
+ mrb_gc_arena_restore(s->mrb, ai);
+ push();
+ }
+ break;
+
+ case NODE_DREGX:
+ if (val) {
+ node *n = tree->car;
+ int ai = mrb_gc_arena_save(s->mrb);
+ int sym = new_sym(s, mrb_intern_lit(s->mrb, REGEXP_CLASS));
+ int argc = 1;
+ int off;
+ char *p;
+
+ genop(s, MKOP_A(OP_OCLASS, cursp()));
+ genop(s, MKOP_ABx(OP_GETMCNST, cursp(), sym));
+ push();
+ codegen(s, n->car, VAL);
+ n = n->cdr;
+ while (n) {
+ codegen(s, n->car, VAL);
+ pop(); pop();
+ genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ push();
+ n = n->cdr;
+ }
+ n = tree->cdr->cdr;
+ if (n->car) { /* tail */
+ p = (char*)n->car;
+ off = new_lit(s, mrb_str_new_cstr(s->mrb, p));
+ codegen(s, tree->car, VAL);
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ pop();
+ genop_peep(s, MKOP_AB(OP_STRCAT, cursp(), cursp()+1), VAL);
+ push();
+ }
+ if (n->cdr->car) { /* opt */
+ char *p2 = (char*)n->cdr->car;
+ off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ push();
+ argc++;
+ }
+ if (n->cdr->cdr) { /* enc */
+ char *p2 = (char*)n->cdr->cdr;
+ off = new_lit(s, mrb_str_new_cstr(s->mrb, p2));
+ genop(s, MKOP_ABx(OP_STRING, cursp(), off));
+ push();
+ argc++;
+ }
+ push(); /* space for a block */
+ pop_n(argc+2);
+ sym = new_sym(s, mrb_intern_lit(s->mrb, "compile"));
+ genop(s, MKOP_ABC(OP_SEND, cursp(), sym, argc));
+ mrb_gc_arena_restore(s->mrb, ai);
+ push();
+ }
+ else {
+ node *n = tree->car;
+
+ while (n) {
+ if (nint(n->car->car) != NODE_STR) {
+ codegen(s, n->car, NOVAL);
+ }
+ n = n->cdr;
+ }
+ }
+ break;
+
+ case NODE_SYM:
+ if (val) {
+ int sym = new_sym(s, nsym(tree));
+
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
+ push();
+ }
+ break;
+
+ case NODE_DSYM:
+ codegen(s, tree, val);
+ if (val) {
+ gen_send_intern(s);
+ }
+ break;
+
+ case NODE_SELF:
+ if (val) {
+ genop(s, MKOP_A(OP_LOADSELF, cursp()));
+ push();
+ }
+ break;
+
+ case NODE_NIL:
+ if (val) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ }
+ break;
+
+ case NODE_TRUE:
+ if (val) {
+ genop(s, MKOP_A(OP_LOADT, cursp()));
+ push();
+ }
+ break;
+
+ case NODE_FALSE:
+ if (val) {
+ genop(s, MKOP_A(OP_LOADF, cursp()));
+ push();
+ }
+ break;
+
+ case NODE_ALIAS:
+ {
+ int a = new_msym(s, nsym(tree->car));
+ int b = new_msym(s, nsym(tree->cdr));
+ int c = new_msym(s, mrb_intern_lit(s->mrb, "alias_method"));
+
+ genop(s, MKOP_A(OP_TCLASS, cursp()));
+ push();
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), a));
+ push();
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), b));
+ push();
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push(); /* space for a block */
+ pop_n(4);
+ genop(s, MKOP_ABC(OP_SEND, cursp(), c, 2));
+ if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_UNDEF:
+ {
+ int undef = new_msym(s, mrb_intern_lit(s->mrb, "undef_method"));
+ int num = 0;
+ node *t = tree;
+
+ genop(s, MKOP_A(OP_TCLASS, cursp()));
+ push();
+ while (t) {
+ int symbol;
+ if (num >= CALL_MAXARGS - 1) {
+ pop_n(num);
+ genop(s, MKOP_ABC(OP_ARRAY, cursp(), cursp(), num));
+ while (t) {
+ symbol = new_msym(s, nsym(t->car));
+ push();
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
+ pop();
+ genop(s, MKOP_AB(OP_ARYPUSH, cursp(), cursp()+1));
+ t = t->cdr;
+ }
+ num = CALL_MAXARGS;
+ break;
+ }
+ symbol = new_msym(s, nsym(t->car));
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), symbol));
+ push();
+ t = t->cdr;
+ num++;
+ }
+ push();pop(); /* space for a block */
+ pop();
+ if (num < CALL_MAXARGS) {
+ pop_n(num);
+ }
+ genop(s, MKOP_ABC(OP_SEND, cursp(), undef, num));
+ if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_CLASS:
+ {
+ int idx;
+
+ if (tree->car->car == (node*)0) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ }
+ else if (tree->car->car == (node*)1) {
+ genop(s, MKOP_A(OP_OCLASS, cursp()));
+ push();
+ }
+ else {
+ codegen(s, tree->car->car, VAL);
+ }
+ if (tree->cdr->car) {
+ codegen(s, tree->cdr->car, VAL);
+ }
+ else {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ }
+ pop(); pop();
+ idx = new_msym(s, nsym(tree->car->cdr));
+ genop(s, MKOP_AB(OP_CLASS, cursp(), idx));
+ idx = scope_body(s, tree->cdr->cdr->car, val);
+ genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
+ if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_MODULE:
+ {
+ int idx;
+
+ if (tree->car->car == (node*)0) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ push();
+ }
+ else if (tree->car->car == (node*)1) {
+ genop(s, MKOP_A(OP_OCLASS, cursp()));
+ push();
+ }
+ else {
+ codegen(s, tree->car->car, VAL);
+ }
+ pop();
+ idx = new_msym(s, nsym(tree->car->cdr));
+ genop(s, MKOP_AB(OP_MODULE, cursp(), idx));
+ idx = scope_body(s, tree->cdr->car, val);
+ genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
+ if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_SCLASS:
+ {
+ int idx;
+
+ codegen(s, tree->car, VAL);
+ pop();
+ genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp()));
+ idx = scope_body(s, tree->cdr->car, val);
+ genop(s, MKOP_ABx(OP_EXEC, cursp(), idx));
+ if (val) {
+ push();
+ }
+ }
+ break;
+
+ case NODE_DEF:
+ {
+ int sym = new_msym(s, nsym(tree->car));
+ int idx = lambda_body(s, tree->cdr, 0);
+
+ genop(s, MKOP_A(OP_TCLASS, cursp()));
+ push();
+ genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD));
+ push(); pop();
+ pop();
+ genop(s, MKOP_AB(OP_METHOD, cursp(), sym));
+ if (val) {
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
+ push();
+ }
+ }
+ break;
+
+ case NODE_SDEF:
+ {
+ node *recv = tree->car;
+ int sym = new_msym(s, nsym(tree->cdr->car));
+ int idx = lambda_body(s, tree->cdr->cdr, 0);
+
+ codegen(s, recv, VAL);
+ pop();
+ genop(s, MKOP_AB(OP_SCLASS, cursp(), cursp()));
+ push();
+ genop(s, MKOP_Abc(OP_LAMBDA, cursp(), idx, OP_L_METHOD));
+ pop();
+ genop(s, MKOP_AB(OP_METHOD, cursp(), sym));
+ if (val) {
+ genop(s, MKOP_ABx(OP_LOADSYM, cursp(), sym));
+ push();
+ }
+ }
+ break;
+
+ case NODE_POSTEXE:
+ codegen(s, tree, NOVAL);
+ break;
+
+ default:
+ break;
+ }
+ exit:
+ s->rlev = rlev;
+}
+
+static void
+scope_add_irep(codegen_scope *s, mrb_irep *irep)
+{
+ if (s->irep == NULL) {
+ s->irep = irep;
+ return;
+ }
+ if (s->irep->rlen == s->rcapa) {
+ s->rcapa *= 2;
+ s->irep->reps = (mrb_irep**)codegen_realloc(s, s->irep->reps, sizeof(mrb_irep*)*s->rcapa);
+ }
+ s->irep->reps[s->irep->rlen] = irep;
+ s->irep->rlen++;
+}
+
+static codegen_scope*
+scope_new(mrb_state *mrb, codegen_scope *prev, node *lv)
+{
+ static const codegen_scope codegen_scope_zero = { 0 };
+ mrb_pool *pool = mrb_pool_open(mrb);
+ codegen_scope *p = (codegen_scope *)mrb_pool_alloc(pool, sizeof(codegen_scope));
+
+ if (!p) return NULL;
+ *p = codegen_scope_zero;
+ p->mrb = mrb;
+ p->mpool = pool;
+ if (!prev) return p;
+ p->prev = prev;
+ p->ainfo = -1;
+ p->mscope = 0;
+
+ p->irep = mrb_add_irep(mrb);
+ scope_add_irep(prev, p->irep);
+
+ p->rcapa = 8;
+ p->irep->reps = (mrb_irep**)mrb_malloc(mrb, sizeof(mrb_irep*)*p->rcapa);
+
+ p->icapa = 1024;
+ p->iseq = (mrb_code*)mrb_malloc(mrb, sizeof(mrb_code)*p->icapa);
+ p->irep->iseq = NULL;
+
+ p->pcapa = 32;
+ p->irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value)*p->pcapa);
+ p->irep->plen = 0;
+
+ p->scapa = MAXMSYMLEN;
+ p->irep->syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym)*p->scapa);
+ p->irep->slen = 0;
+
+ p->lv = lv;
+ p->sp += node_len(lv)+1; /* add self */
+ p->nlocals = p->sp;
+ if (lv) {
+ node *n = lv;
+ size_t i = 0;
+
+ p->irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (p->nlocals - 1));
+ for (i=0, n=lv; n; i++,n=n->cdr) {
+ p->irep->lv[i].name = lv_name(n);
+ if (lv_name(n)) {
+ p->irep->lv[i].r = lv_idx(p, lv_name(n));
+ }
+ else {
+ p->irep->lv[i].r = 0;
+ }
+ }
+ mrb_assert(i + 1 == p->nlocals);
+ }
+ p->ai = mrb_gc_arena_save(mrb);
+
+ p->filename = prev->filename;
+ if (p->filename) {
+ p->lines = (uint16_t*)mrb_malloc(mrb, sizeof(short)*p->icapa);
+ }
+ p->lineno = prev->lineno;
+
+ /* debug setting */
+ p->debug_start_pos = 0;
+ if (p->filename) {
+ mrb_debug_info_alloc(mrb, p->irep);
+ p->irep->filename = p->filename;
+ p->irep->lines = p->lines;
+ }
+ else {
+ p->irep->debug_info = NULL;
+ }
+ p->parser = prev->parser;
+ p->filename_index = prev->filename_index;
+
+ p->rlev = prev->rlev+1;
+
+ return p;
+}
+
+static void
+scope_finish(codegen_scope *s)
+{
+ mrb_state *mrb = s->mrb;
+ mrb_irep *irep = s->irep;
+ size_t fname_len;
+ char *fname;
+
+ irep->flags = 0;
+ if (s->iseq) {
+ irep->iseq = (mrb_code *)codegen_realloc(s, s->iseq, sizeof(mrb_code)*s->pc);
+ irep->ilen = s->pc;
+ if (s->lines) {
+ irep->lines = (uint16_t *)codegen_realloc(s, s->lines, sizeof(uint16_t)*s->pc);
+ }
+ else {
+ irep->lines = 0;
+ }
+ }
+ irep->pool = (mrb_value*)codegen_realloc(s, irep->pool, sizeof(mrb_value)*irep->plen);
+ irep->syms = (mrb_sym*)codegen_realloc(s, irep->syms, sizeof(mrb_sym)*irep->slen);
+ irep->reps = (mrb_irep**)codegen_realloc(s, irep->reps, sizeof(mrb_irep*)*irep->rlen);
+ if (s->filename) {
+ irep->filename = mrb_parser_get_filename(s->parser, s->filename_index);
+ mrb_debug_info_append_file(mrb, irep, s->debug_start_pos, s->pc);
+
+ fname_len = strlen(s->filename);
+ fname = (char*)codegen_malloc(s, fname_len + 1);
+ memcpy(fname, s->filename, fname_len);
+ fname[fname_len] = '\0';
+ irep->filename = fname;
+ irep->own_filename = TRUE;
+ }
+
+ irep->nlocals = s->nlocals;
+ irep->nregs = s->nregs;
+
+ mrb_gc_arena_restore(mrb, s->ai);
+ mrb_pool_close(s->mpool);
+}
+
+static struct loopinfo*
+loop_push(codegen_scope *s, enum looptype t)
+{
+ struct loopinfo *p = (struct loopinfo *)codegen_palloc(s, sizeof(struct loopinfo));
+
+ p->type = t;
+ p->pc1 = p->pc2 = p->pc3 = 0;
+ p->prev = s->loop;
+ p->ensure_level = s->ensure_level;
+ p->acc = cursp();
+ s->loop = p;
+
+ return p;
+}
+
+static void
+loop_break(codegen_scope *s, node *tree)
+{
+ if (!s->loop) {
+ codegen(s, tree, NOVAL);
+ raise_error(s, "unexpected break");
+ }
+ else {
+ struct loopinfo *loop;
+ int n = 0;
+
+ if (tree) {
+ gen_retval(s, tree);
+ }
+
+ loop = s->loop;
+ while (loop) {
+ if (loop->type == LOOP_BEGIN) {
+ n++;
+ loop = loop->prev;
+ }
+ else if (loop->type == LOOP_RESCUE) {
+ loop = loop->prev;
+ }
+ else{
+ break;
+ }
+ }
+ if (!loop) {
+ raise_error(s, "unexpected break");
+ return;
+ }
+ if (n > 0) {
+ genop_peep(s, MKOP_A(OP_POPERR, n), NOVAL);
+ }
+
+ if (loop->type == LOOP_NORMAL) {
+ int tmp;
+
+ if (s->ensure_level > s->loop->ensure_level) {
+ genop_peep(s, MKOP_A(OP_EPOP, s->ensure_level - s->loop->ensure_level), NOVAL);
+ }
+ if (tree) {
+ genop_peep(s, MKOP_AB(OP_MOVE, loop->acc, cursp()), NOVAL);
+ }
+ tmp = genop(s, MKOP_sBx(OP_JMP, loop->pc3));
+ loop->pc3 = tmp;
+ }
+ else {
+ if (!tree) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ }
+ genop(s, MKOP_AB(OP_RETURN, cursp(), OP_R_BREAK));
+ }
+ }
+}
+
+static void
+loop_pop(codegen_scope *s, int val)
+{
+ if (val) {
+ genop(s, MKOP_A(OP_LOADNIL, cursp()));
+ }
+ dispatch_linked(s, s->loop->pc3);
+ s->loop = s->loop->prev;
+ if (val) push();
+}
+
+MRB_API struct RProc*
+mrb_generate_code(mrb_state *mrb, parser_state *p)
+{
+ codegen_scope *scope = scope_new(mrb, 0, 0);
+ struct RProc *proc;
+ struct mrb_jmpbuf *prev_jmp = mrb->jmp;
+
+ if (!scope) {
+ return NULL;
+ }
+ scope->mrb = mrb;
+ scope->parser = p;
+ scope->filename = p->filename;
+ scope->filename_index = p->current_filename_index;
+
+ MRB_TRY(&scope->jmp) {
+ mrb->jmp = &scope->jmp;
+ /* prepare irep */
+ codegen(scope, p->tree, NOVAL);
+ proc = mrb_proc_new(mrb, scope->irep);
+ mrb_irep_decref(mrb, scope->irep);
+ mrb_pool_close(scope->mpool);
+ proc->c = NULL;
+ mrb->jmp = prev_jmp;
+ return proc;
+ }
+ MRB_CATCH(&scope->jmp) {
+ mrb_irep_decref(mrb, scope->irep);
+ mrb_pool_close(scope->mpool);
+ mrb->jmp = prev_jmp;
+ return NULL;
+ }
+ MRB_END_EXC(&scope->jmp);
+}