first commit
This commit is contained in:
+1368
File diff suppressed because it is too large
Load Diff
+238
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
** backtrace.c -
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
*/
|
||||
|
||||
#include <mruby.h>
|
||||
#include <mruby/variable.h>
|
||||
#include <mruby/proc.h>
|
||||
#include <mruby/array.h>
|
||||
#include <mruby/string.h>
|
||||
#include <mruby/class.h>
|
||||
#include <mruby/debug.h>
|
||||
#include <mruby/error.h>
|
||||
#include <mruby/numeric.h>
|
||||
#include <mruby/data.h>
|
||||
|
||||
struct backtrace_location {
|
||||
int32_t lineno;
|
||||
mrb_sym method_id;
|
||||
const char *filename;
|
||||
};
|
||||
|
||||
typedef void (*each_backtrace_func)(mrb_state*, const struct backtrace_location*, void*);
|
||||
|
||||
static const mrb_data_type bt_type = { "Backtrace", mrb_free };
|
||||
|
||||
mrb_value mrb_exc_inspect(mrb_state *mrb, mrb_value exc);
|
||||
mrb_value mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace);
|
||||
|
||||
static void
|
||||
each_backtrace(mrb_state *mrb, ptrdiff_t ciidx, const mrb_code *pc0, each_backtrace_func func, void *data)
|
||||
{
|
||||
ptrdiff_t i;
|
||||
|
||||
if (ciidx >= mrb->c->ciend - mrb->c->cibase)
|
||||
ciidx = 10; /* ciidx is broken... */
|
||||
|
||||
for (i=ciidx; i >= 0; i--) {
|
||||
struct backtrace_location loc;
|
||||
mrb_callinfo *ci;
|
||||
mrb_irep *irep;
|
||||
const mrb_code *pc;
|
||||
|
||||
ci = &mrb->c->cibase[i];
|
||||
|
||||
if (!ci->proc) continue;
|
||||
if (MRB_PROC_CFUNC_P(ci->proc)) continue;
|
||||
|
||||
irep = ci->proc->body.irep;
|
||||
if (!irep) continue;
|
||||
|
||||
if (mrb->c->cibase[i].err) {
|
||||
pc = mrb->c->cibase[i].err;
|
||||
}
|
||||
else if (i+1 <= ciidx) {
|
||||
if (!mrb->c->cibase[i + 1].pc) continue;
|
||||
pc = &mrb->c->cibase[i+1].pc[-1];
|
||||
}
|
||||
else {
|
||||
pc = pc0;
|
||||
}
|
||||
|
||||
loc.lineno = mrb_debug_get_line(mrb, irep, pc - irep->iseq);
|
||||
if (loc.lineno == -1) continue;
|
||||
|
||||
loc.filename = mrb_debug_get_filename(mrb, irep, pc - irep->iseq);
|
||||
if (!loc.filename) {
|
||||
loc.filename = "(unknown)";
|
||||
}
|
||||
|
||||
loc.method_id = ci->mid;
|
||||
func(mrb, &loc, data);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef MRB_DISABLE_STDIO
|
||||
|
||||
static void
|
||||
print_backtrace(mrb_state *mrb, struct RObject *exc, mrb_value backtrace)
|
||||
{
|
||||
mrb_int i;
|
||||
mrb_int n = RARRAY_LEN(backtrace);
|
||||
mrb_value *loc, mesg;
|
||||
FILE *stream = stderr;
|
||||
|
||||
if (n != 0) {
|
||||
fprintf(stream, "trace (most recent call last):\n");
|
||||
for (i=n-1,loc=&RARRAY_PTR(backtrace)[i]; i>0; i--,loc--) {
|
||||
if (mrb_string_p(*loc)) {
|
||||
fprintf(stream, "\t[%d] %.*s\n",
|
||||
(int)i, (int)RSTRING_LEN(*loc), RSTRING_PTR(*loc));
|
||||
}
|
||||
}
|
||||
if (mrb_string_p(*loc)) {
|
||||
fprintf(stream, "%.*s: ", (int)RSTRING_LEN(*loc), RSTRING_PTR(*loc));
|
||||
}
|
||||
}
|
||||
mesg = mrb_exc_inspect(mrb, mrb_obj_value(exc));
|
||||
fprintf(stream, "%.*s\n", (int)RSTRING_LEN(mesg), RSTRING_PTR(mesg));
|
||||
}
|
||||
|
||||
/* mrb_print_backtrace
|
||||
|
||||
function to retrieve backtrace information from the last exception.
|
||||
*/
|
||||
|
||||
MRB_API void
|
||||
mrb_print_backtrace(mrb_state *mrb)
|
||||
{
|
||||
mrb_value backtrace;
|
||||
|
||||
if (!mrb->exc) {
|
||||
return;
|
||||
}
|
||||
|
||||
backtrace = mrb_obj_iv_get(mrb, mrb->exc, mrb_intern_lit(mrb, "backtrace"));
|
||||
if (mrb_nil_p(backtrace)) return;
|
||||
if (!mrb_array_p(backtrace)) backtrace = mrb_unpack_backtrace(mrb, backtrace);
|
||||
print_backtrace(mrb, mrb->exc, backtrace);
|
||||
}
|
||||
#else
|
||||
|
||||
MRB_API void
|
||||
mrb_print_backtrace(mrb_state *mrb)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void
|
||||
count_backtrace_i(mrb_state *mrb,
|
||||
const struct backtrace_location *loc,
|
||||
void *data)
|
||||
{
|
||||
int *lenp = (int*)data;
|
||||
|
||||
(*lenp)++;
|
||||
}
|
||||
|
||||
static void
|
||||
pack_backtrace_i(mrb_state *mrb,
|
||||
const struct backtrace_location *loc,
|
||||
void *data)
|
||||
{
|
||||
struct backtrace_location **pptr = (struct backtrace_location**)data;
|
||||
struct backtrace_location *ptr = *pptr;
|
||||
|
||||
*ptr = *loc;
|
||||
*pptr = ptr+1;
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
packed_backtrace(mrb_state *mrb)
|
||||
{
|
||||
struct RData *backtrace;
|
||||
ptrdiff_t ciidx = mrb->c->ci - mrb->c->cibase;
|
||||
int len = 0;
|
||||
int size;
|
||||
void *ptr;
|
||||
|
||||
each_backtrace(mrb, ciidx, mrb->c->ci->pc, count_backtrace_i, &len);
|
||||
size = len * sizeof(struct backtrace_location);
|
||||
ptr = mrb_malloc(mrb, size);
|
||||
backtrace = mrb_data_object_alloc(mrb, NULL, ptr, &bt_type);
|
||||
backtrace->flags = (uint32_t)len;
|
||||
each_backtrace(mrb, ciidx, mrb->c->ci->pc, pack_backtrace_i, &ptr);
|
||||
return mrb_obj_value(backtrace);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_keep_backtrace(mrb_state *mrb, mrb_value exc)
|
||||
{
|
||||
mrb_sym sym = mrb_intern_lit(mrb, "backtrace");
|
||||
mrb_value backtrace;
|
||||
int ai;
|
||||
|
||||
if (mrb_iv_defined(mrb, exc, sym)) return;
|
||||
ai = mrb_gc_arena_save(mrb);
|
||||
backtrace = packed_backtrace(mrb);
|
||||
mrb_iv_set(mrb, exc, sym, backtrace);
|
||||
mrb_gc_arena_restore(mrb, ai);
|
||||
}
|
||||
|
||||
mrb_value
|
||||
mrb_unpack_backtrace(mrb_state *mrb, mrb_value backtrace)
|
||||
{
|
||||
const struct backtrace_location *bt;
|
||||
mrb_int n, i;
|
||||
int ai;
|
||||
|
||||
if (mrb_nil_p(backtrace)) {
|
||||
empty_backtrace:
|
||||
return mrb_ary_new_capa(mrb, 0);
|
||||
}
|
||||
if (mrb_array_p(backtrace)) return backtrace;
|
||||
bt = (struct backtrace_location*)mrb_data_check_get_ptr(mrb, backtrace, &bt_type);
|
||||
if (bt == NULL) goto empty_backtrace;
|
||||
n = (mrb_int)RDATA(backtrace)->flags;
|
||||
backtrace = mrb_ary_new_capa(mrb, n);
|
||||
ai = mrb_gc_arena_save(mrb);
|
||||
for (i = 0; i < n; i++) {
|
||||
const struct backtrace_location *entry = &bt[i];
|
||||
mrb_value btline;
|
||||
|
||||
btline = mrb_format(mrb, "%s:%d", entry->filename, (int)entry->lineno);
|
||||
if (entry->method_id != 0) {
|
||||
mrb_str_cat_lit(mrb, btline, ":in ");
|
||||
mrb_str_cat_cstr(mrb, btline, mrb_sym_name(mrb, entry->method_id));
|
||||
}
|
||||
mrb_ary_push(mrb, backtrace, btline);
|
||||
mrb_gc_arena_restore(mrb, ai);
|
||||
}
|
||||
|
||||
return backtrace;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_exc_backtrace(mrb_state *mrb, mrb_value exc)
|
||||
{
|
||||
mrb_sym attr_name;
|
||||
mrb_value backtrace;
|
||||
|
||||
attr_name = mrb_intern_lit(mrb, "backtrace");
|
||||
backtrace = mrb_iv_get(mrb, exc, attr_name);
|
||||
if (mrb_nil_p(backtrace) || mrb_array_p(backtrace)) {
|
||||
return backtrace;
|
||||
}
|
||||
backtrace = mrb_unpack_backtrace(mrb, backtrace);
|
||||
mrb_iv_set(mrb, exc, attr_name, backtrace);
|
||||
return backtrace;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_get_backtrace(mrb_state *mrb)
|
||||
{
|
||||
return mrb_unpack_backtrace(mrb, packed_backtrace(mrb));
|
||||
}
|
||||
+2333
File diff suppressed because it is too large
Load Diff
+553
@@ -0,0 +1,553 @@
|
||||
#include <mruby.h>
|
||||
#include <mruby/irep.h>
|
||||
#include <mruby/debug.h>
|
||||
#include <mruby/opcode.h>
|
||||
#include <mruby/string.h>
|
||||
#include <mruby/proc.h>
|
||||
|
||||
#ifndef MRB_DISABLE_STDIO
|
||||
static void
|
||||
print_r(mrb_state *mrb, mrb_irep *irep, size_t n)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (n == 0) return;
|
||||
|
||||
for (i=0; i+1<irep->nlocals; i++) {
|
||||
if (irep->lv[i].r == n) {
|
||||
mrb_sym sym = irep->lv[i].name;
|
||||
printf(" R%d:%s", (int)n, mrb_sym_dump(mrb, sym));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_lv_a(mrb_state *mrb, mrb_irep *irep, uint16_t a)
|
||||
{
|
||||
if (!irep->lv || a >= irep->nlocals || a == 0) {
|
||||
printf("\n");
|
||||
return;
|
||||
}
|
||||
printf("\t;");
|
||||
print_r(mrb, irep, a);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
print_lv_ab(mrb_state *mrb, mrb_irep *irep, uint16_t a, uint16_t b)
|
||||
{
|
||||
if (!irep->lv || (a >= irep->nlocals && b >= irep->nlocals) || a+b == 0) {
|
||||
printf("\n");
|
||||
return;
|
||||
}
|
||||
printf("\t;");
|
||||
if (a > 0) print_r(mrb, irep, a);
|
||||
if (b > 0) print_r(mrb, irep, b);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
print_header(mrb_state *mrb, mrb_irep *irep, ptrdiff_t i)
|
||||
{
|
||||
int32_t line;
|
||||
|
||||
line = mrb_debug_get_line(mrb, irep, i);
|
||||
if (line < 0) {
|
||||
printf(" ");
|
||||
}
|
||||
else {
|
||||
printf("%5d ", line);
|
||||
}
|
||||
|
||||
printf("%03d ", (int)i);
|
||||
}
|
||||
|
||||
#define CASE(insn,ops) case insn: FETCH_ ## ops (); L_ ## insn
|
||||
|
||||
static void
|
||||
codedump(mrb_state *mrb, mrb_irep *irep)
|
||||
{
|
||||
int ai;
|
||||
const mrb_code *pc, *pcend;
|
||||
mrb_code ins;
|
||||
const char *file = NULL, *next_file;
|
||||
|
||||
if (!irep) return;
|
||||
printf("irep %p nregs=%d nlocals=%d pools=%d syms=%d reps=%d iseq=%d\n", (void*)irep,
|
||||
irep->nregs, irep->nlocals, (int)irep->plen, (int)irep->slen, (int)irep->rlen, (int)irep->ilen);
|
||||
|
||||
if (irep->lv) {
|
||||
int i;
|
||||
|
||||
printf("local variable names:\n");
|
||||
for (i = 1; i < irep->nlocals; ++i) {
|
||||
char const *s = mrb_sym_dump(mrb, irep->lv[i - 1].name);
|
||||
int n = irep->lv[i - 1].r ? irep->lv[i - 1].r : i;
|
||||
printf(" R%d:%s\n", n, s ? s : "");
|
||||
}
|
||||
}
|
||||
|
||||
pc = irep->iseq;
|
||||
pcend = pc + irep->ilen;
|
||||
while (pc < pcend) {
|
||||
ptrdiff_t i;
|
||||
uint32_t a;
|
||||
uint16_t b;
|
||||
uint8_t c;
|
||||
|
||||
ai = mrb_gc_arena_save(mrb);
|
||||
|
||||
i = pc - irep->iseq;
|
||||
next_file = mrb_debug_get_filename(mrb, irep, i);
|
||||
if (next_file && file != next_file) {
|
||||
printf("file: %s\n", next_file);
|
||||
file = next_file;
|
||||
}
|
||||
print_header(mrb, irep, i);
|
||||
ins = READ_B();
|
||||
switch (ins) {
|
||||
CASE(OP_NOP, Z):
|
||||
printf("OP_NOP\n");
|
||||
break;
|
||||
CASE(OP_MOVE, BB):
|
||||
printf("OP_MOVE\tR%d\tR%d\t", a, b);
|
||||
print_lv_ab(mrb, irep, a, b);
|
||||
break;
|
||||
CASE(OP_LOADL, BB):
|
||||
{
|
||||
mrb_value v = irep->pool[b];
|
||||
mrb_value s = mrb_inspect(mrb, v);
|
||||
printf("OP_LOADL\tR%d\tL(%d)\t; %s", a, b, RSTRING_PTR(s));
|
||||
}
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_LOADI, BB):
|
||||
printf("OP_LOADI\tR%d\t%d\t", a, b);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_LOADINEG, BB):
|
||||
printf("OP_LOADI\tR%d\t-%d\t", a, b);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_LOADI16, BS):
|
||||
printf("OP_LOADI16\tR%d\t%d\t", a, (int)(int16_t)b);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_LOADI__1, B):
|
||||
printf("OP_LOADI__1\tR%d\t\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_LOADI_0, B): goto L_LOADI;
|
||||
CASE(OP_LOADI_1, B): goto L_LOADI;
|
||||
CASE(OP_LOADI_2, B): goto L_LOADI;
|
||||
CASE(OP_LOADI_3, B): goto L_LOADI;
|
||||
CASE(OP_LOADI_4, B): goto L_LOADI;
|
||||
CASE(OP_LOADI_5, B): goto L_LOADI;
|
||||
CASE(OP_LOADI_6, B): goto L_LOADI;
|
||||
CASE(OP_LOADI_7, B):
|
||||
L_LOADI:
|
||||
printf("OP_LOADI_%d\tR%d\t\t", ins-(int)OP_LOADI_0, a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_LOADSYM, BB):
|
||||
printf("OP_LOADSYM\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b]));
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_LOADNIL, B):
|
||||
printf("OP_LOADNIL\tR%d\t\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_LOADSELF, B):
|
||||
printf("OP_LOADSELF\tR%d\t\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_LOADT, B):
|
||||
printf("OP_LOADT\tR%d\t\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_LOADF, B):
|
||||
printf("OP_LOADF\tR%d\t\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_GETGV, BB):
|
||||
printf("OP_GETGV\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b]));
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_SETGV, BB):
|
||||
printf("OP_SETGV\t:%s\tR%d", mrb_sym_dump(mrb, irep->syms[b]), a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_GETSV, BB):
|
||||
printf("OP_GETSV\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b]));
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_SETSV, BB):
|
||||
printf("OP_SETSV\t:%s\tR%d", mrb_sym_dump(mrb, irep->syms[b]), a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_GETCONST, BB):
|
||||
printf("OP_GETCONST\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b]));
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_SETCONST, BB):
|
||||
printf("OP_SETCONST\t:%s\tR%d", mrb_sym_dump(mrb, irep->syms[b]), a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_GETMCNST, BB):
|
||||
printf("OP_GETMCNST\tR%d\tR%d::%s", a, a, mrb_sym_dump(mrb, irep->syms[b]));
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_SETMCNST, BB):
|
||||
printf("OP_SETMCNST\tR%d::%s\tR%d", a+1, mrb_sym_dump(mrb, irep->syms[b]), a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_GETIV, BB):
|
||||
printf("OP_GETIV\tR%d\t%s", a, mrb_sym_dump(mrb, irep->syms[b]));
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_SETIV, BB):
|
||||
printf("OP_SETIV\t%s\tR%d", mrb_sym_dump(mrb, irep->syms[b]), a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_GETUPVAR, BBB):
|
||||
printf("OP_GETUPVAR\tR%d\t%d\t%d", a, b, c);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_SETUPVAR, BBB):
|
||||
printf("OP_SETUPVAR\tR%d\t%d\t%d", a, b, c);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_GETCV, BB):
|
||||
printf("OP_GETCV\tR%d\t%s", a, mrb_sym_dump(mrb, irep->syms[b]));
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_SETCV, BB):
|
||||
printf("OP_SETCV\t%s\tR%d", mrb_sym_dump(mrb, irep->syms[b]), a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_JMP, S):
|
||||
printf("OP_JMP\t\t%03d\n", a);
|
||||
break;
|
||||
CASE(OP_JMPIF, BS):
|
||||
printf("OP_JMPIF\tR%d\t%03d\t", a, b);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_JMPNOT, BS):
|
||||
printf("OP_JMPNOT\tR%d\t%03d\t", a, b);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_JMPNIL, BS):
|
||||
printf("OP_JMPNIL\tR%d\t%03d\t", a, b);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_SENDV, BB):
|
||||
printf("OP_SENDV\tR%d\t:%s\n", a, mrb_sym_dump(mrb, irep->syms[b]));
|
||||
break;
|
||||
CASE(OP_SENDVB, BB):
|
||||
printf("OP_SENDVB\tR%d\t:%s\n", a, mrb_sym_dump(mrb, irep->syms[b]));
|
||||
break;
|
||||
CASE(OP_SEND, BBB):
|
||||
printf("OP_SEND\tR%d\t:%s\t%d\n", a, mrb_sym_dump(mrb, irep->syms[b]), c);
|
||||
break;
|
||||
CASE(OP_SENDB, BBB):
|
||||
printf("OP_SENDB\tR%d\t:%s\t%d\n", a, mrb_sym_dump(mrb, irep->syms[b]), c);
|
||||
break;
|
||||
CASE(OP_CALL, Z):
|
||||
printf("OP_CALL\n");
|
||||
break;
|
||||
CASE(OP_SUPER, BB):
|
||||
printf("OP_SUPER\tR%d\t%d\n", a, b);
|
||||
break;
|
||||
CASE(OP_ARGARY, BS):
|
||||
printf("OP_ARGARY\tR%d\t%d:%d:%d:%d (%d)", a,
|
||||
(b>>11)&0x3f,
|
||||
(b>>10)&0x1,
|
||||
(b>>5)&0x1f,
|
||||
(b>>4)&0x1,
|
||||
(b>>0)&0xf);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_ENTER, W):
|
||||
printf("OP_ENTER\t%d:%d:%d:%d:%d:%d:%d\n",
|
||||
MRB_ASPEC_REQ(a),
|
||||
MRB_ASPEC_OPT(a),
|
||||
MRB_ASPEC_REST(a),
|
||||
MRB_ASPEC_POST(a),
|
||||
MRB_ASPEC_KEY(a),
|
||||
MRB_ASPEC_KDICT(a),
|
||||
MRB_ASPEC_BLOCK(a));
|
||||
break;
|
||||
CASE(OP_KEY_P, BB):
|
||||
printf("OP_KEY_P\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b]));
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_KEYEND, Z):
|
||||
printf("OP_KEYEND\n");
|
||||
break;
|
||||
CASE(OP_KARG, BB):
|
||||
printf("OP_KARG\tR%d\t:%s\t", a, mrb_sym_dump(mrb, irep->syms[b]));
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_RETURN, B):
|
||||
printf("OP_RETURN\tR%d\t\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_RETURN_BLK, B):
|
||||
printf("OP_RETURN_BLK\tR%d\t\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_BREAK, B):
|
||||
printf("OP_BREAK\tR%d\t\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_BLKPUSH, BS):
|
||||
printf("OP_BLKPUSH\tR%d\t%d:%d:%d:%d (%d)", a,
|
||||
(b>>11)&0x3f,
|
||||
(b>>10)&0x1,
|
||||
(b>>5)&0x1f,
|
||||
(b>>4)&0x1,
|
||||
(b>>0)&0xf);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_LAMBDA, BB):
|
||||
printf("OP_LAMBDA\tR%d\tI(%d:%p)\n", a, b, irep->reps[b]);
|
||||
break;
|
||||
CASE(OP_BLOCK, BB):
|
||||
printf("OP_BLOCK\tR%d\tI(%d:%p)\n", a, b, irep->reps[b]);
|
||||
break;
|
||||
CASE(OP_METHOD, BB):
|
||||
printf("OP_METHOD\tR%d\tI(%d:%p)\n", a, b, irep->reps[b]);
|
||||
break;
|
||||
CASE(OP_RANGE_INC, B):
|
||||
printf("OP_RANGE_INC\tR%d\n", a);
|
||||
break;
|
||||
CASE(OP_RANGE_EXC, B):
|
||||
printf("OP_RANGE_EXC\tR%d\n", a);
|
||||
break;
|
||||
CASE(OP_DEF, BB):
|
||||
printf("OP_DEF\tR%d\t:%s\n", a, mrb_sym_dump(mrb, irep->syms[b]));
|
||||
break;
|
||||
CASE(OP_UNDEF, B):
|
||||
printf("OP_UNDEF\t:%s\n", mrb_sym_dump(mrb, irep->syms[a]));
|
||||
break;
|
||||
CASE(OP_ALIAS, BB):
|
||||
printf("OP_ALIAS\t:%s\t%s\n", mrb_sym_dump(mrb, irep->syms[a]), mrb_sym_dump(mrb, irep->syms[b]));
|
||||
break;
|
||||
CASE(OP_ADD, B):
|
||||
printf("OP_ADD\tR%d\t\n", a);
|
||||
break;
|
||||
CASE(OP_ADDI, BB):
|
||||
printf("OP_ADDI\tR%d\t%d\n", a, b);
|
||||
break;
|
||||
CASE(OP_SUB, B):
|
||||
printf("OP_SUB\tR%d\t\n", a);
|
||||
break;
|
||||
CASE(OP_SUBI, BB):
|
||||
printf("OP_SUBI\tR%d\t%d\n", a, b);
|
||||
break;
|
||||
CASE(OP_MUL, B):
|
||||
printf("OP_MUL\tR%d\t\n", a);
|
||||
break;
|
||||
CASE(OP_DIV, B):
|
||||
printf("OP_DIV\tR%d\t\n", a);
|
||||
break;
|
||||
CASE(OP_LT, B):
|
||||
printf("OP_LT\t\tR%d\t\n", a);
|
||||
break;
|
||||
CASE(OP_LE, B):
|
||||
printf("OP_LE\t\tR%d\t\n", a);
|
||||
break;
|
||||
CASE(OP_GT, B):
|
||||
printf("OP_GT\t\tR%d\t\n", a);
|
||||
break;
|
||||
CASE(OP_GE, B):
|
||||
printf("OP_GE\t\tR%d\t\n", a);
|
||||
break;
|
||||
CASE(OP_EQ, B):
|
||||
printf("OP_EQ\t\tR%d\t\n", a);
|
||||
break;
|
||||
CASE(OP_ARRAY, BB):
|
||||
printf("OP_ARRAY\tR%d\t%d\t", a, b);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_ARRAY2, BBB):
|
||||
printf("OP_ARRAY\tR%d\tR%d\t%d\t", a, b, c);
|
||||
print_lv_ab(mrb, irep, a, b);
|
||||
break;
|
||||
CASE(OP_ARYCAT, B):
|
||||
printf("OP_ARYCAT\tR%d\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_ARYPUSH, B):
|
||||
printf("OP_ARYPUSH\tR%d\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_ARYDUP, B):
|
||||
printf("OP_ARYDUP\tR%d\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_AREF, BBB):
|
||||
printf("OP_AREF\tR%d\tR%d\t%d", a, b, c);
|
||||
print_lv_ab(mrb, irep, a, b);
|
||||
break;
|
||||
CASE(OP_ASET, BBB):
|
||||
printf("OP_ASET\tR%d\tR%d\t%d", a, b, c);
|
||||
print_lv_ab(mrb, irep, a, b);
|
||||
break;
|
||||
CASE(OP_APOST, BBB):
|
||||
printf("OP_APOST\tR%d\t%d\t%d", a, b, c);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_INTERN, B):
|
||||
printf("OP_INTERN\tR%d", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_STRING, BB):
|
||||
{
|
||||
mrb_value v = irep->pool[b];
|
||||
mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v)));
|
||||
printf("OP_STRING\tR%d\tL(%d)\t; %s", a, b, RSTRING_PTR(s));
|
||||
}
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_STRCAT, B):
|
||||
printf("OP_STRCAT\tR%d\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_HASH, BB):
|
||||
printf("OP_HASH\tR%d\t%d\t", a, b);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_HASHADD, BB):
|
||||
printf("OP_HASHADD\tR%d\t%d\t", a, b);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_HASHCAT, B):
|
||||
printf("OP_HASHCAT\tR%d\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
|
||||
CASE(OP_OCLASS, B):
|
||||
printf("OP_OCLASS\tR%d\t\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_CLASS, BB):
|
||||
printf("OP_CLASS\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b]));
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_MODULE, BB):
|
||||
printf("OP_MODULE\tR%d\t:%s", a, mrb_sym_dump(mrb, irep->syms[b]));
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_EXEC, BB):
|
||||
printf("OP_EXEC\tR%d\tI(%d:%p)", a, b, irep->reps[b]);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_SCLASS, B):
|
||||
printf("OP_SCLASS\tR%d\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_TCLASS, B):
|
||||
printf("OP_TCLASS\tR%d\t\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_ERR, B):
|
||||
{
|
||||
mrb_value v = irep->pool[a];
|
||||
mrb_value s = mrb_str_dump(mrb, mrb_str_new(mrb, RSTRING_PTR(v), RSTRING_LEN(v)));
|
||||
printf("OP_ERR\t%s\n", RSTRING_PTR(s));
|
||||
}
|
||||
break;
|
||||
CASE(OP_EPUSH, B):
|
||||
printf("OP_EPUSH\t\t:I(%d:%p)\n", a, irep->reps[a]);
|
||||
break;
|
||||
CASE(OP_ONERR, S):
|
||||
printf("OP_ONERR\t%03d\n", a);
|
||||
break;
|
||||
CASE(OP_EXCEPT, B):
|
||||
printf("OP_EXCEPT\tR%d\t\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_RESCUE, BB):
|
||||
printf("OP_RESCUE\tR%d\tR%d", a, b);
|
||||
print_lv_ab(mrb, irep, a, b);
|
||||
break;
|
||||
CASE(OP_RAISE, B):
|
||||
printf("OP_RAISE\tR%d\t\t", a);
|
||||
print_lv_a(mrb, irep, a);
|
||||
break;
|
||||
CASE(OP_POPERR, B):
|
||||
printf("OP_POPERR\t%d\t\t\n", a);
|
||||
break;
|
||||
CASE(OP_EPOP, B):
|
||||
printf("OP_EPOP\t%d\n", a);
|
||||
break;
|
||||
|
||||
CASE(OP_DEBUG, BBB):
|
||||
printf("OP_DEBUG\t%d\t%d\t%d\n", a, b, c);
|
||||
break;
|
||||
|
||||
CASE(OP_STOP, Z):
|
||||
printf("OP_STOP\n");
|
||||
break;
|
||||
|
||||
CASE(OP_EXT1, Z):
|
||||
ins = READ_B();
|
||||
printf("OP_EXT1\n");
|
||||
print_header(mrb, irep, pc-irep->iseq-2);
|
||||
switch (ins) {
|
||||
#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _1 (); goto L_OP_ ## i;
|
||||
#include "mruby/ops.h"
|
||||
#undef OPCODE
|
||||
}
|
||||
break;
|
||||
CASE(OP_EXT2, Z):
|
||||
ins = READ_B();
|
||||
printf("OP_EXT2\n");
|
||||
print_header(mrb, irep, pc-irep->iseq-2);
|
||||
switch (ins) {
|
||||
#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _2 (); goto L_OP_ ## i;
|
||||
#include "mruby/ops.h"
|
||||
#undef OPCODE
|
||||
}
|
||||
break;
|
||||
CASE(OP_EXT3, Z):
|
||||
ins = READ_B();
|
||||
printf("OP_EXT3\n");
|
||||
print_header(mrb, irep, pc-irep->iseq-2);
|
||||
switch (ins) {
|
||||
#define OPCODE(i,x) case OP_ ## i: FETCH_ ## x ## _3 (); goto L_OP_ ## i;
|
||||
#include "mruby/ops.h"
|
||||
#undef OPCODE
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("OP_unknown (0x%x)\n", ins);
|
||||
break;
|
||||
}
|
||||
mrb_gc_arena_restore(mrb, ai);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void
|
||||
codedump_recur(mrb_state *mrb, mrb_irep *irep)
|
||||
{
|
||||
int i;
|
||||
|
||||
codedump(mrb, irep);
|
||||
for (i=0; i<irep->rlen; i++) {
|
||||
codedump_recur(mrb, irep->reps[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
mrb_codedump_all(mrb_state *mrb, struct RProc *proc)
|
||||
{
|
||||
#ifndef MRB_DISABLE_STDIO
|
||||
codedump_recur(mrb, proc->body.irep);
|
||||
#endif
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
** compar.c - Comparable module
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
*/
|
||||
|
||||
#include <mruby.h>
|
||||
|
||||
void
|
||||
mrb_init_comparable(mrb_state *mrb)
|
||||
{
|
||||
mrb_define_module(mrb, "Comparable"); /* 15.3.3 */
|
||||
}
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
** crc.c - calculate CRC
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* Calculate CRC (CRC-16-CCITT)
|
||||
**
|
||||
** 0000_0000_0000_0000_0000_0000_0000_0000
|
||||
** ^|------- CRC -------|- work --|
|
||||
** carry
|
||||
*/
|
||||
#define CRC_16_CCITT 0x11021ul /* x^16+x^12+x^5+1 */
|
||||
#define CRC_XOR_PATTERN (CRC_16_CCITT << 8)
|
||||
#define CRC_CARRY_BIT (0x01000000)
|
||||
|
||||
uint16_t
|
||||
calc_crc_16_ccitt(const uint8_t *src, size_t nbytes, uint16_t crc)
|
||||
{
|
||||
size_t ibyte;
|
||||
uint32_t ibit;
|
||||
uint32_t crcwk = crc << 8;
|
||||
|
||||
for (ibyte = 0; ibyte < nbytes; ibyte++) {
|
||||
crcwk |= *src++;
|
||||
for (ibit = 0; ibit < CHAR_BIT; ibit++) {
|
||||
crcwk <<= 1;
|
||||
if (crcwk & CRC_CARRY_BIT) {
|
||||
crcwk ^= CRC_XOR_PATTERN;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (uint16_t)(crcwk >> 8);
|
||||
}
|
||||
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
#include <string.h>
|
||||
#include <mruby.h>
|
||||
#include <mruby/irep.h>
|
||||
#include <mruby/debug.h>
|
||||
|
||||
static mrb_irep_debug_info_file*
|
||||
get_file(mrb_irep_debug_info *info, uint32_t pc)
|
||||
{
|
||||
mrb_irep_debug_info_file **ret;
|
||||
int32_t count;
|
||||
|
||||
if (pc >= info->pc_count) { return NULL; }
|
||||
/* get upper bound */
|
||||
ret = info->files;
|
||||
count = info->flen;
|
||||
while (count > 0) {
|
||||
int32_t step = count / 2;
|
||||
mrb_irep_debug_info_file **it = ret + step;
|
||||
if (!(pc < (*it)->start_pos)) {
|
||||
ret = it + 1;
|
||||
count -= step + 1;
|
||||
}
|
||||
else { count = step; }
|
||||
}
|
||||
|
||||
--ret;
|
||||
|
||||
/* check returning file exists inside debug info */
|
||||
mrb_assert(info->files <= ret && ret < (info->files + info->flen));
|
||||
/* check pc is within the range of returning file */
|
||||
mrb_assert((*ret)->start_pos <= pc &&
|
||||
pc < (((ret + 1 - info->files) < info->flen)
|
||||
? (*(ret+1))->start_pos : info->pc_count));
|
||||
|
||||
return *ret;
|
||||
}
|
||||
|
||||
static mrb_debug_line_type
|
||||
select_line_type(const uint16_t *lines, size_t lines_len)
|
||||
{
|
||||
size_t line_count = 0;
|
||||
int prev_line = -1;
|
||||
size_t i;
|
||||
for (i = 0; i < lines_len; ++i) {
|
||||
if (lines[i] != prev_line) {
|
||||
++line_count;
|
||||
}
|
||||
}
|
||||
return (sizeof(uint16_t) * lines_len) <= (sizeof(mrb_irep_debug_info_line) * line_count)
|
||||
? mrb_debug_line_ary : mrb_debug_line_flat_map;
|
||||
}
|
||||
|
||||
MRB_API char const*
|
||||
mrb_debug_get_filename(mrb_state *mrb, mrb_irep *irep, ptrdiff_t pc)
|
||||
{
|
||||
if (irep && pc >= 0 && pc < irep->ilen) {
|
||||
mrb_irep_debug_info_file* f = NULL;
|
||||
if (!irep->debug_info) return NULL;
|
||||
else if ((f = get_file(irep->debug_info, (uint32_t)pc))) {
|
||||
return mrb_sym_name_len(mrb, f->filename_sym, NULL);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MRB_API int32_t
|
||||
mrb_debug_get_line(mrb_state *mrb, mrb_irep *irep, ptrdiff_t pc)
|
||||
{
|
||||
if (irep && pc >= 0 && pc < irep->ilen) {
|
||||
mrb_irep_debug_info_file* f = NULL;
|
||||
if (!irep->debug_info) {
|
||||
return -1;
|
||||
}
|
||||
else if ((f = get_file(irep->debug_info, (uint32_t)pc))) {
|
||||
switch (f->line_type) {
|
||||
case mrb_debug_line_ary:
|
||||
mrb_assert(f->start_pos <= pc && pc < (f->start_pos + f->line_entry_count));
|
||||
return f->lines.ary[pc - f->start_pos];
|
||||
|
||||
case mrb_debug_line_flat_map: {
|
||||
/* get upper bound */
|
||||
mrb_irep_debug_info_line *ret = f->lines.flat_map;
|
||||
uint32_t count = f->line_entry_count;
|
||||
while (count > 0) {
|
||||
int32_t step = count / 2;
|
||||
mrb_irep_debug_info_line *it = ret + step;
|
||||
if (!(pc < it->start_pos)) {
|
||||
ret = it + 1;
|
||||
count -= step + 1;
|
||||
}
|
||||
else { count = step; }
|
||||
}
|
||||
|
||||
--ret;
|
||||
|
||||
/* check line entry pointer range */
|
||||
mrb_assert(f->lines.flat_map <= ret && ret < (f->lines.flat_map + f->line_entry_count));
|
||||
/* check pc range */
|
||||
mrb_assert(ret->start_pos <= pc &&
|
||||
pc < (((uint32_t)(ret + 1 - f->lines.flat_map) < f->line_entry_count)
|
||||
? (ret+1)->start_pos : irep->debug_info->pc_count));
|
||||
|
||||
return ret->line;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
MRB_API mrb_irep_debug_info*
|
||||
mrb_debug_info_alloc(mrb_state *mrb, mrb_irep *irep)
|
||||
{
|
||||
static const mrb_irep_debug_info initial = { 0, 0, NULL };
|
||||
mrb_irep_debug_info *ret;
|
||||
|
||||
mrb_assert(!irep->debug_info);
|
||||
ret = (mrb_irep_debug_info *)mrb_malloc(mrb, sizeof(*ret));
|
||||
*ret = initial;
|
||||
irep->debug_info = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
MRB_API mrb_irep_debug_info_file*
|
||||
mrb_debug_info_append_file(mrb_state *mrb, mrb_irep_debug_info *d,
|
||||
const char *filename, uint16_t *lines,
|
||||
uint32_t start_pos, uint32_t end_pos)
|
||||
{
|
||||
mrb_irep_debug_info_file *f;
|
||||
uint32_t file_pc_count;
|
||||
size_t fn_len;
|
||||
uint32_t i;
|
||||
|
||||
if (!d) return NULL;
|
||||
if (start_pos == end_pos) return NULL;
|
||||
|
||||
mrb_assert(filename);
|
||||
mrb_assert(lines);
|
||||
|
||||
if (d->flen > 0) {
|
||||
const char *fn = mrb_sym_name_len(mrb, d->files[d->flen - 1]->filename_sym, NULL);
|
||||
if (strcmp(filename, fn) == 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f = (mrb_irep_debug_info_file*)mrb_malloc(mrb, sizeof(*f));
|
||||
d->files = (mrb_irep_debug_info_file**)(
|
||||
d->files
|
||||
? mrb_realloc(mrb, d->files, sizeof(mrb_irep_debug_info_file*) * (d->flen + 1))
|
||||
: mrb_malloc(mrb, sizeof(mrb_irep_debug_info_file*)));
|
||||
d->files[d->flen++] = f;
|
||||
|
||||
file_pc_count = end_pos - start_pos;
|
||||
|
||||
f->start_pos = start_pos;
|
||||
d->pc_count = end_pos;
|
||||
|
||||
fn_len = strlen(filename);
|
||||
f->filename_sym = mrb_intern(mrb, filename, fn_len);
|
||||
|
||||
f->line_type = select_line_type(lines + start_pos, end_pos - start_pos);
|
||||
f->lines.ptr = NULL;
|
||||
|
||||
switch (f->line_type) {
|
||||
case mrb_debug_line_ary:
|
||||
f->line_entry_count = file_pc_count;
|
||||
f->lines.ary = (uint16_t*)mrb_malloc(mrb, sizeof(uint16_t) * file_pc_count);
|
||||
for (i = 0; i < file_pc_count; ++i) {
|
||||
f->lines.ary[i] = lines[start_pos + i];
|
||||
}
|
||||
break;
|
||||
|
||||
case mrb_debug_line_flat_map: {
|
||||
uint16_t prev_line = 0;
|
||||
mrb_irep_debug_info_line m;
|
||||
f->lines.flat_map = (mrb_irep_debug_info_line*)mrb_malloc(mrb, sizeof(mrb_irep_debug_info_line) * 1);
|
||||
f->line_entry_count = 0;
|
||||
for (i = 0; i < file_pc_count; ++i) {
|
||||
if (lines[start_pos + i] == prev_line) { continue; }
|
||||
|
||||
f->lines.flat_map = (mrb_irep_debug_info_line*)mrb_realloc(
|
||||
mrb, f->lines.flat_map,
|
||||
sizeof(mrb_irep_debug_info_line) * (f->line_entry_count + 1));
|
||||
m.start_pos = start_pos + i;
|
||||
m.line = lines[start_pos + i];
|
||||
f->lines.flat_map[f->line_entry_count] = m;
|
||||
|
||||
/* update */
|
||||
++f->line_entry_count;
|
||||
prev_line = lines[start_pos + i];
|
||||
}
|
||||
} break;
|
||||
|
||||
default: mrb_assert(0); break;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
MRB_API void
|
||||
mrb_debug_info_free(mrb_state *mrb, mrb_irep_debug_info *d)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if (!d) { return; }
|
||||
|
||||
if (d->files) {
|
||||
for (i = 0; i < d->flen; ++i) {
|
||||
if (d->files[i]) {
|
||||
mrb_free(mrb, d->files[i]->lines.ptr);
|
||||
mrb_free(mrb, d->files[i]);
|
||||
}
|
||||
}
|
||||
mrb_free(mrb, d->files);
|
||||
}
|
||||
mrb_free(mrb, d);
|
||||
}
|
||||
Vendored
+921
@@ -0,0 +1,921 @@
|
||||
/*
|
||||
** dump.c - mruby binary dumper (mrbc binary format)
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <mruby/dump.h>
|
||||
#include <mruby/string.h>
|
||||
#include <mruby/irep.h>
|
||||
#include <mruby/numeric.h>
|
||||
#include <mruby/debug.h>
|
||||
|
||||
#ifndef MRB_WITHOUT_FLOAT
|
||||
#ifdef MRB_USE_FLOAT
|
||||
#define MRB_FLOAT_FMT "%.9g"
|
||||
#else
|
||||
#define MRB_FLOAT_FMT "%.17g"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static size_t get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep);
|
||||
|
||||
#if UINT32_MAX > SIZE_MAX
|
||||
# error This code cannot be built on your environment.
|
||||
#endif
|
||||
|
||||
static size_t
|
||||
write_padding(uint8_t *buf)
|
||||
{
|
||||
const size_t align = MRB_DUMP_ALIGNMENT;
|
||||
size_t pad_len = -(intptr_t)buf & (align-1);
|
||||
if (pad_len > 0) {
|
||||
memset(buf, 0, pad_len);
|
||||
}
|
||||
return pad_len;
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_irep_header_size(mrb_state *mrb)
|
||||
{
|
||||
size_t size = 0;
|
||||
|
||||
size += sizeof(uint32_t) * 1;
|
||||
size += sizeof(uint16_t) * 3;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ptrdiff_t
|
||||
write_irep_header(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
|
||||
{
|
||||
uint8_t *cur = buf;
|
||||
|
||||
cur += uint32_to_bin((uint32_t)get_irep_record_size_1(mrb, irep), cur); /* record size */
|
||||
cur += uint16_to_bin((uint16_t)irep->nlocals, cur); /* number of local variable */
|
||||
cur += uint16_to_bin((uint16_t)irep->nregs, cur); /* number of register variable */
|
||||
cur += uint16_to_bin((uint16_t)irep->rlen, cur); /* number of child irep */
|
||||
|
||||
return cur - buf;
|
||||
}
|
||||
|
||||
|
||||
static size_t
|
||||
get_iseq_block_size(mrb_state *mrb, mrb_irep *irep)
|
||||
{
|
||||
size_t size = 0;
|
||||
|
||||
size += sizeof(uint32_t); /* ilen */
|
||||
size += sizeof(uint32_t); /* max padding */
|
||||
size += sizeof(uint32_t) * irep->ilen; /* iseq(n) */
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ptrdiff_t
|
||||
write_iseq_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf, uint8_t flags)
|
||||
{
|
||||
uint8_t *cur = buf;
|
||||
|
||||
cur += uint32_to_bin(irep->ilen, cur); /* number of opcode */
|
||||
cur += write_padding(cur);
|
||||
memcpy(cur, irep->iseq, irep->ilen * sizeof(mrb_code));
|
||||
cur += irep->ilen * sizeof(mrb_code);
|
||||
|
||||
return cur - buf;
|
||||
}
|
||||
|
||||
#ifndef MRB_WITHOUT_FLOAT
|
||||
static mrb_value
|
||||
float_to_str(mrb_state *mrb, mrb_value flt)
|
||||
{
|
||||
mrb_float f = mrb_float(flt);
|
||||
|
||||
if (isinf(f)) {
|
||||
return f < 0 ? mrb_str_new_lit(mrb, "I") : mrb_str_new_lit(mrb, "i");
|
||||
}
|
||||
return mrb_float_to_str(mrb, flt, MRB_FLOAT_FMT);
|
||||
}
|
||||
#endif
|
||||
|
||||
static size_t
|
||||
get_pool_block_size(mrb_state *mrb, mrb_irep *irep)
|
||||
{
|
||||
int pool_no;
|
||||
size_t size = 0;
|
||||
mrb_value str;
|
||||
|
||||
size += sizeof(uint32_t); /* plen */
|
||||
size += irep->plen * (sizeof(uint8_t) + sizeof(uint16_t)); /* len(n) */
|
||||
|
||||
for (pool_no = 0; pool_no < irep->plen; pool_no++) {
|
||||
int ai = mrb_gc_arena_save(mrb);
|
||||
|
||||
switch (mrb_type(irep->pool[pool_no])) {
|
||||
case MRB_TT_FIXNUM:
|
||||
str = mrb_fixnum_to_str(mrb, irep->pool[pool_no], 10);
|
||||
{
|
||||
mrb_int len = RSTRING_LEN(str);
|
||||
mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
|
||||
size += (size_t)len;
|
||||
}
|
||||
break;
|
||||
|
||||
#ifndef MRB_WITHOUT_FLOAT
|
||||
case MRB_TT_FLOAT:
|
||||
str = float_to_str(mrb, irep->pool[pool_no]);
|
||||
{
|
||||
mrb_int len = RSTRING_LEN(str);
|
||||
mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
|
||||
size += (size_t)len;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case MRB_TT_STRING:
|
||||
{
|
||||
mrb_int len = RSTRING_LEN(irep->pool[pool_no]);
|
||||
mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
|
||||
size += (size_t)len;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mrb_gc_arena_restore(mrb, ai);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ptrdiff_t
|
||||
write_pool_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
|
||||
{
|
||||
int pool_no;
|
||||
uint8_t *cur = buf;
|
||||
uint16_t len;
|
||||
mrb_value str;
|
||||
const char *char_ptr;
|
||||
|
||||
cur += uint32_to_bin(irep->plen, cur); /* number of pool */
|
||||
|
||||
for (pool_no = 0; pool_no < irep->plen; pool_no++) {
|
||||
int ai = mrb_gc_arena_save(mrb);
|
||||
|
||||
switch (mrb_type(irep->pool[pool_no])) {
|
||||
case MRB_TT_FIXNUM:
|
||||
cur += uint8_to_bin(IREP_TT_FIXNUM, cur); /* data type */
|
||||
str = mrb_fixnum_to_str(mrb, irep->pool[pool_no], 10);
|
||||
break;
|
||||
|
||||
#ifndef MRB_WITHOUT_FLOAT
|
||||
case MRB_TT_FLOAT:
|
||||
cur += uint8_to_bin(IREP_TT_FLOAT, cur); /* data type */
|
||||
str = float_to_str(mrb, irep->pool[pool_no]);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case MRB_TT_STRING:
|
||||
cur += uint8_to_bin(IREP_TT_STRING, cur); /* data type */
|
||||
str = irep->pool[pool_no];
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
char_ptr = RSTRING_PTR(str);
|
||||
{
|
||||
mrb_int tlen = RSTRING_LEN(str);
|
||||
mrb_assert_int_fit(mrb_int, tlen, uint16_t, UINT16_MAX);
|
||||
len = (uint16_t)tlen;
|
||||
}
|
||||
|
||||
cur += uint16_to_bin(len, cur); /* data length */
|
||||
memcpy(cur, char_ptr, (size_t)len);
|
||||
cur += len;
|
||||
|
||||
mrb_gc_arena_restore(mrb, ai);
|
||||
}
|
||||
|
||||
return cur - buf;
|
||||
}
|
||||
|
||||
|
||||
static size_t
|
||||
get_syms_block_size(mrb_state *mrb, mrb_irep *irep)
|
||||
{
|
||||
size_t size = 0;
|
||||
int sym_no;
|
||||
mrb_int len;
|
||||
|
||||
size += sizeof(uint32_t); /* slen */
|
||||
for (sym_no = 0; sym_no < irep->slen; sym_no++) {
|
||||
size += sizeof(uint16_t); /* snl(n) */
|
||||
if (irep->syms[sym_no] != 0) {
|
||||
mrb_sym_name_len(mrb, irep->syms[sym_no], &len);
|
||||
size += len + 1; /* sn(n) + null char */
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static ptrdiff_t
|
||||
write_syms_block(mrb_state *mrb, mrb_irep *irep, uint8_t *buf)
|
||||
{
|
||||
int sym_no;
|
||||
uint8_t *cur = buf;
|
||||
const char *name;
|
||||
|
||||
cur += uint32_to_bin(irep->slen, cur); /* number of symbol */
|
||||
|
||||
for (sym_no = 0; sym_no < irep->slen; sym_no++) {
|
||||
if (irep->syms[sym_no] != 0) {
|
||||
mrb_int len;
|
||||
|
||||
name = mrb_sym_name_len(mrb, irep->syms[sym_no], &len);
|
||||
|
||||
mrb_assert_int_fit(mrb_int, len, uint16_t, UINT16_MAX);
|
||||
cur += uint16_to_bin((uint16_t)len, cur); /* length of symbol name */
|
||||
memcpy(cur, name, len); /* symbol name */
|
||||
cur += (uint16_t)len;
|
||||
*cur++ = '\0';
|
||||
}
|
||||
else {
|
||||
cur += uint16_to_bin(MRB_DUMP_NULL_SYM_LEN, cur); /* length of symbol name */
|
||||
}
|
||||
}
|
||||
|
||||
return cur - buf;
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_irep_record_size_1(mrb_state *mrb, mrb_irep *irep)
|
||||
{
|
||||
size_t size = 0;
|
||||
|
||||
size += get_irep_header_size(mrb);
|
||||
size += get_iseq_block_size(mrb, irep);
|
||||
size += get_pool_block_size(mrb, irep);
|
||||
size += get_syms_block_size(mrb, irep);
|
||||
return size;
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_irep_record_size(mrb_state *mrb, mrb_irep *irep)
|
||||
{
|
||||
size_t size = 0;
|
||||
int irep_no;
|
||||
|
||||
size = get_irep_record_size_1(mrb, irep);
|
||||
for (irep_no = 0; irep_no < irep->rlen; irep_no++) {
|
||||
size += get_irep_record_size(mrb, irep->reps[irep_no]);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static int
|
||||
write_irep_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *irep_record_size, uint8_t flags)
|
||||
{
|
||||
int i;
|
||||
uint8_t *src = bin;
|
||||
|
||||
if (irep == NULL) {
|
||||
return MRB_DUMP_INVALID_IREP;
|
||||
}
|
||||
|
||||
*irep_record_size = get_irep_record_size_1(mrb, irep);
|
||||
if (*irep_record_size == 0) {
|
||||
return MRB_DUMP_GENERAL_FAILURE;
|
||||
}
|
||||
|
||||
bin += write_irep_header(mrb, irep, bin);
|
||||
bin += write_iseq_block(mrb, irep, bin, flags);
|
||||
bin += write_pool_block(mrb, irep, bin);
|
||||
bin += write_syms_block(mrb, irep, bin);
|
||||
|
||||
for (i = 0; i < irep->rlen; i++) {
|
||||
int result;
|
||||
size_t rsize;
|
||||
|
||||
result = write_irep_record(mrb, irep->reps[i], bin, &rsize, flags);
|
||||
if (result != MRB_DUMP_OK) {
|
||||
return result;
|
||||
}
|
||||
bin += rsize;
|
||||
}
|
||||
*irep_record_size = bin - src;
|
||||
return MRB_DUMP_OK;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
write_footer(mrb_state *mrb, uint8_t *bin)
|
||||
{
|
||||
struct rite_binary_footer footer;
|
||||
|
||||
memcpy(footer.section_ident, RITE_BINARY_EOF, sizeof(footer.section_ident));
|
||||
uint32_to_bin(sizeof(struct rite_binary_footer), footer.section_size);
|
||||
memcpy(bin, &footer, sizeof(struct rite_binary_footer));
|
||||
|
||||
return sizeof(struct rite_binary_footer);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
write_section_irep_header(mrb_state *mrb, size_t section_size, uint8_t *bin)
|
||||
{
|
||||
struct rite_section_irep_header *header = (struct rite_section_irep_header*)bin;
|
||||
|
||||
memcpy(header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(header->section_ident));
|
||||
|
||||
mrb_assert_int_fit(size_t, section_size, uint32_t, UINT32_MAX);
|
||||
uint32_to_bin((uint32_t)section_size, header->section_size);
|
||||
memcpy(header->rite_version, RITE_VM_VER, sizeof(header->rite_version));
|
||||
|
||||
return MRB_DUMP_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
write_section_irep(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, size_t *len_p, uint8_t flags)
|
||||
{
|
||||
int result;
|
||||
size_t rsize = 0;
|
||||
uint8_t *cur = bin;
|
||||
|
||||
if (mrb == NULL || bin == NULL) {
|
||||
return MRB_DUMP_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
cur += sizeof(struct rite_section_irep_header);
|
||||
|
||||
result = write_irep_record(mrb, irep, cur, &rsize, flags);
|
||||
if (result != MRB_DUMP_OK) {
|
||||
return result;
|
||||
}
|
||||
*len_p = cur - bin + rsize;
|
||||
write_section_irep_header(mrb, *len_p, bin);
|
||||
|
||||
return MRB_DUMP_OK;
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_debug_record_size(mrb_state *mrb, mrb_irep *irep)
|
||||
{
|
||||
size_t ret = 0;
|
||||
uint16_t f_idx;
|
||||
int i;
|
||||
|
||||
ret += sizeof(uint32_t); /* record size */
|
||||
ret += sizeof(uint16_t); /* file count */
|
||||
|
||||
for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
|
||||
mrb_irep_debug_info_file const* file = irep->debug_info->files[f_idx];
|
||||
|
||||
ret += sizeof(uint32_t); /* position */
|
||||
ret += sizeof(uint16_t); /* filename index */
|
||||
|
||||
/* lines */
|
||||
ret += sizeof(uint32_t); /* entry count */
|
||||
ret += sizeof(uint8_t); /* line type */
|
||||
switch (file->line_type) {
|
||||
case mrb_debug_line_ary:
|
||||
ret += sizeof(uint16_t) * (size_t)(file->line_entry_count);
|
||||
break;
|
||||
|
||||
case mrb_debug_line_flat_map:
|
||||
ret += (sizeof(uint32_t) + sizeof(uint16_t)) * (size_t)(file->line_entry_count);
|
||||
break;
|
||||
|
||||
default: mrb_assert(0); break;
|
||||
}
|
||||
}
|
||||
for (i=0; i<irep->rlen; i++) {
|
||||
ret += get_debug_record_size(mrb, irep->reps[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
find_filename_index(const mrb_sym *ary, int ary_len, mrb_sym s)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ary_len; ++i) {
|
||||
if (ary[i] == s) { return i; }
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_filename_table_size(mrb_state *mrb, mrb_irep *irep, mrb_sym **fp, uint16_t *lp)
|
||||
{
|
||||
mrb_sym *filenames = *fp;
|
||||
size_t size = 0;
|
||||
mrb_irep_debug_info *di = irep->debug_info;
|
||||
int i;
|
||||
|
||||
mrb_assert(lp);
|
||||
for (i = 0; i < di->flen; ++i) {
|
||||
mrb_irep_debug_info_file *file;
|
||||
mrb_int filename_len;
|
||||
|
||||
file = di->files[i];
|
||||
if (find_filename_index(filenames, *lp, file->filename_sym) == -1) {
|
||||
/* register filename */
|
||||
*lp += 1;
|
||||
*fp = filenames = (mrb_sym *)mrb_realloc(mrb, filenames, sizeof(mrb_sym) * (*lp));
|
||||
filenames[*lp - 1] = file->filename_sym;
|
||||
|
||||
/* filename */
|
||||
mrb_sym_name_len(mrb, file->filename_sym, &filename_len);
|
||||
size += sizeof(uint16_t) + (size_t)filename_len;
|
||||
}
|
||||
}
|
||||
for (i=0; i<irep->rlen; i++) {
|
||||
size += get_filename_table_size(mrb, irep->reps[i], fp, lp);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static size_t
|
||||
write_debug_record_1(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len)
|
||||
{
|
||||
uint8_t *cur;
|
||||
uint16_t f_idx;
|
||||
ptrdiff_t ret;
|
||||
|
||||
cur = bin + sizeof(uint32_t); /* skip record size */
|
||||
cur += uint16_to_bin(irep->debug_info->flen, cur); /* file count */
|
||||
|
||||
for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
|
||||
int filename_idx;
|
||||
const mrb_irep_debug_info_file *file = irep->debug_info->files[f_idx];
|
||||
|
||||
/* position */
|
||||
cur += uint32_to_bin(file->start_pos, cur);
|
||||
|
||||
/* filename index */
|
||||
filename_idx = find_filename_index(filenames, filenames_len,
|
||||
file->filename_sym);
|
||||
mrb_assert_int_fit(int, filename_idx, uint16_t, UINT16_MAX);
|
||||
cur += uint16_to_bin((uint16_t)filename_idx, cur);
|
||||
|
||||
/* lines */
|
||||
cur += uint32_to_bin(file->line_entry_count, cur);
|
||||
cur += uint8_to_bin(file->line_type, cur);
|
||||
switch (file->line_type) {
|
||||
case mrb_debug_line_ary: {
|
||||
uint32_t l;
|
||||
for (l = 0; l < file->line_entry_count; ++l) {
|
||||
cur += uint16_to_bin(file->lines.ary[l], cur);
|
||||
}
|
||||
} break;
|
||||
|
||||
case mrb_debug_line_flat_map: {
|
||||
uint32_t line;
|
||||
for (line = 0; line < file->line_entry_count; ++line) {
|
||||
cur += uint32_to_bin(file->lines.flat_map[line].start_pos, cur);
|
||||
cur += uint16_to_bin(file->lines.flat_map[line].line, cur);
|
||||
}
|
||||
} break;
|
||||
|
||||
default: mrb_assert(0); break;
|
||||
}
|
||||
}
|
||||
|
||||
ret = cur - bin;
|
||||
mrb_assert_int_fit(ptrdiff_t, ret, uint32_t, UINT32_MAX);
|
||||
uint32_to_bin((uint32_t)ret, bin);
|
||||
|
||||
mrb_assert_int_fit(ptrdiff_t, ret, size_t, SIZE_MAX);
|
||||
return (size_t)ret;
|
||||
}
|
||||
|
||||
static size_t
|
||||
write_debug_record(mrb_state *mrb, mrb_irep *irep, uint8_t *bin, mrb_sym const* filenames, uint16_t filenames_len)
|
||||
{
|
||||
size_t size, len;
|
||||
int irep_no;
|
||||
|
||||
size = len = write_debug_record_1(mrb, irep, bin, filenames, filenames_len);
|
||||
bin += len;
|
||||
for (irep_no = 0; irep_no < irep->rlen; irep_no++) {
|
||||
len = write_debug_record(mrb, irep->reps[irep_no], bin, filenames, filenames_len);
|
||||
bin += len;
|
||||
size += len;
|
||||
}
|
||||
|
||||
mrb_assert(size == get_debug_record_size(mrb, irep));
|
||||
return size;
|
||||
}
|
||||
|
||||
static int
|
||||
write_section_debug(mrb_state *mrb, mrb_irep *irep, uint8_t *cur, mrb_sym const *filenames, uint16_t filenames_len)
|
||||
{
|
||||
size_t section_size = 0;
|
||||
const uint8_t *bin = cur;
|
||||
struct rite_section_debug_header *header;
|
||||
size_t dlen;
|
||||
uint16_t i;
|
||||
char const *sym; mrb_int sym_len;
|
||||
|
||||
if (mrb == NULL || cur == NULL) {
|
||||
return MRB_DUMP_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
header = (struct rite_section_debug_header *)bin;
|
||||
cur += sizeof(struct rite_section_debug_header);
|
||||
section_size += sizeof(struct rite_section_debug_header);
|
||||
|
||||
/* filename table */
|
||||
cur += uint16_to_bin(filenames_len, cur);
|
||||
section_size += sizeof(uint16_t);
|
||||
for (i = 0; i < filenames_len; ++i) {
|
||||
sym = mrb_sym_name_len(mrb, filenames[i], &sym_len);
|
||||
mrb_assert(sym);
|
||||
cur += uint16_to_bin((uint16_t)sym_len, cur);
|
||||
memcpy(cur, sym, sym_len);
|
||||
cur += sym_len;
|
||||
section_size += sizeof(uint16_t) + sym_len;
|
||||
}
|
||||
|
||||
/* debug records */
|
||||
dlen = write_debug_record(mrb, irep, cur, filenames, filenames_len);
|
||||
section_size += dlen;
|
||||
|
||||
memcpy(header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(header->section_ident));
|
||||
mrb_assert(section_size <= INT32_MAX);
|
||||
uint32_to_bin((uint32_t)section_size, header->section_size);
|
||||
|
||||
return MRB_DUMP_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
create_lv_sym_table(mrb_state *mrb, const mrb_irep *irep, mrb_sym **syms, uint32_t *syms_len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (*syms == NULL) {
|
||||
*syms = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) * 1);
|
||||
}
|
||||
|
||||
for (i = 0; i + 1 < irep->nlocals; ++i) {
|
||||
mrb_sym const name = irep->lv[i].name;
|
||||
if (name == 0) continue;
|
||||
if (find_filename_index(*syms, *syms_len, name) != -1) continue;
|
||||
|
||||
++(*syms_len);
|
||||
*syms = (mrb_sym*)mrb_realloc(mrb, *syms, sizeof(mrb_sym) * (*syms_len));
|
||||
(*syms)[*syms_len - 1] = name;
|
||||
}
|
||||
|
||||
for (i = 0; i < irep->rlen; ++i) {
|
||||
create_lv_sym_table(mrb, irep->reps[i], syms, syms_len);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
write_lv_sym_table(mrb_state *mrb, uint8_t **start, mrb_sym const *syms, uint32_t syms_len)
|
||||
{
|
||||
uint8_t *cur = *start;
|
||||
uint32_t i;
|
||||
const char *str;
|
||||
mrb_int str_len;
|
||||
|
||||
cur += uint32_to_bin(syms_len, cur);
|
||||
|
||||
for (i = 0; i < syms_len; ++i) {
|
||||
str = mrb_sym_name_len(mrb, syms[i], &str_len);
|
||||
cur += uint16_to_bin((uint16_t)str_len, cur);
|
||||
memcpy(cur, str, str_len);
|
||||
cur += str_len;
|
||||
}
|
||||
|
||||
*start = cur;
|
||||
|
||||
return MRB_DUMP_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
write_lv_record(mrb_state *mrb, const mrb_irep *irep, uint8_t **start, mrb_sym const *syms, uint32_t syms_len)
|
||||
{
|
||||
uint8_t *cur = *start;
|
||||
int i;
|
||||
|
||||
for (i = 0; i + 1 < irep->nlocals; ++i) {
|
||||
if (irep->lv[i].name == 0) {
|
||||
cur += uint16_to_bin(RITE_LV_NULL_MARK, cur);
|
||||
cur += uint16_to_bin(0, cur);
|
||||
}
|
||||
else {
|
||||
int const sym_idx = find_filename_index(syms, syms_len, irep->lv[i].name);
|
||||
mrb_assert(sym_idx != -1); /* local variable name must be in syms */
|
||||
|
||||
cur += uint16_to_bin(sym_idx, cur);
|
||||
cur += uint16_to_bin(irep->lv[i].r, cur);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < irep->rlen; ++i) {
|
||||
write_lv_record(mrb, irep->reps[i], &cur, syms, syms_len);
|
||||
}
|
||||
|
||||
*start = cur;
|
||||
|
||||
return MRB_DUMP_OK;
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_lv_record_size(mrb_state *mrb, mrb_irep *irep)
|
||||
{
|
||||
size_t ret = 0;
|
||||
int i;
|
||||
|
||||
ret += (sizeof(uint16_t) + sizeof(uint16_t)) * (irep->nlocals - 1);
|
||||
|
||||
for (i = 0; i < irep->rlen; ++i) {
|
||||
ret += get_lv_record_size(mrb, irep->reps[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static size_t
|
||||
get_lv_section_size(mrb_state *mrb, mrb_irep *irep, mrb_sym const *syms, uint32_t syms_len)
|
||||
{
|
||||
size_t ret = 0, i;
|
||||
|
||||
ret += sizeof(uint32_t); /* syms_len */
|
||||
ret += sizeof(uint16_t) * syms_len; /* symbol name lengths */
|
||||
for (i = 0; i < syms_len; ++i) {
|
||||
mrb_int str_len;
|
||||
mrb_sym_name_len(mrb, syms[i], &str_len);
|
||||
ret += str_len;
|
||||
}
|
||||
|
||||
ret += get_lv_record_size(mrb, irep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
write_section_lv(mrb_state *mrb, mrb_irep *irep, uint8_t *start, mrb_sym const *syms, uint32_t const syms_len)
|
||||
{
|
||||
uint8_t *cur = start;
|
||||
struct rite_section_lv_header *header;
|
||||
ptrdiff_t diff;
|
||||
int result = MRB_DUMP_OK;
|
||||
|
||||
if (mrb == NULL || cur == NULL) {
|
||||
return MRB_DUMP_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
header = (struct rite_section_lv_header*)cur;
|
||||
cur += sizeof(struct rite_section_lv_header);
|
||||
|
||||
result = write_lv_sym_table(mrb, &cur, syms, syms_len);
|
||||
if (result != MRB_DUMP_OK) {
|
||||
goto lv_section_exit;
|
||||
}
|
||||
|
||||
result = write_lv_record(mrb, irep, &cur, syms, syms_len);
|
||||
if (result != MRB_DUMP_OK) {
|
||||
goto lv_section_exit;
|
||||
}
|
||||
|
||||
memcpy(header->section_ident, RITE_SECTION_LV_IDENT, sizeof(header->section_ident));
|
||||
|
||||
diff = cur - start;
|
||||
mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
|
||||
uint32_to_bin((uint32_t)diff, header->section_size);
|
||||
|
||||
lv_section_exit:
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
write_rite_binary_header(mrb_state *mrb, size_t binary_size, uint8_t *bin, uint8_t flags)
|
||||
{
|
||||
struct rite_binary_header *header = (struct rite_binary_header *)bin;
|
||||
uint16_t crc;
|
||||
uint32_t offset;
|
||||
|
||||
memcpy(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident));
|
||||
memcpy(header->binary_version, RITE_BINARY_FORMAT_VER, sizeof(header->binary_version));
|
||||
memcpy(header->compiler_name, RITE_COMPILER_NAME, sizeof(header->compiler_name));
|
||||
memcpy(header->compiler_version, RITE_COMPILER_VERSION, sizeof(header->compiler_version));
|
||||
mrb_assert(binary_size <= UINT32_MAX);
|
||||
uint32_to_bin((uint32_t)binary_size, header->binary_size);
|
||||
|
||||
offset = (uint32_t)((&(header->binary_crc[0]) - bin) + sizeof(uint16_t));
|
||||
crc = calc_crc_16_ccitt(bin + offset, binary_size - offset, 0);
|
||||
uint16_to_bin(crc, header->binary_crc);
|
||||
|
||||
return MRB_DUMP_OK;
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
debug_info_defined_p(mrb_irep *irep)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!irep->debug_info) return FALSE;
|
||||
for (i=0; i<irep->rlen; i++) {
|
||||
if (!debug_info_defined_p(irep->reps[i])) return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
lv_defined_p(mrb_irep *irep)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (irep->lv) { return TRUE; }
|
||||
|
||||
for (i = 0; i < irep->rlen; ++i) {
|
||||
if (lv_defined_p(irep->reps[i])) { return TRUE; }
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static int
|
||||
dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size)
|
||||
{
|
||||
int result = MRB_DUMP_GENERAL_FAILURE;
|
||||
size_t malloc_size;
|
||||
size_t section_irep_size;
|
||||
size_t section_lineno_size = 0, section_lv_size = 0;
|
||||
uint8_t *cur = NULL;
|
||||
mrb_bool const debug_info_defined = debug_info_defined_p(irep), lv_defined = lv_defined_p(irep);
|
||||
mrb_sym *lv_syms = NULL; uint32_t lv_syms_len = 0;
|
||||
mrb_sym *filenames = NULL; uint16_t filenames_len = 0;
|
||||
|
||||
if (mrb == NULL) {
|
||||
*bin = NULL;
|
||||
return MRB_DUMP_GENERAL_FAILURE;
|
||||
}
|
||||
|
||||
section_irep_size = sizeof(struct rite_section_irep_header);
|
||||
section_irep_size += get_irep_record_size(mrb, irep);
|
||||
|
||||
/* DEBUG section size */
|
||||
if (flags & DUMP_DEBUG_INFO) {
|
||||
if (debug_info_defined) {
|
||||
section_lineno_size += sizeof(struct rite_section_debug_header);
|
||||
/* filename table */
|
||||
filenames = (mrb_sym*)mrb_malloc(mrb, sizeof(mrb_sym) + 1);
|
||||
|
||||
/* filename table size */
|
||||
section_lineno_size += sizeof(uint16_t);
|
||||
section_lineno_size += get_filename_table_size(mrb, irep, &filenames, &filenames_len);
|
||||
|
||||
section_lineno_size += get_debug_record_size(mrb, irep);
|
||||
}
|
||||
}
|
||||
|
||||
if (lv_defined) {
|
||||
section_lv_size += sizeof(struct rite_section_lv_header);
|
||||
create_lv_sym_table(mrb, irep, &lv_syms, &lv_syms_len);
|
||||
section_lv_size += get_lv_section_size(mrb, irep, lv_syms, lv_syms_len);
|
||||
}
|
||||
|
||||
malloc_size = sizeof(struct rite_binary_header) +
|
||||
section_irep_size + section_lineno_size + section_lv_size +
|
||||
sizeof(struct rite_binary_footer);
|
||||
cur = *bin = (uint8_t*)mrb_malloc(mrb, malloc_size);
|
||||
cur += sizeof(struct rite_binary_header);
|
||||
|
||||
result = write_section_irep(mrb, irep, cur, §ion_irep_size, flags);
|
||||
if (result != MRB_DUMP_OK) {
|
||||
goto error_exit;
|
||||
}
|
||||
cur += section_irep_size;
|
||||
*bin_size = sizeof(struct rite_binary_header) +
|
||||
section_irep_size + section_lineno_size + section_lv_size +
|
||||
sizeof(struct rite_binary_footer);
|
||||
|
||||
/* write DEBUG section */
|
||||
if (flags & DUMP_DEBUG_INFO) {
|
||||
if (debug_info_defined) {
|
||||
result = write_section_debug(mrb, irep, cur, filenames, filenames_len);
|
||||
if (result != MRB_DUMP_OK) {
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
cur += section_lineno_size;
|
||||
}
|
||||
|
||||
if (lv_defined) {
|
||||
result = write_section_lv(mrb, irep, cur, lv_syms, lv_syms_len);
|
||||
if (result != MRB_DUMP_OK) {
|
||||
goto error_exit;
|
||||
}
|
||||
cur += section_lv_size;
|
||||
}
|
||||
|
||||
write_footer(mrb, cur);
|
||||
write_rite_binary_header(mrb, *bin_size, *bin, flags);
|
||||
|
||||
error_exit:
|
||||
if (result != MRB_DUMP_OK) {
|
||||
mrb_free(mrb, *bin);
|
||||
*bin = NULL;
|
||||
}
|
||||
mrb_free(mrb, lv_syms);
|
||||
mrb_free(mrb, filenames);
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_dump_irep(mrb_state *mrb, mrb_irep *irep, uint8_t flags, uint8_t **bin, size_t *bin_size)
|
||||
{
|
||||
return dump_irep(mrb, irep, flags, bin, bin_size);
|
||||
}
|
||||
|
||||
#ifndef MRB_DISABLE_STDIO
|
||||
|
||||
int
|
||||
mrb_dump_irep_binary(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE* fp)
|
||||
{
|
||||
uint8_t *bin = NULL;
|
||||
size_t bin_size = 0;
|
||||
int result;
|
||||
|
||||
if (fp == NULL) {
|
||||
return MRB_DUMP_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
result = dump_irep(mrb, irep, flags, &bin, &bin_size);
|
||||
if (result == MRB_DUMP_OK) {
|
||||
if (fwrite(bin, sizeof(bin[0]), bin_size, fp) != bin_size) {
|
||||
result = MRB_DUMP_WRITE_FAULT;
|
||||
}
|
||||
}
|
||||
|
||||
mrb_free(mrb, bin);
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
mrb_dump_irep_cfunc(mrb_state *mrb, mrb_irep *irep, uint8_t flags, FILE *fp, const char *initname)
|
||||
{
|
||||
uint8_t *bin = NULL;
|
||||
size_t bin_size = 0, bin_idx = 0;
|
||||
int result;
|
||||
|
||||
if (fp == NULL || initname == NULL || initname[0] == '\0') {
|
||||
return MRB_DUMP_INVALID_ARGUMENT;
|
||||
}
|
||||
result = dump_irep(mrb, irep, flags, &bin, &bin_size);
|
||||
if (result == MRB_DUMP_OK) {
|
||||
if (fprintf(fp, "#include <stdint.h>\n") < 0) { /* for uint8_t under at least Darwin */
|
||||
mrb_free(mrb, bin);
|
||||
return MRB_DUMP_WRITE_FAULT;
|
||||
}
|
||||
if (fprintf(fp,
|
||||
"#ifdef __cplusplus\n"
|
||||
"extern const uint8_t %s[];\n"
|
||||
"#endif\n"
|
||||
"const uint8_t\n"
|
||||
"#if defined __GNUC__\n"
|
||||
"__attribute__((aligned(%u)))\n"
|
||||
"#elif defined _MSC_VER\n"
|
||||
"__declspec(align(%u))\n"
|
||||
"#endif\n"
|
||||
"%s[] = {",
|
||||
initname,
|
||||
(uint16_t)MRB_DUMP_ALIGNMENT, (uint16_t)MRB_DUMP_ALIGNMENT, initname) < 0) {
|
||||
mrb_free(mrb, bin);
|
||||
return MRB_DUMP_WRITE_FAULT;
|
||||
}
|
||||
while (bin_idx < bin_size) {
|
||||
if (bin_idx % 16 == 0) {
|
||||
if (fputs("\n", fp) == EOF) {
|
||||
mrb_free(mrb, bin);
|
||||
return MRB_DUMP_WRITE_FAULT;
|
||||
}
|
||||
}
|
||||
if (fprintf(fp, "0x%02x,", bin[bin_idx++]) < 0) {
|
||||
mrb_free(mrb, bin);
|
||||
return MRB_DUMP_WRITE_FAULT;
|
||||
}
|
||||
}
|
||||
if (fputs("\n};\n", fp) == EOF) {
|
||||
mrb_free(mrb, bin);
|
||||
return MRB_DUMP_WRITE_FAULT;
|
||||
}
|
||||
}
|
||||
|
||||
mrb_free(mrb, bin);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* MRB_DISABLE_STDIO */
|
||||
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
** enum.c - Enumerable module
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
*/
|
||||
|
||||
#include <mruby.h>
|
||||
#include <mruby/proc.h>
|
||||
|
||||
/* internal method `__update_hash(oldhash, index, itemhash)` */
|
||||
static mrb_value
|
||||
enum_update_hash(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_int hash;
|
||||
mrb_int index;
|
||||
mrb_int hv;
|
||||
|
||||
mrb_get_args(mrb, "iii", &hash, &index, &hv);
|
||||
hash ^= ((uint32_t)hv << (index % 16));
|
||||
|
||||
return mrb_fixnum_value(hash);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_init_enumerable(mrb_state *mrb)
|
||||
{
|
||||
struct RClass *enumerable;
|
||||
enumerable = mrb_define_module(mrb, "Enumerable"); /* 15.3.2 */
|
||||
mrb_define_module_function(mrb, enumerable, "__update_hash", enum_update_hash, MRB_ARGS_REQ(3));
|
||||
}
|
||||
+632
@@ -0,0 +1,632 @@
|
||||
/*
|
||||
** error.c - Exception class
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <mruby.h>
|
||||
#include <mruby/array.h>
|
||||
#include <mruby/irep.h>
|
||||
#include <mruby/proc.h>
|
||||
#include <mruby/string.h>
|
||||
#include <mruby/variable.h>
|
||||
#include <mruby/error.h>
|
||||
#include <mruby/class.h>
|
||||
#include <mruby/throw.h>
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_exc_new(mrb_state *mrb, struct RClass *c, const char *ptr, size_t len)
|
||||
{
|
||||
mrb_value arg = mrb_str_new(mrb, ptr, len);
|
||||
return mrb_obj_new(mrb, c, 1, &arg);
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value str)
|
||||
{
|
||||
mrb_to_str(mrb, str);
|
||||
return mrb_obj_new(mrb, c, 1, &str);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Exception.new(msg = nil) -> exception
|
||||
*
|
||||
* Construct a new Exception object, optionally passing in
|
||||
* a message.
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
exc_initialize(mrb_state *mrb, mrb_value exc)
|
||||
{
|
||||
mrb_value mesg;
|
||||
|
||||
if (mrb_get_args(mrb, "|o", &mesg) == 1) {
|
||||
mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "mesg"), mesg);
|
||||
}
|
||||
return exc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Document-method: exception
|
||||
*
|
||||
* call-seq:
|
||||
* exc.exception(string) -> an_exception or exc
|
||||
*
|
||||
* With no argument, or if the argument is the same as the receiver,
|
||||
* return the receiver. Otherwise, create a new
|
||||
* exception object of the same class as the receiver, but with a
|
||||
* message equal to <code>string</code>.
|
||||
*
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
exc_exception(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value exc;
|
||||
mrb_value a;
|
||||
mrb_int argc;
|
||||
|
||||
argc = mrb_get_args(mrb, "|o", &a);
|
||||
if (argc == 0) return self;
|
||||
if (mrb_obj_equal(mrb, self, a)) return self;
|
||||
exc = mrb_obj_clone(mrb, self);
|
||||
mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "mesg"), a);
|
||||
|
||||
return exc;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* exception.to_s -> string
|
||||
*
|
||||
* Returns exception's message (or the name of the exception if
|
||||
* no message is set).
|
||||
*/
|
||||
|
||||
mrb_value
|
||||
exc_to_s(mrb_state *mrb, mrb_value exc)
|
||||
{
|
||||
mrb_value mesg = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "mesg"));
|
||||
struct RObject *p;
|
||||
|
||||
if (!mrb_string_p(mesg)) {
|
||||
return mrb_str_new_cstr(mrb, mrb_obj_classname(mrb, exc));
|
||||
}
|
||||
p = mrb_obj_ptr(mesg);
|
||||
if (!p->c) {
|
||||
p->c = mrb->string_class;
|
||||
}
|
||||
return mesg;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* exception.message -> string
|
||||
*
|
||||
* Returns the result of invoking <code>exception.to_s</code>.
|
||||
* Normally this returns the exception's message or name.
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
exc_message(mrb_state *mrb, mrb_value exc)
|
||||
{
|
||||
return mrb_funcall(mrb, exc, "to_s", 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* exception.inspect -> string
|
||||
*
|
||||
* Returns this exception's file name, line number,
|
||||
* message and class name.
|
||||
* If file name or line number is not set,
|
||||
* returns message and class name.
|
||||
*/
|
||||
|
||||
mrb_value
|
||||
mrb_exc_inspect(mrb_state *mrb, mrb_value exc)
|
||||
{
|
||||
mrb_value mesg = mrb_attr_get(mrb, exc, mrb_intern_lit(mrb, "mesg"));
|
||||
mrb_value cname = mrb_mod_to_s(mrb, mrb_obj_value(mrb_obj_class(mrb, exc)));
|
||||
mesg = mrb_obj_as_string(mrb, mesg);
|
||||
return RSTRING_LEN(mesg) == 0 ? cname : mrb_format(mrb, "%v (%v)", mesg, cname);
|
||||
}
|
||||
|
||||
void mrb_keep_backtrace(mrb_state *mrb, mrb_value exc);
|
||||
|
||||
static void
|
||||
set_backtrace(mrb_state *mrb, mrb_value exc, mrb_value backtrace)
|
||||
{
|
||||
if (!mrb_array_p(backtrace)) {
|
||||
type_err:
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "backtrace must be Array of String");
|
||||
}
|
||||
else {
|
||||
const mrb_value *p = RARRAY_PTR(backtrace);
|
||||
const mrb_value *pend = p + RARRAY_LEN(backtrace);
|
||||
|
||||
while (p < pend) {
|
||||
if (!mrb_string_p(*p)) goto type_err;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
mrb_iv_set(mrb, exc, mrb_intern_lit(mrb, "backtrace"), backtrace);
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
exc_set_backtrace(mrb_state *mrb, mrb_value exc)
|
||||
{
|
||||
mrb_value backtrace = mrb_get_arg1(mrb);
|
||||
|
||||
set_backtrace(mrb, exc, backtrace);
|
||||
return backtrace;
|
||||
}
|
||||
|
||||
void
|
||||
mrb_exc_set(mrb_state *mrb, mrb_value exc)
|
||||
{
|
||||
if (mrb_nil_p(exc)) {
|
||||
mrb->exc = 0;
|
||||
}
|
||||
else {
|
||||
mrb->exc = mrb_obj_ptr(exc);
|
||||
if (mrb->gc.arena_idx > 0 &&
|
||||
(struct RBasic*)mrb->exc == mrb->gc.arena[mrb->gc.arena_idx-1]) {
|
||||
mrb->gc.arena_idx--;
|
||||
}
|
||||
if (!mrb->gc.out_of_memory && !mrb_frozen_p(mrb->exc)) {
|
||||
mrb_keep_backtrace(mrb, exc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static mrb_noreturn void
|
||||
exc_throw(mrb_state *mrb, mrb_value exc)
|
||||
{
|
||||
if (!mrb->jmp) {
|
||||
mrb_p(mrb, exc);
|
||||
abort();
|
||||
}
|
||||
MRB_THROW(mrb->jmp);
|
||||
}
|
||||
|
||||
MRB_API mrb_noreturn void
|
||||
mrb_exc_raise(mrb_state *mrb, mrb_value exc)
|
||||
{
|
||||
if (mrb_break_p(exc)) {
|
||||
mrb->exc = mrb_obj_ptr(exc);
|
||||
}
|
||||
else {
|
||||
if (!mrb_obj_is_kind_of(mrb, exc, mrb->eException_class)) {
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "exception object expected");
|
||||
}
|
||||
mrb_exc_set(mrb, exc);
|
||||
}
|
||||
exc_throw(mrb, exc);
|
||||
}
|
||||
|
||||
MRB_API mrb_noreturn void
|
||||
mrb_raise(mrb_state *mrb, struct RClass *c, const char *msg)
|
||||
{
|
||||
mrb_exc_raise(mrb, mrb_exc_new_str(mrb, c, mrb_str_new_cstr(mrb, msg)));
|
||||
}
|
||||
|
||||
/*
|
||||
* <code>vsprintf</code> like formatting.
|
||||
*
|
||||
* The syntax of a format sequence is as follows.
|
||||
*
|
||||
* %[modifier]specifier
|
||||
*
|
||||
* The modifiers are:
|
||||
*
|
||||
* ----------+------------------------------------------------------------
|
||||
* Modifier | Meaning
|
||||
* ----------+------------------------------------------------------------
|
||||
* ! | Convert to string by corresponding `inspect` instead of
|
||||
* | corresponding `to_s`.
|
||||
* ----------+------------------------------------------------------------
|
||||
*
|
||||
* The specifiers are:
|
||||
*
|
||||
* ----------+----------------+--------------------------------------------
|
||||
* Specifier | Argument Type | Note
|
||||
* ----------+----------------+--------------------------------------------
|
||||
* c | char |
|
||||
* d | int |
|
||||
* f | mrb_float |
|
||||
* i | mrb_int |
|
||||
* l | char*, size_t | Arguments are string and length.
|
||||
* n | mrb_sym |
|
||||
* s | char* | Argument is NUL terminated string.
|
||||
* t | mrb_value | Convert to type (class) of object.
|
||||
* v,S | mrb_value |
|
||||
* C | struct RClass* |
|
||||
* T | mrb_value | Convert to real type (class) of object.
|
||||
* Y | mrb_value | Same as `!v` if argument is `true`, `false`
|
||||
* | | or `nil`, otherwise same as `T`.
|
||||
* % | - | Convert to percent sign itself (no argument
|
||||
* | | taken).
|
||||
* ----------+----------------+--------------------------------------------
|
||||
*/
|
||||
MRB_API mrb_value
|
||||
mrb_vformat(mrb_state *mrb, const char *format, va_list ap)
|
||||
{
|
||||
const char *chars, *p = format, *b = format, *e;
|
||||
char ch;
|
||||
size_t len;
|
||||
mrb_int i;
|
||||
struct RClass *cls;
|
||||
mrb_bool inspect = FALSE;
|
||||
mrb_value result = mrb_str_new_capa(mrb, 128), obj, str;
|
||||
int ai = mrb_gc_arena_save(mrb);
|
||||
|
||||
while (*p) {
|
||||
const char c = *p++;
|
||||
e = p;
|
||||
if (c == '%') {
|
||||
if (*p == '!') {
|
||||
inspect = TRUE;
|
||||
++p;
|
||||
}
|
||||
if (!*p) break;
|
||||
switch (*p) {
|
||||
case 'c':
|
||||
ch = (char)va_arg(ap, int);
|
||||
chars = &ch;
|
||||
len = 1;
|
||||
goto L_cat;
|
||||
case 'd': case 'i':
|
||||
#if MRB_INT_MAX < INT_MAX
|
||||
i = (mrb_int)va_arg(ap, int);
|
||||
#else
|
||||
i = *p == 'd' ? (mrb_int)va_arg(ap, int) : va_arg(ap, mrb_int);
|
||||
#endif
|
||||
obj = mrb_fixnum_value(i);
|
||||
goto L_cat_obj;
|
||||
#ifndef MRB_WITHOUT_FLOAT
|
||||
case 'f':
|
||||
obj = mrb_float_value(mrb, (mrb_float)va_arg(ap, double));
|
||||
goto L_cat_obj;
|
||||
#endif
|
||||
case 'l':
|
||||
chars = va_arg(ap, char*);
|
||||
len = va_arg(ap, size_t);
|
||||
L_cat:
|
||||
if (inspect) {
|
||||
obj = mrb_str_new(mrb, chars, len);
|
||||
goto L_cat_obj;
|
||||
}
|
||||
mrb_str_cat(mrb, result, b, e - b - 1);
|
||||
mrb_str_cat(mrb, result, chars, len);
|
||||
b = ++p;
|
||||
mrb_gc_arena_restore(mrb, ai);
|
||||
break;
|
||||
case 'n':
|
||||
#if UINT32_MAX < INT_MAX
|
||||
obj = mrb_symbol_value((mrb_sym)va_arg(ap, int));
|
||||
#else
|
||||
obj = mrb_symbol_value(va_arg(ap, mrb_sym));
|
||||
#endif
|
||||
goto L_cat_obj;
|
||||
case 's':
|
||||
chars = va_arg(ap, char*);
|
||||
len = strlen(chars);
|
||||
goto L_cat;
|
||||
case 't':
|
||||
cls = mrb_class(mrb, va_arg(ap, mrb_value));
|
||||
goto L_cat_class;
|
||||
case 'v': case 'S':
|
||||
obj = va_arg(ap, mrb_value);
|
||||
L_cat_obj:
|
||||
str = (inspect ? mrb_inspect : mrb_obj_as_string)(mrb, obj);
|
||||
chars = RSTRING_PTR(str);
|
||||
len = RSTRING_LEN(str);
|
||||
inspect = FALSE;
|
||||
goto L_cat;
|
||||
case 'C':
|
||||
cls = va_arg(ap, struct RClass*);
|
||||
L_cat_class:
|
||||
obj = mrb_obj_value(cls);
|
||||
goto L_cat_obj;
|
||||
case 'T':
|
||||
obj = va_arg(ap, mrb_value);
|
||||
L_cat_real_class_of:
|
||||
cls = mrb_obj_class(mrb, obj);
|
||||
goto L_cat_class;
|
||||
case 'Y':
|
||||
obj = va_arg(ap, mrb_value);
|
||||
if (!mrb_test(obj) || mrb_true_p(obj)) {
|
||||
inspect = TRUE;
|
||||
goto L_cat_obj;
|
||||
}
|
||||
else {
|
||||
goto L_cat_real_class_of;
|
||||
}
|
||||
case '%':
|
||||
L_cat_current:
|
||||
chars = p;
|
||||
len = 1;
|
||||
goto L_cat;
|
||||
default:
|
||||
mrb_raisef(mrb, E_ARGUMENT_ERROR, "malformed format string - %%%c", *p);
|
||||
}
|
||||
}
|
||||
else if (c == '\\') {
|
||||
if (!*p) break;
|
||||
goto L_cat_current;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
mrb_str_cat(mrb, result, b, p - b);
|
||||
return result;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_format(mrb_state *mrb, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
mrb_value str;
|
||||
|
||||
va_start(ap, format);
|
||||
str = mrb_vformat(mrb, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
static mrb_noreturn void
|
||||
raise_va(mrb_state *mrb, struct RClass *c, const char *fmt, va_list ap, int argc, mrb_value *argv)
|
||||
{
|
||||
mrb_value mesg;
|
||||
|
||||
mesg = mrb_vformat(mrb, fmt, ap);
|
||||
if (argv == NULL) {
|
||||
argv = &mesg;
|
||||
}
|
||||
else {
|
||||
argv[0] = mesg;
|
||||
}
|
||||
mrb_exc_raise(mrb, mrb_obj_new(mrb, c, argc+1, argv));
|
||||
}
|
||||
|
||||
MRB_API mrb_noreturn void
|
||||
mrb_raisef(mrb_state *mrb, struct RClass *c, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
raise_va(mrb, c, fmt, args, 0, NULL);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
MRB_API mrb_noreturn void
|
||||
mrb_name_error(mrb_state *mrb, mrb_sym id, const char *fmt, ...)
|
||||
{
|
||||
mrb_value argv[2];
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
argv[1] = mrb_symbol_value(id);
|
||||
raise_va(mrb, E_NAME_ERROR, fmt, args, 1, argv);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
MRB_API void
|
||||
mrb_warn(mrb_state *mrb, const char *fmt, ...)
|
||||
{
|
||||
#ifndef MRB_DISABLE_STDIO
|
||||
va_list ap;
|
||||
mrb_value str;
|
||||
|
||||
va_start(ap, fmt);
|
||||
str = mrb_vformat(mrb, fmt, ap);
|
||||
fputs("warning: ", stderr);
|
||||
fwrite(RSTRING_PTR(str), RSTRING_LEN(str), 1, stderr);
|
||||
putc('\n', stderr);
|
||||
va_end(ap);
|
||||
#endif
|
||||
}
|
||||
|
||||
MRB_API mrb_noreturn void
|
||||
mrb_bug(mrb_state *mrb, const char *fmt, ...)
|
||||
{
|
||||
#ifndef MRB_DISABLE_STDIO
|
||||
va_list ap;
|
||||
mrb_value str;
|
||||
|
||||
va_start(ap, fmt);
|
||||
str = mrb_vformat(mrb, fmt, ap);
|
||||
fputs("bug: ", stderr);
|
||||
fwrite(RSTRING_PTR(str), RSTRING_LEN(str), 1, stderr);
|
||||
va_end(ap);
|
||||
#endif
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_make_exception(mrb_state *mrb, mrb_int argc, const mrb_value *argv)
|
||||
{
|
||||
mrb_value mesg;
|
||||
int n;
|
||||
|
||||
mesg = mrb_nil_value();
|
||||
switch (argc) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
if (mrb_nil_p(argv[0]))
|
||||
break;
|
||||
if (mrb_string_p(argv[0])) {
|
||||
mesg = mrb_exc_new_str(mrb, E_RUNTIME_ERROR, argv[0]);
|
||||
break;
|
||||
}
|
||||
n = 0;
|
||||
goto exception_call;
|
||||
|
||||
case 2:
|
||||
case 3:
|
||||
n = 1;
|
||||
exception_call:
|
||||
{
|
||||
mrb_sym exc = mrb_intern_lit(mrb, "exception");
|
||||
if (mrb_respond_to(mrb, argv[0], exc)) {
|
||||
mesg = mrb_funcall_argv(mrb, argv[0], exc, n, argv+1);
|
||||
}
|
||||
else {
|
||||
/* undef */
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "exception class/object expected");
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
mrb_argnum_error(mrb, argc, 0, 3);
|
||||
break;
|
||||
}
|
||||
if (argc > 0) {
|
||||
if (!mrb_obj_is_kind_of(mrb, mesg, mrb->eException_class))
|
||||
mrb_raise(mrb, mrb->eException_class, "exception object expected");
|
||||
if (argc > 2)
|
||||
set_backtrace(mrb, mesg, argv[2]);
|
||||
}
|
||||
|
||||
return mesg;
|
||||
}
|
||||
|
||||
MRB_API void
|
||||
mrb_sys_fail(mrb_state *mrb, const char *mesg)
|
||||
{
|
||||
struct RClass *sce;
|
||||
mrb_int no;
|
||||
|
||||
no = (mrb_int)errno;
|
||||
if (mrb_class_defined(mrb, "SystemCallError")) {
|
||||
sce = mrb_class_get(mrb, "SystemCallError");
|
||||
if (mesg != NULL) {
|
||||
mrb_funcall(mrb, mrb_obj_value(sce), "_sys_fail", 2, mrb_fixnum_value(no), mrb_str_new_cstr(mrb, mesg));
|
||||
}
|
||||
else {
|
||||
mrb_funcall(mrb, mrb_obj_value(sce), "_sys_fail", 1, mrb_fixnum_value(no));
|
||||
}
|
||||
}
|
||||
else {
|
||||
mrb_raise(mrb, E_RUNTIME_ERROR, mesg);
|
||||
}
|
||||
}
|
||||
|
||||
MRB_API mrb_noreturn void
|
||||
mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, char const* fmt, ...)
|
||||
{
|
||||
mrb_value exc;
|
||||
mrb_value argv[3];
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
argv[0] = mrb_vformat(mrb, fmt, ap);
|
||||
argv[1] = mrb_symbol_value(id);
|
||||
argv[2] = args;
|
||||
va_end(ap);
|
||||
exc = mrb_obj_new(mrb, E_NOMETHOD_ERROR, 3, argv);
|
||||
mrb_exc_raise(mrb, exc);
|
||||
}
|
||||
|
||||
MRB_API mrb_noreturn void
|
||||
mrb_frozen_error(mrb_state *mrb, void *frozen_obj)
|
||||
{
|
||||
mrb_raisef(mrb, E_FROZEN_ERROR, "can't modify frozen %t", mrb_obj_value(frozen_obj));
|
||||
}
|
||||
|
||||
MRB_API mrb_noreturn void
|
||||
mrb_argnum_error(mrb_state *mrb, mrb_int argc, int min, int max)
|
||||
{
|
||||
#define FMT(exp) "wrong number of arguments (given %i, expected " exp ")"
|
||||
if (min == max)
|
||||
mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d"), argc, min);
|
||||
else if (max < 0)
|
||||
mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d+"), argc, min);
|
||||
else
|
||||
mrb_raisef(mrb, E_ARGUMENT_ERROR, FMT("%d..%d"), argc, min, max);
|
||||
#undef FMT
|
||||
}
|
||||
|
||||
void mrb_core_init_printabort(void);
|
||||
|
||||
int
|
||||
mrb_core_init_protect(mrb_state *mrb, void (*body)(mrb_state *, void *), void *opaque)
|
||||
{
|
||||
struct mrb_jmpbuf *prev_jmp = mrb->jmp;
|
||||
struct mrb_jmpbuf c_jmp;
|
||||
int err = 1;
|
||||
|
||||
MRB_TRY(&c_jmp) {
|
||||
mrb->jmp = &c_jmp;
|
||||
body(mrb, opaque);
|
||||
err = 0;
|
||||
} MRB_CATCH(&c_jmp) {
|
||||
if (mrb->exc) {
|
||||
mrb_p(mrb, mrb_obj_value(mrb->exc));
|
||||
mrb->exc = NULL;
|
||||
}
|
||||
else {
|
||||
mrb_core_init_printabort();
|
||||
}
|
||||
} MRB_END_EXC(&c_jmp);
|
||||
|
||||
mrb->jmp = prev_jmp;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
mrb_noreturn void
|
||||
mrb_core_init_abort(mrb_state *mrb)
|
||||
{
|
||||
mrb->exc = NULL;
|
||||
exc_throw(mrb, mrb_nil_value());
|
||||
}
|
||||
|
||||
mrb_noreturn void
|
||||
mrb_raise_nomemory(mrb_state *mrb)
|
||||
{
|
||||
if (mrb->nomem_err) {
|
||||
mrb_exc_raise(mrb, mrb_obj_value(mrb->nomem_err));
|
||||
}
|
||||
else {
|
||||
mrb_core_init_abort(mrb);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mrb_init_exception(mrb_state *mrb)
|
||||
{
|
||||
struct RClass *exception, *script_error, *stack_error, *nomem_error;
|
||||
|
||||
mrb->eException_class = exception = mrb_define_class(mrb, "Exception", mrb->object_class); /* 15.2.22 */
|
||||
MRB_SET_INSTANCE_TT(exception, MRB_TT_EXCEPTION);
|
||||
mrb_define_class_method(mrb, exception, "exception", mrb_instance_new, MRB_ARGS_OPT(1));
|
||||
mrb_define_method(mrb, exception, "exception", exc_exception, MRB_ARGS_OPT(1));
|
||||
mrb_define_method(mrb, exception, "initialize", exc_initialize, MRB_ARGS_OPT(1));
|
||||
mrb_define_method(mrb, exception, "to_s", exc_to_s, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, exception, "message", exc_message, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, exception, "inspect", mrb_exc_inspect, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, exception, "backtrace", mrb_exc_backtrace, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, exception, "set_backtrace", exc_set_backtrace, MRB_ARGS_REQ(1));
|
||||
|
||||
mrb->eStandardError_class = mrb_define_class(mrb, "StandardError", mrb->eException_class); /* 15.2.23 */
|
||||
mrb_define_class(mrb, "RuntimeError", mrb->eStandardError_class); /* 15.2.28 */
|
||||
script_error = mrb_define_class(mrb, "ScriptError", mrb->eException_class); /* 15.2.37 */
|
||||
mrb_define_class(mrb, "SyntaxError", script_error); /* 15.2.38 */
|
||||
stack_error = mrb_define_class(mrb, "SystemStackError", exception);
|
||||
mrb->stack_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, stack_error, "stack level too deep"));
|
||||
|
||||
nomem_error = mrb_define_class(mrb, "NoMemoryError", exception);
|
||||
mrb->nomem_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, nomem_error, "Out of memory"));
|
||||
#ifdef MRB_GC_FIXED_ARENA
|
||||
mrb->arena_err = mrb_obj_ptr(mrb_exc_new_str_lit(mrb, nomem_error, "arena overflow error"));
|
||||
#endif
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
/* this header file is to be removed soon.
|
||||
added for compatibility purpose (1.0.0) */
|
||||
#include <mruby/error.h>
|
||||
Vendored
+222
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
** etc.c
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
*/
|
||||
|
||||
#include <mruby.h>
|
||||
#include <mruby/string.h>
|
||||
#include <mruby/data.h>
|
||||
#include <mruby/class.h>
|
||||
|
||||
MRB_API struct RData*
|
||||
mrb_data_object_alloc(mrb_state *mrb, struct RClass *klass, void *ptr, const mrb_data_type *type)
|
||||
{
|
||||
struct RData *data;
|
||||
|
||||
data = (struct RData*)mrb_obj_alloc(mrb, MRB_TT_DATA, klass);
|
||||
data->data = ptr;
|
||||
data->type = type;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
MRB_API void
|
||||
mrb_data_check_type(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
|
||||
{
|
||||
if (!mrb_data_p(obj)) {
|
||||
mrb_check_type(mrb, obj, MRB_TT_DATA);
|
||||
}
|
||||
if (DATA_TYPE(obj) != type) {
|
||||
const mrb_data_type *t2 = DATA_TYPE(obj);
|
||||
|
||||
if (t2) {
|
||||
mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %s (expected %s)",
|
||||
t2->struct_name, type->struct_name);
|
||||
}
|
||||
else {
|
||||
mrb_raisef(mrb, E_TYPE_ERROR, "uninitialized %t (expected %s)",
|
||||
obj, type->struct_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MRB_API void*
|
||||
mrb_data_check_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
|
||||
{
|
||||
if (!mrb_data_p(obj)) {
|
||||
return NULL;
|
||||
}
|
||||
if (DATA_TYPE(obj) != type) {
|
||||
return NULL;
|
||||
}
|
||||
return DATA_PTR(obj);
|
||||
}
|
||||
|
||||
MRB_API void*
|
||||
mrb_data_get_ptr(mrb_state *mrb, mrb_value obj, const mrb_data_type *type)
|
||||
{
|
||||
mrb_data_check_type(mrb, obj, type);
|
||||
return DATA_PTR(obj);
|
||||
}
|
||||
|
||||
MRB_API mrb_sym
|
||||
mrb_obj_to_sym(mrb_state *mrb, mrb_value name)
|
||||
{
|
||||
if (mrb_symbol_p(name)) return mrb_symbol(name);
|
||||
if (mrb_string_p(name)) return mrb_intern_str(mrb, name);
|
||||
mrb_raisef(mrb, E_TYPE_ERROR, "%!v is not a symbol nor a string", name);
|
||||
return 0; /* not reached */
|
||||
}
|
||||
|
||||
MRB_API mrb_int
|
||||
#ifdef MRB_WITHOUT_FLOAT
|
||||
mrb_fixnum_id(mrb_int f)
|
||||
#else
|
||||
mrb_float_id(mrb_float f)
|
||||
#endif
|
||||
{
|
||||
const char *p = (const char*)&f;
|
||||
int len = sizeof(f);
|
||||
uint32_t id = 0;
|
||||
|
||||
#ifndef MRB_WITHOUT_FLOAT
|
||||
/* normalize -0.0 to 0.0 */
|
||||
if (f == 0) f = 0.0;
|
||||
#endif
|
||||
while (len--) {
|
||||
id = id*65599 + *p;
|
||||
p++;
|
||||
}
|
||||
id = id + (id>>5);
|
||||
|
||||
return (mrb_int)id;
|
||||
}
|
||||
|
||||
MRB_API mrb_int
|
||||
mrb_obj_id(mrb_value obj)
|
||||
{
|
||||
mrb_int tt = mrb_type(obj);
|
||||
|
||||
#define MakeID2(p,t) (mrb_int)(((intptr_t)(p))^(t))
|
||||
#define MakeID(p) MakeID2(p,tt)
|
||||
|
||||
switch (tt) {
|
||||
case MRB_TT_FREE:
|
||||
case MRB_TT_UNDEF:
|
||||
return MakeID(0); /* not define */
|
||||
case MRB_TT_FALSE:
|
||||
if (mrb_nil_p(obj))
|
||||
return MakeID(1);
|
||||
return MakeID(0);
|
||||
case MRB_TT_TRUE:
|
||||
return MakeID(1);
|
||||
case MRB_TT_SYMBOL:
|
||||
return MakeID(mrb_symbol(obj));
|
||||
case MRB_TT_FIXNUM:
|
||||
#ifdef MRB_WITHOUT_FLOAT
|
||||
return MakeID(mrb_fixnum_id(mrb_fixnum(obj)));
|
||||
#else
|
||||
return MakeID2(mrb_float_id((mrb_float)mrb_fixnum(obj)), MRB_TT_FLOAT);
|
||||
case MRB_TT_FLOAT:
|
||||
return MakeID(mrb_float_id(mrb_float(obj)));
|
||||
#endif
|
||||
case MRB_TT_STRING:
|
||||
case MRB_TT_OBJECT:
|
||||
case MRB_TT_CLASS:
|
||||
case MRB_TT_MODULE:
|
||||
case MRB_TT_ICLASS:
|
||||
case MRB_TT_SCLASS:
|
||||
case MRB_TT_PROC:
|
||||
case MRB_TT_ARRAY:
|
||||
case MRB_TT_HASH:
|
||||
case MRB_TT_RANGE:
|
||||
case MRB_TT_EXCEPTION:
|
||||
case MRB_TT_DATA:
|
||||
case MRB_TT_ISTRUCT:
|
||||
default:
|
||||
return MakeID(mrb_ptr(obj));
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(MRB_NAN_BOXING) && defined(MRB_64BIT)
|
||||
#define mrb_xxx_boxing_cptr_value mrb_nan_boxing_cptr_value
|
||||
#endif
|
||||
|
||||
#ifdef MRB_WORD_BOXING
|
||||
#define mrb_xxx_boxing_cptr_value mrb_word_boxing_cptr_value
|
||||
|
||||
#ifndef MRB_WITHOUT_FLOAT
|
||||
MRB_API mrb_value
|
||||
mrb_word_boxing_float_value(mrb_state *mrb, mrb_float f)
|
||||
{
|
||||
mrb_value v;
|
||||
|
||||
v.value.p = mrb_obj_alloc(mrb, MRB_TT_FLOAT, mrb->float_class);
|
||||
v.value.fp->f = f;
|
||||
MRB_SET_FROZEN_FLAG(v.value.bp);
|
||||
return v;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_word_boxing_float_pool(mrb_state *mrb, mrb_float f)
|
||||
{
|
||||
struct RFloat *nf = (struct RFloat *)mrb_malloc(mrb, sizeof(struct RFloat));
|
||||
nf->tt = MRB_TT_FLOAT;
|
||||
nf->c = mrb->float_class;
|
||||
nf->f = f;
|
||||
MRB_SET_FROZEN_FLAG(nf);
|
||||
return mrb_obj_value(nf);
|
||||
}
|
||||
#endif /* MRB_WITHOUT_FLOAT */
|
||||
#endif /* MRB_WORD_BOXING */
|
||||
|
||||
#if defined(MRB_WORD_BOXING) || (defined(MRB_NAN_BOXING) && defined(MRB_64BIT))
|
||||
MRB_API mrb_value
|
||||
mrb_xxx_boxing_cptr_value(mrb_state *mrb, void *p)
|
||||
{
|
||||
mrb_value v;
|
||||
struct RCptr *cptr = (struct RCptr*)mrb_obj_alloc(mrb, MRB_TT_CPTR, mrb->object_class);
|
||||
|
||||
SET_OBJ_VALUE(v, cptr);
|
||||
cptr->p = p;
|
||||
return v;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined _MSC_VER && _MSC_VER < 1900
|
||||
|
||||
#ifndef va_copy
|
||||
static void
|
||||
mrb_msvc_va_copy(va_list *dest, va_list src)
|
||||
{
|
||||
*dest = src;
|
||||
}
|
||||
#define va_copy(dest, src) mrb_msvc_va_copy(&(dest), src)
|
||||
#endif
|
||||
|
||||
MRB_API int
|
||||
mrb_msvc_vsnprintf(char *s, size_t n, const char *format, va_list arg)
|
||||
{
|
||||
int cnt;
|
||||
va_list argcp;
|
||||
va_copy(argcp, arg);
|
||||
if (n == 0 || (cnt = _vsnprintf_s(s, n, _TRUNCATE, format, argcp)) < 0) {
|
||||
cnt = _vscprintf(format, arg);
|
||||
}
|
||||
va_end(argcp);
|
||||
return cnt;
|
||||
}
|
||||
|
||||
MRB_API int
|
||||
mrb_msvc_snprintf(char *s, size_t n, const char *format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
int ret;
|
||||
va_start(arg, format);
|
||||
ret = mrb_msvc_vsnprintf(s, n, format, arg);
|
||||
va_end(arg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* defined _MSC_VER && _MSC_VER < 1900 */
|
||||
+464
@@ -0,0 +1,464 @@
|
||||
#ifndef MRB_WITHOUT_FLOAT
|
||||
#if defined(MRB_DISABLE_STDIO) || defined(_WIN32) || defined(_WIN64)
|
||||
/*
|
||||
|
||||
Most code in this file originates from musl (src/stdio/vfprintf.c)
|
||||
which, just like mruby itself, is licensed under the MIT license.
|
||||
|
||||
Copyright (c) 2005-2014 Rich Felker, et al.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <float.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <mruby.h>
|
||||
#include <mruby/string.h>
|
||||
|
||||
struct fmt_args;
|
||||
|
||||
typedef void output_func(struct fmt_args *f, const char *s, size_t l);
|
||||
|
||||
struct fmt_args {
|
||||
mrb_state *mrb;
|
||||
output_func *output;
|
||||
void *opaque;
|
||||
};
|
||||
|
||||
struct mrb_cstr {
|
||||
char *buf;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
#define MAX(a,b) ((a)>(b) ? (a) : (b))
|
||||
#define MIN(a,b) ((a)<(b) ? (a) : (b))
|
||||
|
||||
/* Convenient bit representation for modifier flags, which all fall
|
||||
* within 31 codepoints of the space character. */
|
||||
|
||||
#define ALT_FORM (1U<<('#'-' '))
|
||||
#define ZERO_PAD (1U<<('0'-' '))
|
||||
#define LEFT_ADJ (1U<<('-'-' '))
|
||||
#define PAD_POS (1U<<(' '-' '))
|
||||
#define MARK_POS (1U<<('+'-' '))
|
||||
|
||||
#define FLAGMASK (ALT_FORM|ZERO_PAD|LEFT_ADJ|PAD_POS|MARK_POS)
|
||||
|
||||
static output_func strcat_value;
|
||||
static output_func strcat_cstr;
|
||||
|
||||
static void
|
||||
strcat_value(struct fmt_args *f, const char *s, size_t l)
|
||||
{
|
||||
mrb_value str = *(mrb_value*)f->opaque;
|
||||
mrb_str_cat(f->mrb, str, s, l);
|
||||
}
|
||||
|
||||
static void
|
||||
strcat_cstr(struct fmt_args *f, const char *s, size_t l)
|
||||
{
|
||||
struct mrb_cstr *cstr = (struct mrb_cstr*)f->opaque;
|
||||
|
||||
if (l > cstr->len) {
|
||||
mrb_state *mrb = f->mrb;
|
||||
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "string buffer too small");
|
||||
}
|
||||
|
||||
memcpy(cstr->buf, s, l);
|
||||
|
||||
cstr->buf += l;
|
||||
cstr->len -= l;
|
||||
}
|
||||
|
||||
static void
|
||||
out(struct fmt_args *f, const char *s, size_t l)
|
||||
{
|
||||
f->output(f, s, l);
|
||||
}
|
||||
|
||||
#define PAD_SIZE 256
|
||||
static void
|
||||
pad(struct fmt_args *f, char c, ptrdiff_t w, ptrdiff_t l, uint32_t fl)
|
||||
{
|
||||
char pad[PAD_SIZE];
|
||||
if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return;
|
||||
l = w - l;
|
||||
memset(pad, c, l>PAD_SIZE ? PAD_SIZE : l);
|
||||
for (; l >= PAD_SIZE; l -= PAD_SIZE)
|
||||
out(f, pad, PAD_SIZE);
|
||||
out(f, pad, l);
|
||||
}
|
||||
|
||||
static const char xdigits[16] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
|
||||
};
|
||||
|
||||
static char*
|
||||
fmt_u(uint32_t x, char *s)
|
||||
{
|
||||
for (; x; x /= 10) *--s = '0' + x % 10;
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Do not override this check. The floating point printing code below
|
||||
* depends on the float.h constants being right. If they are wrong, it
|
||||
* may overflow the stack. */
|
||||
#if LDBL_MANT_DIG == 53
|
||||
typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)];
|
||||
#endif
|
||||
|
||||
static int
|
||||
fmt_fp(struct fmt_args *f, long double y, ptrdiff_t w, ptrdiff_t p, uint32_t fl, int t)
|
||||
{
|
||||
uint32_t big[(LDBL_MANT_DIG+28)/29 + 1 // mantissa expansion
|
||||
+ (LDBL_MAX_EXP+LDBL_MANT_DIG+28+8)/9]; // exponent expansion
|
||||
uint32_t *a, *d, *r, *z;
|
||||
uint32_t i;
|
||||
int e2=0, e, j;
|
||||
ptrdiff_t l;
|
||||
char buf[9+LDBL_MANT_DIG/4], *s;
|
||||
const char *prefix="-0X+0X 0X-0x+0x 0x";
|
||||
ptrdiff_t pl;
|
||||
char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr;
|
||||
|
||||
pl=1;
|
||||
if (signbit(y)) {
|
||||
y=-y;
|
||||
} else if (fl & MARK_POS) {
|
||||
prefix+=3;
|
||||
} else if (fl & PAD_POS) {
|
||||
prefix+=6;
|
||||
} else prefix++, pl=0;
|
||||
|
||||
if (!isfinite(y)) {
|
||||
const char *ss = (t&32)?"inf":"INF";
|
||||
if (y!=y) ss=(t&32)?"nan":"NAN";
|
||||
pad(f, ' ', w, 3+pl, fl&~ZERO_PAD);
|
||||
out(f, prefix, pl);
|
||||
out(f, ss, 3);
|
||||
pad(f, ' ', w, 3+pl, fl^LEFT_ADJ);
|
||||
return (int)MAX(w, 3+pl);
|
||||
}
|
||||
|
||||
y = frexp((double)y, &e2) * 2;
|
||||
if (y) e2--;
|
||||
|
||||
if ((t|32)=='a') {
|
||||
long double round = 8.0;
|
||||
ptrdiff_t re;
|
||||
|
||||
if (t&32) prefix += 9;
|
||||
pl += 2;
|
||||
|
||||
if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0;
|
||||
else re=LDBL_MANT_DIG/4-1-p;
|
||||
|
||||
if (re) {
|
||||
while (re--) round*=16;
|
||||
if (*prefix=='-') {
|
||||
y=-y;
|
||||
y-=round;
|
||||
y+=round;
|
||||
y=-y;
|
||||
}
|
||||
else {
|
||||
y+=round;
|
||||
y-=round;
|
||||
}
|
||||
}
|
||||
|
||||
estr=fmt_u(e2<0 ? -e2 : e2, ebuf);
|
||||
if (estr==ebuf) *--estr='0';
|
||||
*--estr = (e2<0 ? '-' : '+');
|
||||
*--estr = t+('p'-'a');
|
||||
|
||||
s=buf;
|
||||
do {
|
||||
int x=(int)y;
|
||||
*s++=xdigits[x]|(t&32);
|
||||
y=16*(y-x);
|
||||
if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.';
|
||||
} while (y);
|
||||
|
||||
if (p && s-buf-2 < p)
|
||||
l = (p+2) + (ebuf-estr);
|
||||
else
|
||||
l = (s-buf) + (ebuf-estr);
|
||||
|
||||
pad(f, ' ', w, pl+l, fl);
|
||||
out(f, prefix, pl);
|
||||
pad(f, '0', w, pl+l, fl^ZERO_PAD);
|
||||
out(f, buf, s-buf);
|
||||
pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0);
|
||||
out(f, estr, ebuf-estr);
|
||||
pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
|
||||
return (int)MAX(w, pl+l);
|
||||
}
|
||||
if (p<0) p=6;
|
||||
|
||||
if (y) y *= 268435456.0, e2-=28;
|
||||
|
||||
if (e2<0) a=r=z=big;
|
||||
else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1;
|
||||
|
||||
do {
|
||||
*z = (uint32_t)y;
|
||||
y = 1000000000*(y-*z++);
|
||||
} while (y);
|
||||
|
||||
while (e2>0) {
|
||||
uint32_t carry=0;
|
||||
int sh=MIN(29,e2);
|
||||
for (d=z-1; d>=a; d--) {
|
||||
uint64_t x = ((uint64_t)*d<<sh)+carry;
|
||||
*d = x % 1000000000;
|
||||
carry = (uint32_t)(x / 1000000000);
|
||||
}
|
||||
if (carry) *--a = carry;
|
||||
while (z>a && !z[-1]) z--;
|
||||
e2-=sh;
|
||||
}
|
||||
while (e2<0) {
|
||||
uint32_t carry=0, *b;
|
||||
int sh=MIN(9,-e2), need=1+((int)p+LDBL_MANT_DIG/3+8)/9;
|
||||
for (d=a; d<z; d++) {
|
||||
uint32_t rm = *d & ((1<<sh)-1);
|
||||
*d = (*d>>sh) + carry;
|
||||
carry = (1000000000>>sh) * rm;
|
||||
}
|
||||
if (!*a) a++;
|
||||
if (carry) *z++ = carry;
|
||||
/* Avoid (slow!) computation past requested precision */
|
||||
b = (t|32)=='f' ? r : a;
|
||||
if (z-b > need) z = b+need;
|
||||
e2+=sh;
|
||||
}
|
||||
|
||||
if (a<z) for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++);
|
||||
else e=0;
|
||||
|
||||
/* Perform rounding: j is precision after the radix (possibly neg) */
|
||||
j = (int)p - ((t|32)!='f')*e - ((t|32)=='g' && p);
|
||||
if (j < 9*(z-r-1)) {
|
||||
uint32_t x;
|
||||
/* We avoid C's broken division of negative numbers */
|
||||
d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP);
|
||||
j += 9*LDBL_MAX_EXP;
|
||||
j %= 9;
|
||||
for (i=10, j++; j<9; i*=10, j++);
|
||||
x = *d % i;
|
||||
/* Are there any significant digits past j? */
|
||||
if (x || d+1!=z) {
|
||||
long double round = 2/LDBL_EPSILON;
|
||||
long double small;
|
||||
if (*d/i & 1) round += 2;
|
||||
if (x<i/2) small=0.5;
|
||||
else if (x==i/2 && d+1==z) small=1.0;
|
||||
else small=1.5;
|
||||
if (pl && *prefix=='-') round*=-1, small*=-1;
|
||||
*d -= x;
|
||||
/* Decide whether to round by probing round+small */
|
||||
if (round+small != round) {
|
||||
*d = *d + i;
|
||||
while (*d > 999999999) {
|
||||
*d--=0;
|
||||
if (d<a) *--a=0;
|
||||
(*d)++;
|
||||
}
|
||||
for (i=10, e=9*(int)(r-a); *a>=i; i*=10, e++);
|
||||
}
|
||||
}
|
||||
if (z>d+1) z=d+1;
|
||||
}
|
||||
for (; z>a && !z[-1]; z--);
|
||||
|
||||
if ((t|32)=='g') {
|
||||
if (!p) p++;
|
||||
if (p>e && e>=-4) {
|
||||
t--;
|
||||
p-=e+1;
|
||||
}
|
||||
else {
|
||||
t-=2;
|
||||
p--;
|
||||
}
|
||||
if (!(fl&ALT_FORM)) {
|
||||
/* Count trailing zeros in last place */
|
||||
if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++);
|
||||
else j=9;
|
||||
if ((t|32)=='f')
|
||||
p = MIN(p,MAX(0,9*(z-r-1)-j));
|
||||
else
|
||||
p = MIN(p,MAX(0,9*(z-r-1)+e-j));
|
||||
}
|
||||
}
|
||||
l = 1 + p + (p || (fl&ALT_FORM));
|
||||
if ((t|32)=='f') {
|
||||
if (e>0) l+=e;
|
||||
}
|
||||
else {
|
||||
estr=fmt_u(e<0 ? -e : e, ebuf);
|
||||
while(ebuf-estr<2) *--estr='0';
|
||||
*--estr = (e<0 ? '-' : '+');
|
||||
*--estr = t;
|
||||
l += ebuf-estr;
|
||||
}
|
||||
|
||||
pad(f, ' ', w, pl+l, fl);
|
||||
out(f, prefix, pl);
|
||||
pad(f, '0', w, pl+l, fl^ZERO_PAD);
|
||||
|
||||
if ((t|32)=='f') {
|
||||
if (a>r) a=r;
|
||||
for (d=a; d<=r; d++) {
|
||||
char *ss = fmt_u(*d, buf+9);
|
||||
if (d!=a) while (ss>buf) *--ss='0';
|
||||
else if (ss==buf+9) *--ss='0';
|
||||
out(f, ss, buf+9-ss);
|
||||
}
|
||||
if (p || (fl&ALT_FORM)) out(f, ".", 1);
|
||||
for (; d<z && p>0; d++, p-=9) {
|
||||
char *ss = fmt_u(*d, buf+9);
|
||||
while (ss>buf) *--ss='0';
|
||||
out(f, ss, MIN(9,p));
|
||||
}
|
||||
pad(f, '0', p+9, 9, 0);
|
||||
}
|
||||
else {
|
||||
if (z<=a) z=a+1;
|
||||
for (d=a; d<z && p>=0; d++) {
|
||||
char *ss = fmt_u(*d, buf+9);
|
||||
if (ss==buf+9) *--ss='0';
|
||||
if (d!=a) while (ss>buf) *--ss='0';
|
||||
else {
|
||||
out(f, ss++, 1);
|
||||
if (p>0||(fl&ALT_FORM)) out(f, ".", 1);
|
||||
}
|
||||
out(f, ss, MIN(buf+9-ss, p));
|
||||
p -= (int)(buf+9-ss);
|
||||
}
|
||||
pad(f, '0', p+18, 18, 0);
|
||||
out(f, estr, ebuf-estr);
|
||||
}
|
||||
|
||||
pad(f, ' ', w, pl+l, fl^LEFT_ADJ);
|
||||
|
||||
return (int)MAX(w, pl+l);
|
||||
}
|
||||
|
||||
static int
|
||||
fmt_core(struct fmt_args *f, const char *fmt, mrb_float flo)
|
||||
{
|
||||
ptrdiff_t w, p;
|
||||
uint32_t fl;
|
||||
|
||||
if (*fmt != '%') {
|
||||
return -1;
|
||||
}
|
||||
++fmt;
|
||||
|
||||
/* Read modifier flags */
|
||||
for (fl=0; (unsigned)*fmt-' '<32 && (FLAGMASK&(1U<<(*fmt-' '))); fmt++)
|
||||
fl |= 1U<<(*fmt-' ');
|
||||
|
||||
/* - and 0 flags are mutually exclusive */
|
||||
if (fl & LEFT_ADJ) fl &= ~ZERO_PAD;
|
||||
|
||||
for (w = 0; ISDIGIT(*fmt); ++fmt) {
|
||||
w = 10 * w + (*fmt - '0');
|
||||
}
|
||||
|
||||
if (*fmt == '.') {
|
||||
++fmt;
|
||||
for (p = 0; ISDIGIT(*fmt); ++fmt) {
|
||||
p = 10 * p + (*fmt - '0');
|
||||
}
|
||||
}
|
||||
else {
|
||||
p = -1;
|
||||
}
|
||||
|
||||
switch (*fmt) {
|
||||
case 'e': case 'f': case 'g': case 'a':
|
||||
case 'E': case 'F': case 'G': case 'A':
|
||||
return fmt_fp(f, flo, w, p, fl, *fmt);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
|
||||
{
|
||||
struct fmt_args f;
|
||||
mrb_value str = mrb_str_new_capa(mrb, 24);
|
||||
|
||||
f.mrb = mrb;
|
||||
f.output = strcat_value;
|
||||
f.opaque = (void*)&str;
|
||||
if (fmt_core(&f, fmt, mrb_float(flo)) < 0) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string");
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
MRB_API int
|
||||
mrb_float_to_cstr(mrb_state *mrb, char *buf, size_t len, const char *fmt, mrb_float fval)
|
||||
{
|
||||
struct fmt_args f;
|
||||
struct mrb_cstr cstr;
|
||||
|
||||
cstr.buf = buf;
|
||||
cstr.len = len - 1; /* reserve NUL terminator */
|
||||
f.mrb = mrb;
|
||||
f.output = strcat_cstr;
|
||||
f.opaque = (void*)&cstr;
|
||||
if (fmt_core(&f, fmt, fval) < 0) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format string");
|
||||
}
|
||||
*cstr.buf = '\0';
|
||||
return (int)(cstr.buf - buf);
|
||||
}
|
||||
#else /* MRB_DISABLE_STDIO || _WIN32 || _WIN64 */
|
||||
#include <mruby.h>
|
||||
#include <stdio.h>
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_float_to_str(mrb_state *mrb, mrb_value flo, const char *fmt)
|
||||
{
|
||||
char buf[25];
|
||||
|
||||
snprintf(buf, sizeof(buf), fmt, mrb_float(flo));
|
||||
return mrb_str_new_cstr(mrb, buf);
|
||||
}
|
||||
|
||||
MRB_API int
|
||||
mrb_float_to_cstr(mrb_state *mrb, char *buf, size_t len, const char *fmt, mrb_float fval)
|
||||
{
|
||||
return snprintf(buf, len, fmt, fval);
|
||||
}
|
||||
#endif /* MRB_DISABLE_STDIO || _WIN32 || _WIN64 */
|
||||
#endif
|
||||
Vendored
+1638
File diff suppressed because it is too large
Load Diff
Vendored
+1429
File diff suppressed because it is too large
Load Diff
Vendored
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
** init.c - initialize mruby core
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
*/
|
||||
|
||||
#include <mruby.h>
|
||||
|
||||
void mrb_init_symtbl(mrb_state*);
|
||||
void mrb_init_class(mrb_state*);
|
||||
void mrb_init_object(mrb_state*);
|
||||
void mrb_init_kernel(mrb_state*);
|
||||
void mrb_init_comparable(mrb_state*);
|
||||
void mrb_init_enumerable(mrb_state*);
|
||||
void mrb_init_symbol(mrb_state*);
|
||||
void mrb_init_string(mrb_state*);
|
||||
void mrb_init_exception(mrb_state*);
|
||||
void mrb_init_proc(mrb_state*);
|
||||
void mrb_init_array(mrb_state*);
|
||||
void mrb_init_hash(mrb_state*);
|
||||
void mrb_init_numeric(mrb_state*);
|
||||
void mrb_init_range(mrb_state*);
|
||||
void mrb_init_gc(mrb_state*);
|
||||
void mrb_init_math(mrb_state*);
|
||||
void mrb_init_version(mrb_state*);
|
||||
void mrb_init_mrblib(mrb_state*);
|
||||
|
||||
#define DONE mrb_gc_arena_restore(mrb, 0);
|
||||
void
|
||||
mrb_init_core(mrb_state *mrb)
|
||||
{
|
||||
mrb_init_symtbl(mrb); DONE;
|
||||
|
||||
mrb_init_class(mrb); DONE;
|
||||
mrb_init_object(mrb); DONE;
|
||||
mrb_init_kernel(mrb); DONE;
|
||||
mrb_init_comparable(mrb); DONE;
|
||||
mrb_init_enumerable(mrb); DONE;
|
||||
|
||||
mrb_init_symbol(mrb); DONE;
|
||||
mrb_init_string(mrb); DONE;
|
||||
mrb_init_exception(mrb); DONE;
|
||||
mrb_init_proc(mrb); DONE;
|
||||
mrb_init_array(mrb); DONE;
|
||||
mrb_init_hash(mrb); DONE;
|
||||
mrb_init_numeric(mrb); DONE;
|
||||
mrb_init_range(mrb); DONE;
|
||||
mrb_init_gc(mrb); DONE;
|
||||
mrb_init_version(mrb); DONE;
|
||||
mrb_init_mrblib(mrb); DONE;
|
||||
}
|
||||
+801
@@ -0,0 +1,801 @@
|
||||
/*
|
||||
** kernel.c - Kernel module
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
*/
|
||||
|
||||
#include <mruby.h>
|
||||
#include <mruby/array.h>
|
||||
#include <mruby/hash.h>
|
||||
#include <mruby/class.h>
|
||||
#include <mruby/proc.h>
|
||||
#include <mruby/string.h>
|
||||
#include <mruby/variable.h>
|
||||
#include <mruby/error.h>
|
||||
#include <mruby/istruct.h>
|
||||
|
||||
MRB_API mrb_bool
|
||||
mrb_func_basic_p(mrb_state *mrb, mrb_value obj, mrb_sym mid, mrb_func_t func)
|
||||
{
|
||||
struct RClass *c = mrb_class(mrb, obj);
|
||||
mrb_method_t m = mrb_method_search_vm(mrb, &c, mid);
|
||||
struct RProc *p;
|
||||
|
||||
if (MRB_METHOD_UNDEF_P(m)) return FALSE;
|
||||
if (MRB_METHOD_FUNC_P(m))
|
||||
return MRB_METHOD_FUNC(m) == func;
|
||||
p = MRB_METHOD_PROC(m);
|
||||
if (MRB_PROC_CFUNC_P(p) && (MRB_PROC_CFUNC(p) == func))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
mrb_obj_basic_to_s_p(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
return mrb_func_basic_p(mrb, obj, mrb_intern_lit(mrb, "to_s"), mrb_any_to_s);
|
||||
}
|
||||
|
||||
/* 15.3.1.3.17 */
|
||||
/*
|
||||
* call-seq:
|
||||
* obj.inspect -> string
|
||||
*
|
||||
* Returns a string containing a human-readable representation of
|
||||
* <i>obj</i>. If not overridden and no instance variables, uses the
|
||||
* <code>to_s</code> method to generate the string.
|
||||
* <i>obj</i>. If not overridden, uses the <code>to_s</code> method to
|
||||
* generate the string.
|
||||
*
|
||||
* [ 1, 2, 3..4, 'five' ].inspect #=> "[1, 2, 3..4, \"five\"]"
|
||||
* Time.new.inspect #=> "2008-03-08 19:43:39 +0900"
|
||||
*/
|
||||
MRB_API mrb_value
|
||||
mrb_obj_inspect(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
if (mrb_object_p(obj) && mrb_obj_basic_to_s_p(mrb, obj)) {
|
||||
return mrb_obj_iv_inspect(mrb, mrb_obj_ptr(obj));
|
||||
}
|
||||
return mrb_any_to_s(mrb, obj);
|
||||
}
|
||||
|
||||
/* 15.3.1.3.2 */
|
||||
/*
|
||||
* call-seq:
|
||||
* obj === other -> true or false
|
||||
*
|
||||
* Case Equality---For class <code>Object</code>, effectively the same
|
||||
* as calling <code>#==</code>, but typically overridden by descendants
|
||||
* to provide meaningful semantics in <code>case</code> statements.
|
||||
*/
|
||||
static mrb_value
|
||||
mrb_equal_m(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value arg = mrb_get_arg1(mrb);
|
||||
|
||||
return mrb_bool_value(mrb_equal(mrb, self, arg));
|
||||
}
|
||||
|
||||
/* 15.3.1.3.3 */
|
||||
/* 15.3.1.3.33 */
|
||||
/*
|
||||
* Document-method: __id__
|
||||
* Document-method: object_id
|
||||
*
|
||||
* call-seq:
|
||||
* obj.__id__ -> fixnum
|
||||
* obj.object_id -> fixnum
|
||||
*
|
||||
* Returns an integer identifier for <i>obj</i>. The same number will
|
||||
* be returned on all calls to <code>id</code> for a given object, and
|
||||
* no two active objects will share an id.
|
||||
* <code>Object#object_id</code> is a different concept from the
|
||||
* <code>:name</code> notation, which returns the symbol id of
|
||||
* <code>name</code>. Replaces the deprecated <code>Object#id</code>.
|
||||
*/
|
||||
mrb_value
|
||||
mrb_obj_id_m(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
return mrb_fixnum_value(mrb_obj_id(self));
|
||||
}
|
||||
|
||||
/* 15.3.1.2.2 */
|
||||
/* 15.3.1.2.5 */
|
||||
/* 15.3.1.3.6 */
|
||||
/* 15.3.1.3.25 */
|
||||
/*
|
||||
* call-seq:
|
||||
* block_given? -> true or false
|
||||
* iterator? -> true or false
|
||||
*
|
||||
* Returns <code>true</code> if <code>yield</code> would execute a
|
||||
* block in the current context. The <code>iterator?</code> form
|
||||
* is mildly deprecated.
|
||||
*
|
||||
* def try
|
||||
* if block_given?
|
||||
* yield
|
||||
* else
|
||||
* "no block"
|
||||
* end
|
||||
* end
|
||||
* try #=> "no block"
|
||||
* try { "hello" } #=> "hello"
|
||||
* try do "hello" end #=> "hello"
|
||||
*/
|
||||
static mrb_value
|
||||
mrb_f_block_given_p_m(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_callinfo *ci = &mrb->c->ci[-1];
|
||||
mrb_callinfo *cibase = mrb->c->cibase;
|
||||
mrb_value *bp;
|
||||
struct RProc *p;
|
||||
|
||||
if (ci <= cibase) {
|
||||
/* toplevel does not have block */
|
||||
return mrb_false_value();
|
||||
}
|
||||
p = ci->proc;
|
||||
/* search method/class/module proc */
|
||||
while (p) {
|
||||
if (MRB_PROC_SCOPE_P(p)) break;
|
||||
p = p->upper;
|
||||
}
|
||||
if (p == NULL) return mrb_false_value();
|
||||
/* search ci corresponding to proc */
|
||||
while (cibase < ci) {
|
||||
if (ci->proc == p) break;
|
||||
ci--;
|
||||
}
|
||||
if (ci == cibase) {
|
||||
return mrb_false_value();
|
||||
}
|
||||
else if (ci->env) {
|
||||
struct REnv *e = ci->env;
|
||||
int bidx;
|
||||
|
||||
/* top-level does not have block slot (always false) */
|
||||
if (e->stack == mrb->c->stbase)
|
||||
return mrb_false_value();
|
||||
/* use saved block arg position */
|
||||
bidx = MRB_ENV_BIDX(e);
|
||||
/* bidx may be useless (e.g. define_method) */
|
||||
if (bidx >= MRB_ENV_LEN(e))
|
||||
return mrb_false_value();
|
||||
bp = &e->stack[bidx];
|
||||
}
|
||||
else {
|
||||
bp = ci[1].stackent+1;
|
||||
if (ci->argc >= 0) {
|
||||
bp += ci->argc;
|
||||
}
|
||||
else {
|
||||
bp++;
|
||||
}
|
||||
}
|
||||
if (mrb_nil_p(*bp))
|
||||
return mrb_false_value();
|
||||
return mrb_true_value();
|
||||
}
|
||||
|
||||
/* 15.3.1.3.7 */
|
||||
/*
|
||||
* call-seq:
|
||||
* obj.class -> class
|
||||
*
|
||||
* Returns the class of <i>obj</i>. This method must always be
|
||||
* called with an explicit receiver, as <code>class</code> is also a
|
||||
* reserved word in Ruby.
|
||||
*
|
||||
* 1.class #=> Fixnum
|
||||
* self.class #=> Object
|
||||
*/
|
||||
static mrb_value
|
||||
mrb_obj_class_m(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
return mrb_obj_value(mrb_obj_class(mrb, self));
|
||||
}
|
||||
|
||||
static struct RClass*
|
||||
mrb_singleton_class_clone(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
struct RClass *klass = mrb_basic_ptr(obj)->c;
|
||||
|
||||
if (klass->tt != MRB_TT_SCLASS)
|
||||
return klass;
|
||||
else {
|
||||
/* copy singleton(unnamed) class */
|
||||
struct RClass *clone = (struct RClass*)mrb_obj_alloc(mrb, klass->tt, mrb->class_class);
|
||||
|
||||
switch (mrb_type(obj)) {
|
||||
case MRB_TT_CLASS:
|
||||
case MRB_TT_SCLASS:
|
||||
break;
|
||||
default:
|
||||
clone->c = mrb_singleton_class_clone(mrb, mrb_obj_value(klass));
|
||||
break;
|
||||
}
|
||||
clone->super = klass->super;
|
||||
if (klass->iv) {
|
||||
mrb_iv_copy(mrb, mrb_obj_value(clone), mrb_obj_value(klass));
|
||||
mrb_obj_iv_set(mrb, (struct RObject*)clone, mrb_intern_lit(mrb, "__attached__"), obj);
|
||||
}
|
||||
if (klass->mt) {
|
||||
clone->mt = kh_copy(mt, mrb, klass->mt);
|
||||
}
|
||||
else {
|
||||
clone->mt = kh_init(mt, mrb);
|
||||
}
|
||||
clone->tt = MRB_TT_SCLASS;
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
copy_class(mrb_state *mrb, mrb_value dst, mrb_value src)
|
||||
{
|
||||
struct RClass *dc = mrb_class_ptr(dst);
|
||||
struct RClass *sc = mrb_class_ptr(src);
|
||||
/* if the origin is not the same as the class, then the origin and
|
||||
the current class need to be copied */
|
||||
if (sc->flags & MRB_FL_CLASS_IS_PREPENDED) {
|
||||
struct RClass *c0 = sc->super;
|
||||
struct RClass *c1 = dc;
|
||||
|
||||
/* copy prepended iclasses */
|
||||
while (!(c0->flags & MRB_FL_CLASS_IS_ORIGIN)) {
|
||||
c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0)));
|
||||
c1 = c1->super;
|
||||
c0 = c0->super;
|
||||
}
|
||||
c1->super = mrb_class_ptr(mrb_obj_dup(mrb, mrb_obj_value(c0)));
|
||||
c1->super->flags |= MRB_FL_CLASS_IS_ORIGIN;
|
||||
}
|
||||
if (sc->mt) {
|
||||
dc->mt = kh_copy(mt, mrb, sc->mt);
|
||||
}
|
||||
else {
|
||||
dc->mt = kh_init(mt, mrb);
|
||||
}
|
||||
dc->super = sc->super;
|
||||
MRB_SET_INSTANCE_TT(dc, MRB_INSTANCE_TT(sc));
|
||||
}
|
||||
|
||||
static void
|
||||
init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj)
|
||||
{
|
||||
switch (mrb_type(obj)) {
|
||||
case MRB_TT_ICLASS:
|
||||
copy_class(mrb, dest, obj);
|
||||
return;
|
||||
case MRB_TT_CLASS:
|
||||
case MRB_TT_MODULE:
|
||||
copy_class(mrb, dest, obj);
|
||||
mrb_iv_copy(mrb, dest, obj);
|
||||
mrb_iv_remove(mrb, dest, mrb_intern_lit(mrb, "__classname__"));
|
||||
break;
|
||||
case MRB_TT_OBJECT:
|
||||
case MRB_TT_SCLASS:
|
||||
case MRB_TT_HASH:
|
||||
case MRB_TT_DATA:
|
||||
case MRB_TT_EXCEPTION:
|
||||
mrb_iv_copy(mrb, dest, obj);
|
||||
break;
|
||||
case MRB_TT_ISTRUCT:
|
||||
mrb_istruct_copy(dest, obj);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mrb_funcall(mrb, dest, "initialize_copy", 1, obj);
|
||||
}
|
||||
|
||||
/* 15.3.1.3.8 */
|
||||
/*
|
||||
* call-seq:
|
||||
* obj.clone -> an_object
|
||||
*
|
||||
* Produces a shallow copy of <i>obj</i>---the instance variables of
|
||||
* <i>obj</i> are copied, but not the objects they reference. Copies
|
||||
* the frozen state of <i>obj</i>. See also the discussion
|
||||
* under <code>Object#dup</code>.
|
||||
*
|
||||
* class Klass
|
||||
* attr_accessor :str
|
||||
* end
|
||||
* s1 = Klass.new #=> #<Klass:0x401b3a38>
|
||||
* s1.str = "Hello" #=> "Hello"
|
||||
* s2 = s1.clone #=> #<Klass:0x401b3998 @str="Hello">
|
||||
* s2.str[1,4] = "i" #=> "i"
|
||||
* s1.inspect #=> "#<Klass:0x401b3a38 @str=\"Hi\">"
|
||||
* s2.inspect #=> "#<Klass:0x401b3998 @str=\"Hi\">"
|
||||
*
|
||||
* This method may have class-specific behavior. If so, that
|
||||
* behavior will be documented under the #+initialize_copy+ method of
|
||||
* the class.
|
||||
*
|
||||
* Some Class(True False Nil Symbol Fixnum Float) Object cannot clone.
|
||||
*/
|
||||
MRB_API mrb_value
|
||||
mrb_obj_clone(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
struct RObject *p;
|
||||
mrb_value clone;
|
||||
|
||||
if (mrb_immediate_p(self)) {
|
||||
return self;
|
||||
}
|
||||
if (mrb_sclass_p(self)) {
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "can't clone singleton class");
|
||||
}
|
||||
p = (struct RObject*)mrb_obj_alloc(mrb, mrb_type(self), mrb_obj_class(mrb, self));
|
||||
p->c = mrb_singleton_class_clone(mrb, self);
|
||||
mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)p->c);
|
||||
clone = mrb_obj_value(p);
|
||||
init_copy(mrb, clone, self);
|
||||
p->flags |= mrb_obj_ptr(self)->flags & MRB_FL_OBJ_IS_FROZEN;
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/* 15.3.1.3.9 */
|
||||
/*
|
||||
* call-seq:
|
||||
* obj.dup -> an_object
|
||||
*
|
||||
* Produces a shallow copy of <i>obj</i>---the instance variables of
|
||||
* <i>obj</i> are copied, but not the objects they reference.
|
||||
* <code>dup</code> copies the frozen state of <i>obj</i>. See also
|
||||
* the discussion under <code>Object#clone</code>. In general,
|
||||
* <code>clone</code> and <code>dup</code> may have different semantics
|
||||
* in descendant classes. While <code>clone</code> is used to duplicate
|
||||
* an object, including its internal state, <code>dup</code> typically
|
||||
* uses the class of the descendant object to create the new instance.
|
||||
*
|
||||
* This method may have class-specific behavior. If so, that
|
||||
* behavior will be documented under the #+initialize_copy+ method of
|
||||
* the class.
|
||||
*/
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_obj_dup(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
struct RBasic *p;
|
||||
mrb_value dup;
|
||||
|
||||
if (mrb_immediate_p(obj)) {
|
||||
return obj;
|
||||
}
|
||||
if (mrb_sclass_p(obj)) {
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "can't dup singleton class");
|
||||
}
|
||||
p = mrb_obj_alloc(mrb, mrb_type(obj), mrb_obj_class(mrb, obj));
|
||||
dup = mrb_obj_value(p);
|
||||
init_copy(mrb, dup, obj);
|
||||
|
||||
return dup;
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
mrb_obj_extend(mrb_state *mrb, mrb_int argc, mrb_value *argv, mrb_value obj)
|
||||
{
|
||||
mrb_int i;
|
||||
|
||||
if (argc == 0) {
|
||||
mrb_argnum_error(mrb, argc, 1, -1);
|
||||
}
|
||||
for (i = 0; i < argc; i++) {
|
||||
mrb_check_type(mrb, argv[i], MRB_TT_MODULE);
|
||||
}
|
||||
while (argc--) {
|
||||
mrb_funcall(mrb, argv[argc], "extend_object", 1, obj);
|
||||
mrb_funcall(mrb, argv[argc], "extended", 1, obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/* 15.3.1.3.13 */
|
||||
/*
|
||||
* call-seq:
|
||||
* obj.extend(module, ...) -> obj
|
||||
*
|
||||
* Adds to _obj_ the instance methods from each module given as a
|
||||
* parameter.
|
||||
*
|
||||
* module Mod
|
||||
* def hello
|
||||
* "Hello from Mod.\n"
|
||||
* end
|
||||
* end
|
||||
*
|
||||
* class Klass
|
||||
* def hello
|
||||
* "Hello from Klass.\n"
|
||||
* end
|
||||
* end
|
||||
*
|
||||
* k = Klass.new
|
||||
* k.hello #=> "Hello from Klass.\n"
|
||||
* k.extend(Mod) #=> #<Klass:0x401b3bc8>
|
||||
* k.hello #=> "Hello from Mod.\n"
|
||||
*/
|
||||
static mrb_value
|
||||
mrb_obj_extend_m(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value *argv;
|
||||
mrb_int argc;
|
||||
|
||||
mrb_get_args(mrb, "*", &argv, &argc);
|
||||
return mrb_obj_extend(mrb, argc, argv, self);
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_obj_freeze(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
if (!mrb_immediate_p(self)) {
|
||||
struct RBasic *b = mrb_basic_ptr(self);
|
||||
if (!mrb_frozen_p(b)) {
|
||||
MRB_SET_FROZEN_FLAG(b);
|
||||
if (b->c->tt == MRB_TT_SCLASS) MRB_SET_FROZEN_FLAG(b->c);
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
mrb_obj_frozen(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
return mrb_bool_value(mrb_immediate_p(self) || mrb_frozen_p(mrb_basic_ptr(self)));
|
||||
}
|
||||
|
||||
/* 15.3.1.3.15 */
|
||||
/*
|
||||
* call-seq:
|
||||
* obj.hash -> fixnum
|
||||
*
|
||||
* Generates a <code>Fixnum</code> hash value for this object. This
|
||||
* function must have the property that <code>a.eql?(b)</code> implies
|
||||
* <code>a.hash == b.hash</code>. The hash value is used by class
|
||||
* <code>Hash</code>. Any hash value that exceeds the capacity of a
|
||||
* <code>Fixnum</code> will be truncated before being used.
|
||||
*/
|
||||
static mrb_value
|
||||
mrb_obj_hash(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
return mrb_fixnum_value(mrb_obj_id(self));
|
||||
}
|
||||
|
||||
/* 15.3.1.3.16 */
|
||||
static mrb_value
|
||||
mrb_obj_init_copy(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value orig = mrb_get_arg1(mrb);
|
||||
|
||||
if (mrb_obj_equal(mrb, self, orig)) return self;
|
||||
if ((mrb_type(self) != mrb_type(orig)) || (mrb_obj_class(mrb, self) != mrb_obj_class(mrb, orig))) {
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "initialize_copy should take same class object");
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
MRB_API mrb_bool
|
||||
mrb_obj_is_instance_of(mrb_state *mrb, mrb_value obj, struct RClass* c)
|
||||
{
|
||||
if (mrb_obj_class(mrb, obj) == c) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* 15.3.1.3.19 */
|
||||
/*
|
||||
* call-seq:
|
||||
* obj.instance_of?(class) -> true or false
|
||||
*
|
||||
* Returns <code>true</code> if <i>obj</i> is an instance of the given
|
||||
* class. See also <code>Object#kind_of?</code>.
|
||||
*/
|
||||
static mrb_value
|
||||
obj_is_instance_of(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value arg;
|
||||
|
||||
mrb_get_args(mrb, "C", &arg);
|
||||
|
||||
return mrb_bool_value(mrb_obj_is_instance_of(mrb, self, mrb_class_ptr(arg)));
|
||||
}
|
||||
|
||||
/* 15.3.1.3.24 */
|
||||
/* 15.3.1.3.26 */
|
||||
/*
|
||||
* call-seq:
|
||||
* obj.is_a?(class) -> true or false
|
||||
* obj.kind_of?(class) -> true or false
|
||||
*
|
||||
* Returns <code>true</code> if <i>class</i> is the class of
|
||||
* <i>obj</i>, or if <i>class</i> is one of the superclasses of
|
||||
* <i>obj</i> or modules included in <i>obj</i>.
|
||||
*
|
||||
* module M; end
|
||||
* class A
|
||||
* include M
|
||||
* end
|
||||
* class B < A; end
|
||||
* class C < B; end
|
||||
* b = B.new
|
||||
* b.instance_of? A #=> false
|
||||
* b.instance_of? B #=> true
|
||||
* b.instance_of? C #=> false
|
||||
* b.instance_of? M #=> false
|
||||
* b.kind_of? A #=> true
|
||||
* b.kind_of? B #=> true
|
||||
* b.kind_of? C #=> false
|
||||
* b.kind_of? M #=> true
|
||||
*/
|
||||
static mrb_value
|
||||
mrb_obj_is_kind_of_m(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value arg;
|
||||
|
||||
mrb_get_args(mrb, "C", &arg);
|
||||
|
||||
return mrb_bool_value(mrb_obj_is_kind_of(mrb, self, mrb_class_ptr(arg)));
|
||||
}
|
||||
|
||||
KHASH_DECLARE(st, mrb_sym, char, FALSE)
|
||||
KHASH_DEFINE(st, mrb_sym, char, FALSE, kh_int_hash_func, kh_int_hash_equal)
|
||||
|
||||
/* 15.3.1.3.32 */
|
||||
/*
|
||||
* call_seq:
|
||||
* nil.nil? -> true
|
||||
* <anything_else>.nil? -> false
|
||||
*
|
||||
* Only the object <i>nil</i> responds <code>true</code> to <code>nil?</code>.
|
||||
*/
|
||||
static mrb_value
|
||||
mrb_false(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
return mrb_false_value();
|
||||
}
|
||||
|
||||
/* 15.3.1.2.12 */
|
||||
/* 15.3.1.3.40 */
|
||||
/*
|
||||
* call-seq:
|
||||
* raise
|
||||
* raise(string)
|
||||
* raise(exception [, string])
|
||||
*
|
||||
* With no arguments, raises a <code>RuntimeError</code>
|
||||
* With a single +String+ argument, raises a
|
||||
* +RuntimeError+ with the string as a message. Otherwise,
|
||||
* the first parameter should be the name of an +Exception+
|
||||
* class (or an object that returns an +Exception+ object when sent
|
||||
* an +exception+ message). The optional second parameter sets the
|
||||
* message associated with the exception, and the third parameter is an
|
||||
* array of callback information. Exceptions are caught by the
|
||||
* +rescue+ clause of <code>begin...end</code> blocks.
|
||||
*
|
||||
* raise "Failed to create socket"
|
||||
* raise ArgumentError, "No parameters", caller
|
||||
*/
|
||||
MRB_API mrb_value
|
||||
mrb_f_raise(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value a[2], exc;
|
||||
mrb_int argc;
|
||||
|
||||
|
||||
argc = mrb_get_args(mrb, "|oo", &a[0], &a[1]);
|
||||
switch (argc) {
|
||||
case 0:
|
||||
mrb_raise(mrb, E_RUNTIME_ERROR, "");
|
||||
break;
|
||||
case 1:
|
||||
if (mrb_string_p(a[0])) {
|
||||
a[1] = a[0];
|
||||
argc = 2;
|
||||
a[0] = mrb_obj_value(E_RUNTIME_ERROR);
|
||||
}
|
||||
/* fall through */
|
||||
default:
|
||||
exc = mrb_make_exception(mrb, argc, a);
|
||||
mrb_exc_raise(mrb, exc);
|
||||
break;
|
||||
}
|
||||
return mrb_nil_value(); /* not reached */
|
||||
}
|
||||
|
||||
/* 15.3.1.3.41 */
|
||||
/*
|
||||
* call-seq:
|
||||
* obj.remove_instance_variable(symbol) -> obj
|
||||
*
|
||||
* Removes the named instance variable from <i>obj</i>, returning that
|
||||
* variable's value.
|
||||
*
|
||||
* class Dummy
|
||||
* attr_reader :var
|
||||
* def initialize
|
||||
* @var = 99
|
||||
* end
|
||||
* def remove
|
||||
* remove_instance_variable(:@var)
|
||||
* end
|
||||
* end
|
||||
* d = Dummy.new
|
||||
* d.var #=> 99
|
||||
* d.remove #=> 99
|
||||
* d.var #=> nil
|
||||
*/
|
||||
static mrb_value
|
||||
mrb_obj_remove_instance_variable(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_sym sym;
|
||||
mrb_value val;
|
||||
|
||||
mrb_get_args(mrb, "n", &sym);
|
||||
mrb_iv_name_sym_check(mrb, sym);
|
||||
val = mrb_iv_remove(mrb, self, sym);
|
||||
if (mrb_undef_p(val)) {
|
||||
mrb_name_error(mrb, sym, "instance variable %n not defined", sym);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
void
|
||||
mrb_method_missing(mrb_state *mrb, mrb_sym name, mrb_value self, mrb_value args)
|
||||
{
|
||||
mrb_no_method_error(mrb, name, args, "undefined method '%n'", name);
|
||||
}
|
||||
|
||||
/* 15.3.1.3.30 */
|
||||
/*
|
||||
* call-seq:
|
||||
* obj.method_missing(symbol [, *args] ) -> result
|
||||
*
|
||||
* Invoked by Ruby when <i>obj</i> is sent a message it cannot handle.
|
||||
* <i>symbol</i> is the symbol for the method called, and <i>args</i>
|
||||
* are any arguments that were passed to it. By default, the interpreter
|
||||
* raises an error when this method is called. However, it is possible
|
||||
* to override the method to provide more dynamic behavior.
|
||||
* If it is decided that a particular method should not be handled, then
|
||||
* <i>super</i> should be called, so that ancestors can pick up the
|
||||
* missing method.
|
||||
* The example below creates
|
||||
* a class <code>Roman</code>, which responds to methods with names
|
||||
* consisting of roman numerals, returning the corresponding integer
|
||||
* values.
|
||||
*
|
||||
* class Roman
|
||||
* def romanToInt(str)
|
||||
* # ...
|
||||
* end
|
||||
* def method_missing(methId)
|
||||
* str = methId.id2name
|
||||
* romanToInt(str)
|
||||
* end
|
||||
* end
|
||||
*
|
||||
* r = Roman.new
|
||||
* r.iv #=> 4
|
||||
* r.xxiii #=> 23
|
||||
* r.mm #=> 2000
|
||||
*/
|
||||
static mrb_value
|
||||
mrb_obj_missing(mrb_state *mrb, mrb_value mod)
|
||||
{
|
||||
mrb_sym name;
|
||||
mrb_value *a;
|
||||
mrb_int alen;
|
||||
|
||||
mrb_get_args(mrb, "n*!", &name, &a, &alen);
|
||||
mrb_method_missing(mrb, name, mod, mrb_ary_new_from_values(mrb, alen, a));
|
||||
/* not reached */
|
||||
return mrb_nil_value();
|
||||
}
|
||||
|
||||
static inline mrb_bool
|
||||
basic_obj_respond_to(mrb_state *mrb, mrb_value obj, mrb_sym id, int pub)
|
||||
{
|
||||
return mrb_respond_to(mrb, obj, id);
|
||||
}
|
||||
|
||||
/* 15.3.1.3.43 */
|
||||
/*
|
||||
* call-seq:
|
||||
* obj.respond_to?(symbol, include_private=false) -> true or false
|
||||
*
|
||||
* Returns +true+ if _obj_ responds to the given
|
||||
* method. Private methods are included in the search only if the
|
||||
* optional second parameter evaluates to +true+.
|
||||
*
|
||||
* If the method is not implemented,
|
||||
* as Process.fork on Windows, File.lchmod on GNU/Linux, etc.,
|
||||
* false is returned.
|
||||
*
|
||||
* If the method is not defined, <code>respond_to_missing?</code>
|
||||
* method is called and the result is returned.
|
||||
*/
|
||||
static mrb_value
|
||||
obj_respond_to(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_sym id, rtm_id;
|
||||
mrb_bool priv = FALSE, respond_to_p;
|
||||
|
||||
mrb_get_args(mrb, "n|b", &id, &priv);
|
||||
respond_to_p = basic_obj_respond_to(mrb, self, id, !priv);
|
||||
if (!respond_to_p) {
|
||||
rtm_id = mrb_intern_lit(mrb, "respond_to_missing?");
|
||||
if (basic_obj_respond_to(mrb, self, rtm_id, !priv)) {
|
||||
mrb_value args[2], v;
|
||||
args[0] = mrb_symbol_value(id);
|
||||
args[1] = mrb_bool_value(priv);
|
||||
v = mrb_funcall_argv(mrb, self, rtm_id, 2, args);
|
||||
return mrb_bool_value(mrb_bool(v));
|
||||
}
|
||||
}
|
||||
return mrb_bool_value(respond_to_p);
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
mrb_obj_ceqq(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value v = mrb_get_arg1(mrb);
|
||||
mrb_int i, len;
|
||||
mrb_sym eqq = mrb_intern_lit(mrb, "===");
|
||||
mrb_value ary = mrb_ary_splat(mrb, self);
|
||||
|
||||
len = RARRAY_LEN(ary);
|
||||
for (i=0; i<len; i++) {
|
||||
mrb_value c = mrb_funcall_argv(mrb, mrb_ary_entry(ary, i), eqq, 1, &v);
|
||||
if (mrb_test(c)) return mrb_true_value();
|
||||
}
|
||||
return mrb_false_value();
|
||||
}
|
||||
|
||||
mrb_value mrb_obj_equal_m(mrb_state *mrb, mrb_value);
|
||||
|
||||
void
|
||||
mrb_init_kernel(mrb_state *mrb)
|
||||
{
|
||||
struct RClass *krn;
|
||||
|
||||
mrb->kernel_module = krn = mrb_define_module(mrb, "Kernel"); /* 15.3.1 */
|
||||
mrb_define_class_method(mrb, krn, "block_given?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.2 */
|
||||
mrb_define_class_method(mrb, krn, "iterator?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.2.5 */
|
||||
; /* 15.3.1.2.11 */
|
||||
mrb_define_class_method(mrb, krn, "raise", mrb_f_raise, MRB_ARGS_OPT(2)); /* 15.3.1.2.12 */
|
||||
|
||||
|
||||
mrb_define_method(mrb, krn, "===", mrb_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.2 */
|
||||
mrb_define_method(mrb, krn, "block_given?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.3.6 */
|
||||
mrb_define_method(mrb, krn, "class", mrb_obj_class_m, MRB_ARGS_NONE()); /* 15.3.1.3.7 */
|
||||
mrb_define_method(mrb, krn, "clone", mrb_obj_clone, MRB_ARGS_NONE()); /* 15.3.1.3.8 */
|
||||
mrb_define_method(mrb, krn, "dup", mrb_obj_dup, MRB_ARGS_NONE()); /* 15.3.1.3.9 */
|
||||
mrb_define_method(mrb, krn, "eql?", mrb_obj_equal_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.10 */
|
||||
mrb_define_method(mrb, krn, "extend", mrb_obj_extend_m, MRB_ARGS_ANY()); /* 15.3.1.3.13 */
|
||||
mrb_define_method(mrb, krn, "freeze", mrb_obj_freeze, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, krn, "frozen?", mrb_obj_frozen, MRB_ARGS_NONE());
|
||||
mrb_define_method(mrb, krn, "hash", mrb_obj_hash, MRB_ARGS_NONE()); /* 15.3.1.3.15 */
|
||||
mrb_define_method(mrb, krn, "initialize_copy", mrb_obj_init_copy, MRB_ARGS_REQ(1)); /* 15.3.1.3.16 */
|
||||
mrb_define_method(mrb, krn, "inspect", mrb_obj_inspect, MRB_ARGS_NONE()); /* 15.3.1.3.17 */
|
||||
mrb_define_method(mrb, krn, "instance_of?", obj_is_instance_of, MRB_ARGS_REQ(1)); /* 15.3.1.3.19 */
|
||||
|
||||
mrb_define_method(mrb, krn, "is_a?", mrb_obj_is_kind_of_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.24 */
|
||||
mrb_define_method(mrb, krn, "iterator?", mrb_f_block_given_p_m, MRB_ARGS_NONE()); /* 15.3.1.3.25 */
|
||||
mrb_define_method(mrb, krn, "kind_of?", mrb_obj_is_kind_of_m, MRB_ARGS_REQ(1)); /* 15.3.1.3.26 */
|
||||
mrb_define_method(mrb, krn, "method_missing", mrb_obj_missing, MRB_ARGS_ANY()); /* 15.3.1.3.30 */
|
||||
mrb_define_method(mrb, krn, "nil?", mrb_false, MRB_ARGS_NONE()); /* 15.3.1.3.32 */
|
||||
mrb_define_method(mrb, krn, "object_id", mrb_obj_id_m, MRB_ARGS_NONE()); /* 15.3.1.3.33 */
|
||||
mrb_define_method(mrb, krn, "raise", mrb_f_raise, MRB_ARGS_ANY()); /* 15.3.1.3.40 */
|
||||
mrb_define_method(mrb, krn, "remove_instance_variable", mrb_obj_remove_instance_variable,MRB_ARGS_REQ(1)); /* 15.3.1.3.41 */
|
||||
mrb_define_method(mrb, krn, "respond_to?", obj_respond_to, MRB_ARGS_ARG(1,1)); /* 15.3.1.3.43 */
|
||||
mrb_define_method(mrb, krn, "to_s", mrb_any_to_s, MRB_ARGS_NONE()); /* 15.3.1.3.46 */
|
||||
mrb_define_method(mrb, krn, "__case_eqq", mrb_obj_ceqq, MRB_ARGS_REQ(1)); /* internal */
|
||||
mrb_define_method(mrb, krn, "__to_int", mrb_to_int, MRB_ARGS_NONE()); /* internal */
|
||||
mrb_define_method(mrb, krn, "__to_str", mrb_to_str, MRB_ARGS_NONE()); /* internal */
|
||||
|
||||
mrb_include_module(mrb, mrb->object_class, mrb->kernel_module);
|
||||
}
|
||||
Vendored
+692
@@ -0,0 +1,692 @@
|
||||
/*
|
||||
** load.c - mruby binary loader
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <mruby/dump.h>
|
||||
#include <mruby/irep.h>
|
||||
#include <mruby/proc.h>
|
||||
#include <mruby/string.h>
|
||||
#include <mruby/debug.h>
|
||||
#include <mruby/error.h>
|
||||
#include <mruby/data.h>
|
||||
|
||||
#if SIZE_MAX < UINT32_MAX
|
||||
# error size_t must be at least 32 bits wide
|
||||
#endif
|
||||
|
||||
#define FLAG_SRC_MALLOC 1
|
||||
#define FLAG_SRC_STATIC 0
|
||||
|
||||
#define SIZE_ERROR_MUL(nmemb, size) ((size_t)(nmemb) > SIZE_MAX / (size))
|
||||
|
||||
static size_t
|
||||
skip_padding(const uint8_t *buf)
|
||||
{
|
||||
const size_t align = MRB_DUMP_ALIGNMENT;
|
||||
return -(intptr_t)buf & (align-1);
|
||||
}
|
||||
|
||||
static size_t
|
||||
offset_crc_body(void)
|
||||
{
|
||||
struct rite_binary_header header;
|
||||
return ((uint8_t *)header.binary_crc - (uint8_t *)&header) + sizeof(header.binary_crc);
|
||||
}
|
||||
|
||||
#ifndef MRB_WITHOUT_FLOAT
|
||||
double mrb_str_len_to_dbl(mrb_state *mrb, const char *s, size_t len, mrb_bool badcheck);
|
||||
|
||||
static double
|
||||
str_to_double(mrb_state *mrb, const char *p, size_t len)
|
||||
{
|
||||
/* `i`, `inf`, `infinity` */
|
||||
if (len > 0 && p[0] == 'i') return INFINITY;
|
||||
|
||||
/* `I`, `-inf`, `-infinity` */
|
||||
if (p[0] == 'I' || (len > 1 && p[0] == '-' && p[1] == 'i')) return -INFINITY;
|
||||
return mrb_str_len_to_dbl(mrb, p, len, TRUE);
|
||||
}
|
||||
#endif
|
||||
|
||||
mrb_value mrb_str_len_to_inum(mrb_state *mrb, const char *str, mrb_int len, mrb_int base, int badcheck);
|
||||
|
||||
static void
|
||||
tempirep_free(mrb_state *mrb, void *p)
|
||||
{
|
||||
if (p) mrb_irep_decref(mrb, (mrb_irep *)p);
|
||||
}
|
||||
|
||||
static const mrb_data_type tempirep_type = { "temporary irep", tempirep_free };
|
||||
|
||||
static mrb_irep*
|
||||
read_irep_record_1(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags)
|
||||
{
|
||||
int i;
|
||||
const uint8_t *src = bin;
|
||||
ptrdiff_t diff;
|
||||
uint16_t tt, pool_data_len, snl;
|
||||
int plen;
|
||||
struct RData *irep_obj = mrb_data_object_alloc(mrb, mrb->object_class, NULL, &tempirep_type);
|
||||
mrb_irep *irep = mrb_add_irep(mrb);
|
||||
int ai = mrb_gc_arena_save(mrb);
|
||||
|
||||
irep_obj->data = irep;
|
||||
|
||||
/* skip record size */
|
||||
src += sizeof(uint32_t);
|
||||
|
||||
/* number of local variable */
|
||||
irep->nlocals = bin_to_uint16(src);
|
||||
src += sizeof(uint16_t);
|
||||
|
||||
/* number of register variable */
|
||||
irep->nregs = bin_to_uint16(src);
|
||||
src += sizeof(uint16_t);
|
||||
|
||||
/* number of child irep */
|
||||
irep->rlen = (size_t)bin_to_uint16(src);
|
||||
src += sizeof(uint16_t);
|
||||
|
||||
/* Binary Data Section */
|
||||
/* ISEQ BLOCK */
|
||||
irep->ilen = (uint16_t)bin_to_uint32(src);
|
||||
src += sizeof(uint32_t);
|
||||
src += skip_padding(src);
|
||||
|
||||
if (irep->ilen > 0) {
|
||||
if (SIZE_ERROR_MUL(irep->ilen, sizeof(mrb_code))) {
|
||||
return NULL;
|
||||
}
|
||||
if ((flags & FLAG_SRC_MALLOC) == 0) {
|
||||
irep->iseq = (mrb_code*)src;
|
||||
src += sizeof(mrb_code) * irep->ilen;
|
||||
irep->flags |= MRB_ISEQ_NO_FREE;
|
||||
}
|
||||
else {
|
||||
size_t data_len = sizeof(mrb_code) * irep->ilen;
|
||||
void *buf = mrb_malloc(mrb, data_len);
|
||||
irep->iseq = (mrb_code *)buf;
|
||||
memcpy(buf, src, data_len);
|
||||
src += data_len;
|
||||
}
|
||||
}
|
||||
|
||||
/* POOL BLOCK */
|
||||
plen = bin_to_uint32(src); /* number of pool */
|
||||
src += sizeof(uint32_t);
|
||||
if (plen > 0) {
|
||||
if (SIZE_ERROR_MUL(plen, sizeof(mrb_value))) {
|
||||
return NULL;
|
||||
}
|
||||
irep->pool = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * plen);
|
||||
|
||||
for (i = 0; i < plen; i++) {
|
||||
const char *s;
|
||||
mrb_bool st = (flags & FLAG_SRC_MALLOC)==0;
|
||||
|
||||
tt = *src++; /* pool TT */
|
||||
pool_data_len = bin_to_uint16(src); /* pool data length */
|
||||
src += sizeof(uint16_t);
|
||||
s = (const char*)src;
|
||||
src += pool_data_len;
|
||||
switch (tt) { /* pool data */
|
||||
case IREP_TT_FIXNUM: {
|
||||
mrb_value num = mrb_str_len_to_inum(mrb, s, pool_data_len, 10, FALSE);
|
||||
#ifdef MRB_WITHOUT_FLOAT
|
||||
irep->pool[i] = num;
|
||||
#else
|
||||
irep->pool[i] = mrb_float_p(num)? mrb_float_pool(mrb, mrb_float(num)) : num;
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
#ifndef MRB_WITHOUT_FLOAT
|
||||
case IREP_TT_FLOAT:
|
||||
irep->pool[i] = mrb_float_pool(mrb, str_to_double(mrb, s, pool_data_len));
|
||||
break;
|
||||
#endif
|
||||
|
||||
case IREP_TT_STRING:
|
||||
irep->pool[i] = mrb_str_pool(mrb, s, pool_data_len, st);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* should not happen */
|
||||
irep->pool[i] = mrb_nil_value();
|
||||
break;
|
||||
}
|
||||
irep->plen++;
|
||||
mrb_gc_arena_restore(mrb, ai);
|
||||
}
|
||||
}
|
||||
|
||||
/* SYMS BLOCK */
|
||||
irep->slen = (uint16_t)bin_to_uint32(src); /* syms length */
|
||||
src += sizeof(uint32_t);
|
||||
if (irep->slen > 0) {
|
||||
if (SIZE_ERROR_MUL(irep->slen, sizeof(mrb_sym))) {
|
||||
return NULL;
|
||||
}
|
||||
irep->syms = (mrb_sym *)mrb_malloc(mrb, sizeof(mrb_sym) * irep->slen);
|
||||
|
||||
for (i = 0; i < irep->slen; i++) {
|
||||
snl = bin_to_uint16(src); /* symbol name length */
|
||||
src += sizeof(uint16_t);
|
||||
|
||||
if (snl == MRB_DUMP_NULL_SYM_LEN) {
|
||||
irep->syms[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (flags & FLAG_SRC_MALLOC) {
|
||||
irep->syms[i] = mrb_intern(mrb, (char *)src, snl);
|
||||
}
|
||||
else {
|
||||
irep->syms[i] = mrb_intern_static(mrb, (char *)src, snl);
|
||||
}
|
||||
src += snl + 1;
|
||||
|
||||
mrb_gc_arena_restore(mrb, ai);
|
||||
}
|
||||
}
|
||||
|
||||
irep->reps = (mrb_irep**)mrb_calloc(mrb, irep->rlen, sizeof(mrb_irep*));
|
||||
|
||||
diff = src - bin;
|
||||
mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
|
||||
*len = (size_t)diff;
|
||||
|
||||
irep_obj->data = NULL;
|
||||
|
||||
return irep;
|
||||
}
|
||||
|
||||
static mrb_irep*
|
||||
read_irep_record(mrb_state *mrb, const uint8_t *bin, size_t *len, uint8_t flags)
|
||||
{
|
||||
struct RData *irep_obj = mrb_data_object_alloc(mrb, mrb->object_class, NULL, &tempirep_type);
|
||||
int ai = mrb_gc_arena_save(mrb);
|
||||
mrb_irep *irep = read_irep_record_1(mrb, bin, len, flags);
|
||||
int i;
|
||||
|
||||
mrb_gc_arena_restore(mrb, ai);
|
||||
if (irep == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
irep_obj->data = irep;
|
||||
|
||||
bin += *len;
|
||||
for (i=0; i<irep->rlen; i++) {
|
||||
size_t rlen;
|
||||
|
||||
irep->reps[i] = read_irep_record(mrb, bin, &rlen, flags);
|
||||
mrb_gc_arena_restore(mrb, ai);
|
||||
if (irep->reps[i] == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
bin += rlen;
|
||||
*len += rlen;
|
||||
}
|
||||
|
||||
irep_obj->data = NULL;
|
||||
|
||||
return irep;
|
||||
}
|
||||
|
||||
static mrb_irep*
|
||||
read_section_irep(mrb_state *mrb, const uint8_t *bin, uint8_t flags)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
bin += sizeof(struct rite_section_irep_header);
|
||||
return read_irep_record(mrb, bin, &len, flags);
|
||||
}
|
||||
|
||||
static int
|
||||
read_debug_record(mrb_state *mrb, const uint8_t *start, mrb_irep* irep, size_t *record_len, const mrb_sym *filenames, size_t filenames_len)
|
||||
{
|
||||
const uint8_t *bin = start;
|
||||
ptrdiff_t diff;
|
||||
size_t record_size;
|
||||
uint16_t f_idx;
|
||||
int i;
|
||||
|
||||
if (irep->debug_info) { return MRB_DUMP_INVALID_IREP; }
|
||||
|
||||
irep->debug_info = (mrb_irep_debug_info*)mrb_calloc(mrb, 1, sizeof(mrb_irep_debug_info));
|
||||
irep->debug_info->pc_count = (uint32_t)irep->ilen;
|
||||
|
||||
record_size = (size_t)bin_to_uint32(bin);
|
||||
bin += sizeof(uint32_t);
|
||||
|
||||
irep->debug_info->flen = bin_to_uint16(bin);
|
||||
irep->debug_info->files = (mrb_irep_debug_info_file**)mrb_calloc(mrb, irep->debug_info->flen, sizeof(mrb_irep_debug_info*));
|
||||
bin += sizeof(uint16_t);
|
||||
|
||||
for (f_idx = 0; f_idx < irep->debug_info->flen; ++f_idx) {
|
||||
mrb_irep_debug_info_file *file;
|
||||
uint16_t filename_idx;
|
||||
|
||||
file = (mrb_irep_debug_info_file *)mrb_calloc(mrb, 1, sizeof(*file));
|
||||
irep->debug_info->files[f_idx] = file;
|
||||
|
||||
file->start_pos = bin_to_uint32(bin);
|
||||
bin += sizeof(uint32_t);
|
||||
|
||||
/* filename */
|
||||
filename_idx = bin_to_uint16(bin);
|
||||
bin += sizeof(uint16_t);
|
||||
mrb_assert(filename_idx < filenames_len);
|
||||
file->filename_sym = filenames[filename_idx];
|
||||
|
||||
file->line_entry_count = bin_to_uint32(bin);
|
||||
bin += sizeof(uint32_t);
|
||||
file->line_type = (mrb_debug_line_type)bin_to_uint8(bin);
|
||||
bin += sizeof(uint8_t);
|
||||
switch (file->line_type) {
|
||||
case mrb_debug_line_ary: {
|
||||
uint32_t l;
|
||||
|
||||
file->lines.ary = (uint16_t *)mrb_malloc(mrb, sizeof(uint16_t) * (size_t)(file->line_entry_count));
|
||||
for (l = 0; l < file->line_entry_count; ++l) {
|
||||
file->lines.ary[l] = bin_to_uint16(bin);
|
||||
bin += sizeof(uint16_t);
|
||||
}
|
||||
} break;
|
||||
|
||||
case mrb_debug_line_flat_map: {
|
||||
uint32_t l;
|
||||
|
||||
file->lines.flat_map = (mrb_irep_debug_info_line*)mrb_calloc(
|
||||
mrb, (size_t)(file->line_entry_count), sizeof(mrb_irep_debug_info_line));
|
||||
for (l = 0; l < file->line_entry_count; ++l) {
|
||||
file->lines.flat_map[l].start_pos = bin_to_uint32(bin);
|
||||
bin += sizeof(uint32_t);
|
||||
file->lines.flat_map[l].line = bin_to_uint16(bin);
|
||||
bin += sizeof(uint16_t);
|
||||
}
|
||||
} break;
|
||||
|
||||
default: return MRB_DUMP_GENERAL_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
diff = bin - start;
|
||||
mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
|
||||
|
||||
if (record_size != (size_t)diff) {
|
||||
return MRB_DUMP_GENERAL_FAILURE;
|
||||
}
|
||||
|
||||
for (i = 0; i < irep->rlen; i++) {
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
ret = read_debug_record(mrb, bin, irep->reps[i], &len, filenames, filenames_len);
|
||||
if (ret != MRB_DUMP_OK) return ret;
|
||||
bin += len;
|
||||
}
|
||||
|
||||
diff = bin - start;
|
||||
mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
|
||||
*record_len = (size_t)diff;
|
||||
|
||||
return MRB_DUMP_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
read_section_debug(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t flags)
|
||||
{
|
||||
const uint8_t *bin;
|
||||
ptrdiff_t diff;
|
||||
struct rite_section_debug_header *header;
|
||||
uint16_t i;
|
||||
size_t len = 0;
|
||||
int result;
|
||||
uint16_t filenames_len;
|
||||
mrb_sym *filenames;
|
||||
mrb_value filenames_obj;
|
||||
|
||||
bin = start;
|
||||
header = (struct rite_section_debug_header *)bin;
|
||||
bin += sizeof(struct rite_section_debug_header);
|
||||
|
||||
filenames_len = bin_to_uint16(bin);
|
||||
bin += sizeof(uint16_t);
|
||||
filenames_obj = mrb_str_new(mrb, NULL, sizeof(mrb_sym) * (size_t)filenames_len);
|
||||
filenames = (mrb_sym*)RSTRING_PTR(filenames_obj);
|
||||
for (i = 0; i < filenames_len; ++i) {
|
||||
uint16_t f_len = bin_to_uint16(bin);
|
||||
bin += sizeof(uint16_t);
|
||||
if (flags & FLAG_SRC_MALLOC) {
|
||||
filenames[i] = mrb_intern(mrb, (const char *)bin, (size_t)f_len);
|
||||
}
|
||||
else {
|
||||
filenames[i] = mrb_intern_static(mrb, (const char *)bin, (size_t)f_len);
|
||||
}
|
||||
bin += f_len;
|
||||
}
|
||||
|
||||
result = read_debug_record(mrb, bin, irep, &len, filenames, filenames_len);
|
||||
if (result != MRB_DUMP_OK) goto debug_exit;
|
||||
|
||||
bin += len;
|
||||
diff = bin - start;
|
||||
mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
|
||||
if ((uint32_t)diff != bin_to_uint32(header->section_size)) {
|
||||
result = MRB_DUMP_GENERAL_FAILURE;
|
||||
}
|
||||
|
||||
debug_exit:
|
||||
mrb_str_resize(mrb, filenames_obj, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
read_lv_record(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, size_t *record_len, mrb_sym const *syms, uint32_t syms_len)
|
||||
{
|
||||
const uint8_t *bin = start;
|
||||
ptrdiff_t diff;
|
||||
int i;
|
||||
|
||||
irep->lv = (struct mrb_locals*)mrb_malloc(mrb, sizeof(struct mrb_locals) * (irep->nlocals - 1));
|
||||
|
||||
for (i = 0; i + 1< irep->nlocals; ++i) {
|
||||
uint16_t const sym_idx = bin_to_uint16(bin);
|
||||
bin += sizeof(uint16_t);
|
||||
if (sym_idx == RITE_LV_NULL_MARK) {
|
||||
irep->lv[i].name = 0;
|
||||
irep->lv[i].r = 0;
|
||||
}
|
||||
else {
|
||||
if (sym_idx >= syms_len) {
|
||||
return MRB_DUMP_GENERAL_FAILURE;
|
||||
}
|
||||
irep->lv[i].name = syms[sym_idx];
|
||||
|
||||
irep->lv[i].r = bin_to_uint16(bin);
|
||||
}
|
||||
bin += sizeof(uint16_t);
|
||||
}
|
||||
|
||||
for (i = 0; i < irep->rlen; ++i) {
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
ret = read_lv_record(mrb, bin, irep->reps[i], &len, syms, syms_len);
|
||||
if (ret != MRB_DUMP_OK) return ret;
|
||||
bin += len;
|
||||
}
|
||||
|
||||
diff = bin - start;
|
||||
mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
|
||||
*record_len = (size_t)diff;
|
||||
|
||||
return MRB_DUMP_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
read_section_lv(mrb_state *mrb, const uint8_t *start, mrb_irep *irep, uint8_t flags)
|
||||
{
|
||||
const uint8_t *bin;
|
||||
ptrdiff_t diff;
|
||||
struct rite_section_lv_header const *header;
|
||||
uint32_t i;
|
||||
size_t len = 0;
|
||||
int result;
|
||||
uint32_t syms_len;
|
||||
mrb_sym *syms;
|
||||
mrb_value syms_obj;
|
||||
mrb_sym (*intern_func)(mrb_state*, const char*, size_t) =
|
||||
(flags & FLAG_SRC_MALLOC)? mrb_intern : mrb_intern_static;
|
||||
|
||||
bin = start;
|
||||
header = (struct rite_section_lv_header const*)bin;
|
||||
bin += sizeof(struct rite_section_lv_header);
|
||||
|
||||
syms_len = bin_to_uint32(bin);
|
||||
bin += sizeof(uint32_t);
|
||||
syms_obj = mrb_str_new(mrb, NULL, sizeof(mrb_sym) * (size_t)syms_len);
|
||||
syms = (mrb_sym*)RSTRING_PTR(syms_obj);
|
||||
for (i = 0; i < syms_len; ++i) {
|
||||
uint16_t const str_len = bin_to_uint16(bin);
|
||||
bin += sizeof(uint16_t);
|
||||
|
||||
syms[i] = intern_func(mrb, (const char*)bin, str_len);
|
||||
bin += str_len;
|
||||
}
|
||||
|
||||
result = read_lv_record(mrb, bin, irep, &len, syms, syms_len);
|
||||
if (result != MRB_DUMP_OK) goto lv_exit;
|
||||
|
||||
bin += len;
|
||||
diff = bin - start;
|
||||
mrb_assert_int_fit(ptrdiff_t, diff, size_t, SIZE_MAX);
|
||||
if ((uint32_t)diff != bin_to_uint32(header->section_size)) {
|
||||
result = MRB_DUMP_GENERAL_FAILURE;
|
||||
}
|
||||
|
||||
lv_exit:
|
||||
mrb_str_resize(mrb, syms_obj, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
read_binary_header(const uint8_t *bin, size_t bufsize, size_t *bin_size, uint16_t *crc, uint8_t *flags)
|
||||
{
|
||||
const struct rite_binary_header *header = (const struct rite_binary_header *)bin;
|
||||
|
||||
if (bufsize < sizeof(struct rite_binary_header)) {
|
||||
return MRB_DUMP_READ_FAULT;
|
||||
}
|
||||
|
||||
if (memcmp(header->binary_ident, RITE_BINARY_IDENT, sizeof(header->binary_ident)) != 0) {
|
||||
return MRB_DUMP_INVALID_FILE_HEADER;
|
||||
}
|
||||
|
||||
if (memcmp(header->binary_version, RITE_BINARY_FORMAT_VER, sizeof(header->binary_version)) != 0) {
|
||||
return MRB_DUMP_INVALID_FILE_HEADER;
|
||||
}
|
||||
|
||||
if (crc) {
|
||||
*crc = bin_to_uint16(header->binary_crc);
|
||||
}
|
||||
*bin_size = (size_t)bin_to_uint32(header->binary_size);
|
||||
|
||||
if (bufsize < *bin_size) {
|
||||
return MRB_DUMP_READ_FAULT;
|
||||
}
|
||||
|
||||
return MRB_DUMP_OK;
|
||||
}
|
||||
|
||||
static mrb_irep*
|
||||
read_irep(mrb_state *mrb, const uint8_t *bin, size_t bufsize, uint8_t flags)
|
||||
{
|
||||
int result;
|
||||
struct RData *irep_obj = NULL;
|
||||
mrb_irep *irep = NULL;
|
||||
const struct rite_section_header *section_header;
|
||||
uint16_t crc;
|
||||
size_t bin_size = 0;
|
||||
size_t n;
|
||||
|
||||
if ((mrb == NULL) || (bin == NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = read_binary_header(bin, bufsize, &bin_size, &crc, &flags);
|
||||
if (result != MRB_DUMP_OK) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
n = offset_crc_body();
|
||||
if (crc != calc_crc_16_ccitt(bin + n, bin_size - n, 0)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
irep_obj = mrb_data_object_alloc(mrb, mrb->object_class, NULL, &tempirep_type);
|
||||
|
||||
bin += sizeof(struct rite_binary_header);
|
||||
do {
|
||||
section_header = (const struct rite_section_header *)bin;
|
||||
if (memcmp(section_header->section_ident, RITE_SECTION_IREP_IDENT, sizeof(section_header->section_ident)) == 0) {
|
||||
irep = read_section_irep(mrb, bin, flags);
|
||||
if (!irep) return NULL;
|
||||
irep_obj->data = irep;
|
||||
}
|
||||
else if (memcmp(section_header->section_ident, RITE_SECTION_DEBUG_IDENT, sizeof(section_header->section_ident)) == 0) {
|
||||
if (!irep) return NULL; /* corrupted data */
|
||||
result = read_section_debug(mrb, bin, irep, flags);
|
||||
if (result < MRB_DUMP_OK) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (memcmp(section_header->section_ident, RITE_SECTION_LV_IDENT, sizeof(section_header->section_ident)) == 0) {
|
||||
if (!irep) return NULL;
|
||||
result = read_section_lv(mrb, bin, irep, flags);
|
||||
if (result < MRB_DUMP_OK) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
bin += bin_to_uint32(section_header->section_size);
|
||||
} while (memcmp(section_header->section_ident, RITE_BINARY_EOF, sizeof(section_header->section_ident)) != 0);
|
||||
|
||||
irep_obj->data = NULL;
|
||||
|
||||
return irep;
|
||||
}
|
||||
|
||||
mrb_irep*
|
||||
mrb_read_irep(mrb_state *mrb, const uint8_t *bin)
|
||||
{
|
||||
#if defined(MRB_USE_LINK_TIME_RO_DATA_P) || defined(MRB_USE_CUSTOM_RO_DATA_P)
|
||||
uint8_t flags = mrb_ro_data_p((char*)bin) ? FLAG_SRC_STATIC : FLAG_SRC_MALLOC;
|
||||
#else
|
||||
uint8_t flags = FLAG_SRC_STATIC;
|
||||
#endif
|
||||
|
||||
return read_irep(mrb, bin, (size_t)-1, flags);
|
||||
}
|
||||
|
||||
MRB_API mrb_irep*
|
||||
mrb_read_irep_buf(mrb_state *mrb, const void *buf, size_t bufsize)
|
||||
{
|
||||
return read_irep(mrb, (const uint8_t *)buf, bufsize, FLAG_SRC_MALLOC);
|
||||
}
|
||||
|
||||
void mrb_exc_set(mrb_state *mrb, mrb_value exc);
|
||||
|
||||
static void
|
||||
irep_error(mrb_state *mrb)
|
||||
{
|
||||
mrb_exc_set(mrb, mrb_exc_new_str_lit(mrb, E_SCRIPT_ERROR, "irep load error"));
|
||||
}
|
||||
|
||||
void mrb_codedump_all(mrb_state*, struct RProc*);
|
||||
|
||||
static mrb_value
|
||||
load_irep(mrb_state *mrb, mrb_irep *irep, mrbc_context *c)
|
||||
{
|
||||
struct RProc *proc;
|
||||
|
||||
if (!irep) {
|
||||
irep_error(mrb);
|
||||
return mrb_nil_value();
|
||||
}
|
||||
proc = mrb_proc_new(mrb, irep);
|
||||
proc->c = NULL;
|
||||
mrb_irep_decref(mrb, irep);
|
||||
if (c && c->dump_result) mrb_codedump_all(mrb, proc);
|
||||
if (c && c->no_exec) return mrb_obj_value(proc);
|
||||
return mrb_top_run(mrb, proc, mrb_top_self(mrb), 0);
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, mrbc_context *c)
|
||||
{
|
||||
struct RData *irep_obj = mrb_data_object_alloc(mrb, mrb->object_class, NULL, &tempirep_type);
|
||||
mrb_irep *irep = mrb_read_irep(mrb, bin);
|
||||
mrb_value ret;
|
||||
|
||||
irep_obj->data = irep;
|
||||
mrb_irep_incref(mrb, irep);
|
||||
ret = load_irep(mrb, irep, c);
|
||||
irep_obj->data = NULL;
|
||||
mrb_irep_decref(mrb, irep);
|
||||
return ret;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_load_irep_buf_cxt(mrb_state *mrb, const void *buf, size_t bufsize, mrbc_context *c)
|
||||
{
|
||||
return load_irep(mrb, mrb_read_irep_buf(mrb, buf, bufsize), c);
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_load_irep(mrb_state *mrb, const uint8_t *bin)
|
||||
{
|
||||
return mrb_load_irep_cxt(mrb, bin, NULL);
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_load_irep_buf(mrb_state *mrb, const void *buf, size_t bufsize)
|
||||
{
|
||||
return mrb_load_irep_buf_cxt(mrb, buf, bufsize, NULL);
|
||||
}
|
||||
|
||||
#ifndef MRB_DISABLE_STDIO
|
||||
|
||||
mrb_irep*
|
||||
mrb_read_irep_file(mrb_state *mrb, FILE* fp)
|
||||
{
|
||||
mrb_irep *irep = NULL;
|
||||
uint8_t *buf;
|
||||
const size_t header_size = sizeof(struct rite_binary_header);
|
||||
size_t buf_size = 0;
|
||||
uint8_t flags = 0;
|
||||
int result;
|
||||
|
||||
if ((mrb == NULL) || (fp == NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = (uint8_t*)mrb_malloc(mrb, header_size);
|
||||
if (fread(buf, header_size, 1, fp) == 0) {
|
||||
goto irep_exit;
|
||||
}
|
||||
result = read_binary_header(buf, (size_t)-1, &buf_size, NULL, &flags);
|
||||
if (result != MRB_DUMP_OK || buf_size <= header_size) {
|
||||
goto irep_exit;
|
||||
}
|
||||
|
||||
buf = (uint8_t*)mrb_realloc(mrb, buf, buf_size);
|
||||
if (fread(buf+header_size, buf_size-header_size, 1, fp) == 0) {
|
||||
goto irep_exit;
|
||||
}
|
||||
irep = read_irep(mrb, buf, (size_t)-1, FLAG_SRC_MALLOC);
|
||||
|
||||
irep_exit:
|
||||
mrb_free(mrb, buf);
|
||||
return irep;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_load_irep_file_cxt(mrb_state *mrb, FILE* fp, mrbc_context *c)
|
||||
{
|
||||
return load_irep(mrb, mrb_read_irep_file(mrb, fp), c);
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_load_irep_file(mrb_state *mrb, FILE* fp)
|
||||
{
|
||||
return mrb_load_irep_file_cxt(mrb, fp, NULL);
|
||||
}
|
||||
#endif /* MRB_DISABLE_STDIO */
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
MRuby.each_target do
|
||||
current_dir = File.dirname(__FILE__).relative_path_from(Dir.pwd)
|
||||
relative_from_root = File.dirname(__FILE__).relative_path_from(MRUBY_ROOT)
|
||||
current_build_dir = "#{build_dir}/#{relative_from_root}"
|
||||
|
||||
objs = Dir.glob("#{current_dir}/*.c").map { |f|
|
||||
next nil if cxx_exception_enabled? and f =~ /(error|vm).c$/
|
||||
objfile(f.pathmap("#{current_build_dir}/%n"))
|
||||
}.compact
|
||||
|
||||
if cxx_exception_enabled?
|
||||
objs += %w(vm error).map { |v| compile_as_cxx "#{current_dir}/#{v}.c", "#{current_build_dir}/#{v}.cxx" }
|
||||
end
|
||||
self.libmruby_objs << objs
|
||||
|
||||
file libmruby_core_static => objs do |t|
|
||||
archiver.run t.name, t.prerequisites
|
||||
end
|
||||
end
|
||||
+1688
File diff suppressed because it is too large
Load Diff
+658
@@ -0,0 +1,658 @@
|
||||
/*
|
||||
** object.c - Object, NilClass, TrueClass, FalseClass class
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
*/
|
||||
|
||||
#include <mruby.h>
|
||||
#include <mruby/class.h>
|
||||
#include <mruby/numeric.h>
|
||||
#include <mruby/string.h>
|
||||
#include <mruby/class.h>
|
||||
|
||||
MRB_API mrb_bool
|
||||
mrb_obj_eq(mrb_state *mrb, mrb_value v1, mrb_value v2)
|
||||
{
|
||||
if (mrb_type(v1) != mrb_type(v2)) return FALSE;
|
||||
switch (mrb_type(v1)) {
|
||||
case MRB_TT_TRUE:
|
||||
return TRUE;
|
||||
|
||||
case MRB_TT_FALSE:
|
||||
case MRB_TT_FIXNUM:
|
||||
return (mrb_fixnum(v1) == mrb_fixnum(v2));
|
||||
case MRB_TT_SYMBOL:
|
||||
return (mrb_symbol(v1) == mrb_symbol(v2));
|
||||
|
||||
#ifndef MRB_WITHOUT_FLOAT
|
||||
case MRB_TT_FLOAT:
|
||||
return (mrb_float(v1) == mrb_float(v2));
|
||||
#endif
|
||||
|
||||
default:
|
||||
return (mrb_ptr(v1) == mrb_ptr(v2));
|
||||
}
|
||||
}
|
||||
|
||||
MRB_API mrb_bool
|
||||
mrb_obj_equal(mrb_state *mrb, mrb_value v1, mrb_value v2)
|
||||
{
|
||||
/* temporary definition */
|
||||
return mrb_obj_eq(mrb, v1, v2);
|
||||
}
|
||||
|
||||
MRB_API mrb_bool
|
||||
mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2)
|
||||
{
|
||||
mrb_value result;
|
||||
|
||||
if (mrb_obj_eq(mrb, obj1, obj2)) return TRUE;
|
||||
#ifndef MRB_WITHOUT_FLOAT
|
||||
/* value mixing with integer and float */
|
||||
if (mrb_fixnum_p(obj1)) {
|
||||
if (mrb_float_p(obj2) && (mrb_float)mrb_fixnum(obj1) == mrb_float(obj2))
|
||||
return TRUE;
|
||||
}
|
||||
else if (mrb_float_p(obj1)) {
|
||||
if (mrb_fixnum_p(obj2) && mrb_float(obj1) == (mrb_float)mrb_fixnum(obj2))
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
result = mrb_funcall(mrb, obj1, "==", 1, obj2);
|
||||
if (mrb_test(result)) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Document-class: NilClass
|
||||
*
|
||||
* The class of the singleton object <code>nil</code>.
|
||||
*/
|
||||
|
||||
/* 15.2.4.3.4 */
|
||||
/*
|
||||
* call_seq:
|
||||
* nil.nil? -> true
|
||||
*
|
||||
* Only the object <i>nil</i> responds <code>true</code> to <code>nil?</code>.
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
mrb_true(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
return mrb_true_value();
|
||||
}
|
||||
|
||||
/* 15.2.4.3.5 */
|
||||
/*
|
||||
* call-seq:
|
||||
* nil.to_s -> ""
|
||||
*
|
||||
* Always returns the empty string.
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
nil_to_s(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
mrb_value str = mrb_str_new_frozen(mrb, 0, 0);
|
||||
RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
|
||||
return str;
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
nil_inspect(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
mrb_value str = mrb_str_new_lit_frozen(mrb, "nil");
|
||||
RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
|
||||
return str;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Document-class: TrueClass
|
||||
*
|
||||
* The global value <code>true</code> is the only instance of class
|
||||
* <code>TrueClass</code> and represents a logically true value in
|
||||
* boolean expressions. The class provides operators allowing
|
||||
* <code>true</code> to be used in logical expressions.
|
||||
*/
|
||||
|
||||
/* 15.2.5.3.1 */
|
||||
/*
|
||||
* call-seq:
|
||||
* true & obj -> true or false
|
||||
*
|
||||
* And---Returns <code>false</code> if <i>obj</i> is
|
||||
* <code>nil</code> or <code>false</code>, <code>true</code> otherwise.
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
true_and(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
mrb_bool obj2;
|
||||
|
||||
mrb_get_args(mrb, "b", &obj2);
|
||||
|
||||
return mrb_bool_value(obj2);
|
||||
}
|
||||
|
||||
/* 15.2.5.3.2 */
|
||||
/*
|
||||
* call-seq:
|
||||
* true ^ obj -> !obj
|
||||
*
|
||||
* Exclusive Or---Returns <code>true</code> if <i>obj</i> is
|
||||
* <code>nil</code> or <code>false</code>, <code>false</code>
|
||||
* otherwise.
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
true_xor(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
mrb_bool obj2;
|
||||
|
||||
mrb_get_args(mrb, "b", &obj2);
|
||||
return mrb_bool_value(!obj2);
|
||||
}
|
||||
|
||||
/* 15.2.5.3.3 */
|
||||
/*
|
||||
* call-seq:
|
||||
* true.to_s -> "true"
|
||||
*
|
||||
* The string representation of <code>true</code> is "true".
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
true_to_s(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
mrb_value str = mrb_str_new_lit_frozen(mrb, "true");
|
||||
RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
|
||||
return str;
|
||||
}
|
||||
|
||||
/* 15.2.5.3.4 */
|
||||
/*
|
||||
* call-seq:
|
||||
* true | obj -> true
|
||||
*
|
||||
* Or---Returns <code>true</code>. As <i>anObject</i> is an argument to
|
||||
* a method call, it is always evaluated; there is no short-circuit
|
||||
* evaluation in this case.
|
||||
*
|
||||
* true | puts("or")
|
||||
* true || puts("logical or")
|
||||
*
|
||||
* <em>produces:</em>
|
||||
*
|
||||
* or
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
true_or(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
return mrb_true_value();
|
||||
}
|
||||
|
||||
/*
|
||||
* Document-class: FalseClass
|
||||
*
|
||||
* The global value <code>false</code> is the only instance of class
|
||||
* <code>FalseClass</code> and represents a logically false value in
|
||||
* boolean expressions. The class provides operators allowing
|
||||
* <code>false</code> to participate correctly in logical expressions.
|
||||
*
|
||||
*/
|
||||
|
||||
/* 15.2.4.3.1 */
|
||||
/* 15.2.6.3.1 */
|
||||
/*
|
||||
* call-seq:
|
||||
* false & obj -> false
|
||||
* nil & obj -> false
|
||||
*
|
||||
* And---Returns <code>false</code>. <i>obj</i> is always
|
||||
* evaluated as it is the argument to a method call---there is no
|
||||
* short-circuit evaluation in this case.
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
false_and(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
return mrb_false_value();
|
||||
}
|
||||
|
||||
/* 15.2.4.3.2 */
|
||||
/* 15.2.6.3.2 */
|
||||
/*
|
||||
* call-seq:
|
||||
* false ^ obj -> true or false
|
||||
* nil ^ obj -> true or false
|
||||
*
|
||||
* Exclusive Or---If <i>obj</i> is <code>nil</code> or
|
||||
* <code>false</code>, returns <code>false</code>; otherwise, returns
|
||||
* <code>true</code>.
|
||||
*
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
false_xor(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
mrb_bool obj2;
|
||||
|
||||
mrb_get_args(mrb, "b", &obj2);
|
||||
return mrb_bool_value(obj2);
|
||||
}
|
||||
|
||||
/* 15.2.4.3.3 */
|
||||
/* 15.2.6.3.4 */
|
||||
/*
|
||||
* call-seq:
|
||||
* false | obj -> true or false
|
||||
* nil | obj -> true or false
|
||||
*
|
||||
* Or---Returns <code>false</code> if <i>obj</i> is
|
||||
* <code>nil</code> or <code>false</code>; <code>true</code> otherwise.
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
false_or(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
mrb_bool obj2;
|
||||
|
||||
mrb_get_args(mrb, "b", &obj2);
|
||||
return mrb_bool_value(obj2);
|
||||
}
|
||||
|
||||
/* 15.2.6.3.3 */
|
||||
/*
|
||||
* call-seq:
|
||||
* false.to_s -> "false"
|
||||
*
|
||||
* 'nuf said...
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
false_to_s(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
mrb_value str = mrb_str_new_lit_frozen(mrb, "false");
|
||||
RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
|
||||
return str;
|
||||
}
|
||||
|
||||
void
|
||||
mrb_init_object(mrb_state *mrb)
|
||||
{
|
||||
struct RClass *n;
|
||||
struct RClass *t;
|
||||
struct RClass *f;
|
||||
|
||||
mrb->nil_class = n = mrb_define_class(mrb, "NilClass", mrb->object_class);
|
||||
MRB_SET_INSTANCE_TT(n, MRB_TT_TRUE);
|
||||
mrb_undef_class_method(mrb, n, "new");
|
||||
mrb_define_method(mrb, n, "&", false_and, MRB_ARGS_REQ(1)); /* 15.2.4.3.1 */
|
||||
mrb_define_method(mrb, n, "^", false_xor, MRB_ARGS_REQ(1)); /* 15.2.4.3.2 */
|
||||
mrb_define_method(mrb, n, "|", false_or, MRB_ARGS_REQ(1)); /* 15.2.4.3.3 */
|
||||
mrb_define_method(mrb, n, "nil?", mrb_true, MRB_ARGS_NONE()); /* 15.2.4.3.4 */
|
||||
mrb_define_method(mrb, n, "to_s", nil_to_s, MRB_ARGS_NONE()); /* 15.2.4.3.5 */
|
||||
mrb_define_method(mrb, n, "inspect", nil_inspect, MRB_ARGS_NONE());
|
||||
|
||||
mrb->true_class = t = mrb_define_class(mrb, "TrueClass", mrb->object_class);
|
||||
MRB_SET_INSTANCE_TT(t, MRB_TT_TRUE);
|
||||
mrb_undef_class_method(mrb, t, "new");
|
||||
mrb_define_method(mrb, t, "&", true_and, MRB_ARGS_REQ(1)); /* 15.2.5.3.1 */
|
||||
mrb_define_method(mrb, t, "^", true_xor, MRB_ARGS_REQ(1)); /* 15.2.5.3.2 */
|
||||
mrb_define_method(mrb, t, "to_s", true_to_s, MRB_ARGS_NONE()); /* 15.2.5.3.3 */
|
||||
mrb_define_method(mrb, t, "|", true_or, MRB_ARGS_REQ(1)); /* 15.2.5.3.4 */
|
||||
mrb_define_method(mrb, t, "inspect", true_to_s, MRB_ARGS_NONE());
|
||||
|
||||
mrb->false_class = f = mrb_define_class(mrb, "FalseClass", mrb->object_class);
|
||||
MRB_SET_INSTANCE_TT(f, MRB_TT_TRUE);
|
||||
mrb_undef_class_method(mrb, f, "new");
|
||||
mrb_define_method(mrb, f, "&", false_and, MRB_ARGS_REQ(1)); /* 15.2.6.3.1 */
|
||||
mrb_define_method(mrb, f, "^", false_xor, MRB_ARGS_REQ(1)); /* 15.2.6.3.2 */
|
||||
mrb_define_method(mrb, f, "to_s", false_to_s, MRB_ARGS_NONE()); /* 15.2.6.3.3 */
|
||||
mrb_define_method(mrb, f, "|", false_or, MRB_ARGS_REQ(1)); /* 15.2.6.3.4 */
|
||||
mrb_define_method(mrb, f, "inspect", false_to_s, MRB_ARGS_NONE());
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
convert_type(mrb_state *mrb, mrb_value val, const char *tname, const char *method, mrb_bool raise)
|
||||
{
|
||||
mrb_sym m = 0;
|
||||
|
||||
m = mrb_intern_cstr(mrb, method);
|
||||
if (!mrb_respond_to(mrb, val, m)) {
|
||||
if (raise) {
|
||||
mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %Y into %s", val, tname);
|
||||
}
|
||||
return mrb_nil_value();
|
||||
}
|
||||
return mrb_funcall_argv(mrb, val, m, 0, 0);
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method)
|
||||
{
|
||||
mrb_value v;
|
||||
|
||||
if (mrb_type(val) == type) return val;
|
||||
v = convert_type(mrb, val, tname, method, TRUE);
|
||||
if (mrb_type(v) != type) {
|
||||
mrb_raisef(mrb, E_TYPE_ERROR, "%v cannot be converted to %s by #%s", val, tname, method);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_check_convert_type(mrb_state *mrb, mrb_value val, enum mrb_vtype type, const char *tname, const char *method)
|
||||
{
|
||||
mrb_value v;
|
||||
|
||||
if (mrb_type(val) == type && type != MRB_TT_DATA && type != MRB_TT_ISTRUCT) return val;
|
||||
v = convert_type(mrb, val, tname, method, FALSE);
|
||||
if (mrb_nil_p(v) || mrb_type(v) != type) return mrb_nil_value();
|
||||
return v;
|
||||
}
|
||||
|
||||
static const struct types {
|
||||
unsigned char type;
|
||||
const char *name;
|
||||
} builtin_types[] = {
|
||||
/* {MRB_TT_NIL, "nil"}, */
|
||||
{MRB_TT_FALSE, "false"},
|
||||
{MRB_TT_TRUE, "true"},
|
||||
{MRB_TT_FIXNUM, "Fixnum"},
|
||||
{MRB_TT_SYMBOL, "Symbol"}, /* :symbol */
|
||||
{MRB_TT_MODULE, "Module"},
|
||||
{MRB_TT_OBJECT, "Object"},
|
||||
{MRB_TT_CLASS, "Class"},
|
||||
{MRB_TT_ICLASS, "iClass"}, /* internal use: mixed-in module holder */
|
||||
{MRB_TT_SCLASS, "SClass"},
|
||||
{MRB_TT_PROC, "Proc"},
|
||||
#ifndef MRB_WITHOUT_FLOAT
|
||||
{MRB_TT_FLOAT, "Float"},
|
||||
#endif
|
||||
{MRB_TT_ARRAY, "Array"},
|
||||
{MRB_TT_HASH, "Hash"},
|
||||
{MRB_TT_STRING, "String"},
|
||||
{MRB_TT_RANGE, "Range"},
|
||||
/* {MRB_TT_BIGNUM, "Bignum"}, */
|
||||
{MRB_TT_DATA, "Data"}, /* internal use: wrapped C pointers */
|
||||
/* {MRB_TT_VARMAP, "Varmap"}, */ /* internal use: dynamic variables */
|
||||
/* {MRB_TT_NODE, "Node"}, */ /* internal use: syntax tree node */
|
||||
/* {MRB_TT_UNDEF, "undef"}, */ /* internal use: #undef; should not happen */
|
||||
{MRB_TT_MAXDEFINE, 0}
|
||||
};
|
||||
|
||||
MRB_API void
|
||||
mrb_check_type(mrb_state *mrb, mrb_value x, enum mrb_vtype t)
|
||||
{
|
||||
const struct types *type = builtin_types;
|
||||
enum mrb_vtype xt;
|
||||
|
||||
xt = mrb_type(x);
|
||||
if ((xt != t) || (xt == MRB_TT_DATA) || (xt == MRB_TT_ISTRUCT)) {
|
||||
while (type->type < MRB_TT_MAXDEFINE) {
|
||||
if (type->type == t) {
|
||||
const char *etype;
|
||||
|
||||
if (mrb_nil_p(x)) {
|
||||
etype = "nil";
|
||||
}
|
||||
else if (mrb_fixnum_p(x)) {
|
||||
etype = "Fixnum";
|
||||
}
|
||||
else if (mrb_symbol_p(x)) {
|
||||
etype = "Symbol";
|
||||
}
|
||||
else if (mrb_immediate_p(x)) {
|
||||
etype = RSTRING_PTR(mrb_obj_as_string(mrb, x));
|
||||
}
|
||||
else {
|
||||
etype = mrb_obj_classname(mrb, x);
|
||||
}
|
||||
mrb_raisef(mrb, E_TYPE_ERROR, "wrong argument type %s (expected %s)",
|
||||
etype, type->name);
|
||||
}
|
||||
type++;
|
||||
}
|
||||
mrb_raisef(mrb, E_TYPE_ERROR, "unknown type %d (%d given)", t, mrb_type(x));
|
||||
}
|
||||
}
|
||||
|
||||
/* 15.3.1.3.46 */
|
||||
/*
|
||||
* call-seq:
|
||||
* obj.to_s => string
|
||||
*
|
||||
* Returns a string representing <i>obj</i>. The default
|
||||
* <code>to_s</code> prints the object's class and an encoding of the
|
||||
* object id. As a special case, the top-level object that is the
|
||||
* initial execution context of Ruby programs returns "main."
|
||||
*/
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_any_to_s(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
mrb_value str = mrb_str_new_capa(mrb, 20);
|
||||
const char *cname = mrb_obj_classname(mrb, obj);
|
||||
|
||||
mrb_str_cat_lit(mrb, str, "#<");
|
||||
mrb_str_cat_cstr(mrb, str, cname);
|
||||
if (!mrb_immediate_p(obj)) {
|
||||
mrb_str_cat_lit(mrb, str, ":");
|
||||
mrb_str_cat_str(mrb, str, mrb_ptr_to_str(mrb, mrb_ptr(obj)));
|
||||
}
|
||||
mrb_str_cat_lit(mrb, str, ">");
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* obj.is_a?(class) => true or false
|
||||
* obj.kind_of?(class) => true or false
|
||||
*
|
||||
* Returns <code>true</code> if <i>class</i> is the class of
|
||||
* <i>obj</i>, or if <i>class</i> is one of the superclasses of
|
||||
* <i>obj</i> or modules included in <i>obj</i>.
|
||||
*
|
||||
* module M; end
|
||||
* class A
|
||||
* include M
|
||||
* end
|
||||
* class B < A; end
|
||||
* class C < B; end
|
||||
* b = B.new
|
||||
* b.instance_of? A #=> false
|
||||
* b.instance_of? B #=> true
|
||||
* b.instance_of? C #=> false
|
||||
* b.instance_of? M #=> false
|
||||
* b.kind_of? A #=> true
|
||||
* b.kind_of? B #=> true
|
||||
* b.kind_of? C #=> false
|
||||
* b.kind_of? M #=> true
|
||||
*/
|
||||
|
||||
MRB_API mrb_bool
|
||||
mrb_obj_is_kind_of(mrb_state *mrb, mrb_value obj, struct RClass *c)
|
||||
{
|
||||
struct RClass *cl = mrb_class(mrb, obj);
|
||||
|
||||
switch (c->tt) {
|
||||
case MRB_TT_MODULE:
|
||||
case MRB_TT_CLASS:
|
||||
case MRB_TT_ICLASS:
|
||||
case MRB_TT_SCLASS:
|
||||
break;
|
||||
|
||||
default:
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "class or module required");
|
||||
}
|
||||
|
||||
MRB_CLASS_ORIGIN(c);
|
||||
while (cl) {
|
||||
if (cl == c || cl->mt == c->mt)
|
||||
return TRUE;
|
||||
cl = cl->super;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_to_int(mrb_state *mrb, mrb_value val)
|
||||
{
|
||||
|
||||
if (!mrb_fixnum_p(val)) {
|
||||
#ifndef MRB_WITHOUT_FLOAT
|
||||
if (mrb_float_p(val)) {
|
||||
return mrb_flo_to_fixnum(mrb, val);
|
||||
}
|
||||
#endif
|
||||
mrb_raisef(mrb, E_TYPE_ERROR, "can't convert %Y to Integer", val);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_convert_to_integer(mrb_state *mrb, mrb_value val, mrb_int base)
|
||||
{
|
||||
mrb_value tmp;
|
||||
|
||||
if (mrb_nil_p(val)) {
|
||||
if (base != 0) goto arg_error;
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Integer");
|
||||
}
|
||||
switch (mrb_type(val)) {
|
||||
#ifndef MRB_WITHOUT_FLOAT
|
||||
case MRB_TT_FLOAT:
|
||||
if (base != 0) goto arg_error;
|
||||
return mrb_flo_to_fixnum(mrb, val);
|
||||
#endif
|
||||
|
||||
case MRB_TT_FIXNUM:
|
||||
if (base != 0) goto arg_error;
|
||||
return val;
|
||||
|
||||
case MRB_TT_STRING:
|
||||
string_conv:
|
||||
return mrb_str_to_inum(mrb, val, base, TRUE);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (base != 0) {
|
||||
tmp = mrb_check_string_type(mrb, val);
|
||||
if (!mrb_nil_p(tmp)) {
|
||||
val = tmp;
|
||||
goto string_conv;
|
||||
}
|
||||
arg_error:
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "base specified for non string value");
|
||||
}
|
||||
/* to raise TypeError */
|
||||
return mrb_to_int(mrb, val);
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_Integer(mrb_state *mrb, mrb_value val)
|
||||
{
|
||||
return mrb_convert_to_integer(mrb, val, 0);
|
||||
}
|
||||
|
||||
#ifndef MRB_WITHOUT_FLOAT
|
||||
MRB_API mrb_value
|
||||
mrb_Float(mrb_state *mrb, mrb_value val)
|
||||
{
|
||||
if (mrb_nil_p(val)) {
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "can't convert nil into Float");
|
||||
}
|
||||
switch (mrb_type(val)) {
|
||||
case MRB_TT_FIXNUM:
|
||||
return mrb_float_value(mrb, (mrb_float)mrb_fixnum(val));
|
||||
|
||||
case MRB_TT_FLOAT:
|
||||
return val;
|
||||
|
||||
case MRB_TT_STRING:
|
||||
return mrb_float_value(mrb, mrb_str_to_dbl(mrb, val, TRUE));
|
||||
|
||||
default:
|
||||
return mrb_convert_type(mrb, val, MRB_TT_FLOAT, "Float", "to_f");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_to_str(mrb_state *mrb, mrb_value val)
|
||||
{
|
||||
return mrb_ensure_string_type(mrb, val);
|
||||
}
|
||||
|
||||
/* obsolete: use mrb_ensure_string_type() instead */
|
||||
MRB_API mrb_value
|
||||
mrb_string_type(mrb_state *mrb, mrb_value str)
|
||||
{
|
||||
return mrb_ensure_string_type(mrb, str);
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_ensure_string_type(mrb_state *mrb, mrb_value str)
|
||||
{
|
||||
if (!mrb_string_p(str)) {
|
||||
mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to String", str);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_check_string_type(mrb_state *mrb, mrb_value str)
|
||||
{
|
||||
if (!mrb_string_p(str)) return mrb_nil_value();
|
||||
return str;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_ensure_array_type(mrb_state *mrb, mrb_value ary)
|
||||
{
|
||||
if (!mrb_array_p(ary)) {
|
||||
mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to Array", ary);
|
||||
}
|
||||
return ary;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_check_array_type(mrb_state *mrb, mrb_value ary)
|
||||
{
|
||||
if (!mrb_array_p(ary)) return mrb_nil_value();
|
||||
return ary;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_ensure_hash_type(mrb_state *mrb, mrb_value hash)
|
||||
{
|
||||
if (!mrb_hash_p(hash)) {
|
||||
mrb_raisef(mrb, E_TYPE_ERROR, "%Y cannot be converted to Hash", hash);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_check_hash_type(mrb_state *mrb, mrb_value hash)
|
||||
{
|
||||
if (!mrb_hash_p(hash)) return mrb_nil_value();
|
||||
return hash;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_inspect(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
return mrb_obj_as_string(mrb, mrb_funcall(mrb, obj, "inspect", 0));
|
||||
}
|
||||
|
||||
MRB_API mrb_bool
|
||||
mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2)
|
||||
{
|
||||
if (mrb_obj_eq(mrb, obj1, obj2)) return TRUE;
|
||||
return mrb_test(mrb_funcall(mrb, obj1, "eql?", 1, obj2));
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
/* this header file is to be removed soon. */
|
||||
#include <mruby/opcode.h>
|
||||
Vendored
+207
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
** pool.c - memory pool
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <mruby.h>
|
||||
|
||||
/* configuration section */
|
||||
/* allocated memory address should be multiple of POOL_ALIGNMENT */
|
||||
/* or undef it if alignment does not matter */
|
||||
#ifndef POOL_ALIGNMENT
|
||||
#if INTPTR_MAX == INT64_MAX
|
||||
#define POOL_ALIGNMENT 8
|
||||
#else
|
||||
#define POOL_ALIGNMENT 4
|
||||
#endif
|
||||
#endif
|
||||
/* page size of memory pool */
|
||||
#ifndef POOL_PAGE_SIZE
|
||||
#define POOL_PAGE_SIZE 16000
|
||||
#endif
|
||||
/* end of configuration section */
|
||||
|
||||
/* Disable MSVC warning "C4200: nonstandard extension used: zero-sized array
|
||||
* in struct/union" when in C++ mode */
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4200)
|
||||
#endif
|
||||
|
||||
struct mrb_pool_page {
|
||||
struct mrb_pool_page *next;
|
||||
size_t offset;
|
||||
size_t len;
|
||||
void *last;
|
||||
char page[];
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
struct mrb_pool {
|
||||
mrb_state *mrb;
|
||||
struct mrb_pool_page *pages;
|
||||
};
|
||||
|
||||
#undef TEST_POOL
|
||||
#ifdef TEST_POOL
|
||||
|
||||
#define mrb_malloc_simple(m,s) malloc(s)
|
||||
#define mrb_free(m,p) free(p)
|
||||
#endif
|
||||
|
||||
#ifdef POOL_ALIGNMENT
|
||||
# define ALIGN_PADDING(x) ((SIZE_MAX - (x) + 1) & (POOL_ALIGNMENT - 1))
|
||||
#else
|
||||
# define ALIGN_PADDING(x) (0)
|
||||
#endif
|
||||
|
||||
MRB_API mrb_pool*
|
||||
mrb_pool_open(mrb_state *mrb)
|
||||
{
|
||||
mrb_pool *pool = (mrb_pool *)mrb_malloc_simple(mrb, sizeof(mrb_pool));
|
||||
|
||||
if (pool) {
|
||||
pool->mrb = mrb;
|
||||
pool->pages = NULL;
|
||||
}
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
MRB_API void
|
||||
mrb_pool_close(mrb_pool *pool)
|
||||
{
|
||||
struct mrb_pool_page *page, *tmp;
|
||||
|
||||
if (!pool) return;
|
||||
page = pool->pages;
|
||||
while (page) {
|
||||
tmp = page;
|
||||
page = page->next;
|
||||
mrb_free(pool->mrb, tmp);
|
||||
}
|
||||
mrb_free(pool->mrb, pool);
|
||||
}
|
||||
|
||||
static struct mrb_pool_page*
|
||||
page_alloc(mrb_pool *pool, size_t len)
|
||||
{
|
||||
struct mrb_pool_page *page;
|
||||
|
||||
if (len < POOL_PAGE_SIZE)
|
||||
len = POOL_PAGE_SIZE;
|
||||
page = (struct mrb_pool_page *)mrb_malloc_simple(pool->mrb, sizeof(struct mrb_pool_page)+len);
|
||||
if (page) {
|
||||
page->offset = 0;
|
||||
page->len = len;
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
MRB_API void*
|
||||
mrb_pool_alloc(mrb_pool *pool, size_t len)
|
||||
{
|
||||
struct mrb_pool_page *page;
|
||||
size_t n;
|
||||
|
||||
if (!pool) return NULL;
|
||||
len += ALIGN_PADDING(len);
|
||||
page = pool->pages;
|
||||
while (page) {
|
||||
if (page->offset + len <= page->len) {
|
||||
n = page->offset;
|
||||
page->offset += len;
|
||||
page->last = (char*)page->page+n;
|
||||
return page->last;
|
||||
}
|
||||
page = page->next;
|
||||
}
|
||||
page = page_alloc(pool, len);
|
||||
if (!page) return NULL;
|
||||
page->offset = len;
|
||||
page->next = pool->pages;
|
||||
pool->pages = page;
|
||||
|
||||
page->last = (void*)page->page;
|
||||
return page->last;
|
||||
}
|
||||
|
||||
MRB_API mrb_bool
|
||||
mrb_pool_can_realloc(mrb_pool *pool, void *p, size_t len)
|
||||
{
|
||||
struct mrb_pool_page *page;
|
||||
|
||||
if (!pool) return FALSE;
|
||||
len += ALIGN_PADDING(len);
|
||||
page = pool->pages;
|
||||
while (page) {
|
||||
if (page->last == p) {
|
||||
size_t beg;
|
||||
|
||||
beg = (char*)p - page->page;
|
||||
if (beg + len > page->len) return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
page = page->next;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
MRB_API void*
|
||||
mrb_pool_realloc(mrb_pool *pool, void *p, size_t oldlen, size_t newlen)
|
||||
{
|
||||
struct mrb_pool_page *page;
|
||||
void *np;
|
||||
|
||||
if (!pool) return NULL;
|
||||
oldlen += ALIGN_PADDING(oldlen);
|
||||
newlen += ALIGN_PADDING(newlen);
|
||||
page = pool->pages;
|
||||
while (page) {
|
||||
if (page->last == p) {
|
||||
size_t beg;
|
||||
|
||||
beg = (char*)p - page->page;
|
||||
if (beg + oldlen != page->offset) break;
|
||||
if (beg + newlen > page->len) {
|
||||
page->offset = beg;
|
||||
break;
|
||||
}
|
||||
page->offset = beg + newlen;
|
||||
return p;
|
||||
}
|
||||
page = page->next;
|
||||
}
|
||||
np = mrb_pool_alloc(pool, newlen);
|
||||
if (np == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(np, p, oldlen);
|
||||
return np;
|
||||
}
|
||||
|
||||
#ifdef TEST_POOL
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
int i, len = 250;
|
||||
mrb_pool *pool;
|
||||
void *p;
|
||||
|
||||
pool = mrb_pool_open(NULL);
|
||||
p = mrb_pool_alloc(pool, len);
|
||||
for (i=1; i<20; i++) {
|
||||
printf("%p (len=%d) %ud\n", p, len, mrb_pool_can_realloc(pool, p, len*2));
|
||||
p = mrb_pool_realloc(pool, p, len, len*2);
|
||||
len *= 2;
|
||||
}
|
||||
mrb_pool_close(pool);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
** print.c - Kernel.#p
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
*/
|
||||
|
||||
#include <mruby.h>
|
||||
#include <mruby/string.h>
|
||||
#include <mruby/variable.h>
|
||||
#include <mruby/error.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef MRB_DISABLE_STDIO
|
||||
static void
|
||||
printcstr(const char *str, size_t len, FILE *stream)
|
||||
{
|
||||
if (str) {
|
||||
fwrite(str, len, 1, stream);
|
||||
putc('\n', stream);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
printstr(mrb_value obj, FILE *stream)
|
||||
{
|
||||
if (mrb_string_p(obj)) {
|
||||
printcstr(RSTRING_PTR(obj), RSTRING_LEN(obj), stream);
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define printcstr(str, len, stream) (void)0
|
||||
# define printstr(obj, stream) (void)0
|
||||
#endif
|
||||
|
||||
void
|
||||
mrb_core_init_printabort(void)
|
||||
{
|
||||
static const char *str = "Failed mruby core initialization";
|
||||
printcstr(str, strlen(str), stdout);
|
||||
}
|
||||
|
||||
MRB_API void
|
||||
mrb_p(mrb_state *mrb, mrb_value obj)
|
||||
{
|
||||
if (mrb_type(obj) == MRB_TT_EXCEPTION && mrb_obj_ptr(obj) == mrb->nomem_err) {
|
||||
static const char *str = "Out of memory";
|
||||
printcstr(str, strlen(str), stdout);
|
||||
}
|
||||
else {
|
||||
printstr(mrb_inspect(mrb, obj), stdout);
|
||||
}
|
||||
}
|
||||
|
||||
MRB_API void
|
||||
mrb_print_error(mrb_state *mrb)
|
||||
{
|
||||
mrb_print_backtrace(mrb);
|
||||
}
|
||||
|
||||
MRB_API void
|
||||
mrb_show_version(mrb_state *mrb)
|
||||
{
|
||||
printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_DESCRIPTION")), stdout);
|
||||
}
|
||||
|
||||
MRB_API void
|
||||
mrb_show_copyright(mrb_state *mrb)
|
||||
{
|
||||
printstr(mrb_const_get(mrb, mrb_obj_value(mrb->object_class), mrb_intern_lit(mrb, "MRUBY_COPYRIGHT")), stdout);
|
||||
}
|
||||
Vendored
+333
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
** proc.c - Proc class
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
*/
|
||||
|
||||
#include <mruby.h>
|
||||
#include <mruby/class.h>
|
||||
#include <mruby/proc.h>
|
||||
#include <mruby/opcode.h>
|
||||
#include <mruby/data.h>
|
||||
|
||||
static const mrb_code call_iseq[] = {
|
||||
OP_CALL,
|
||||
};
|
||||
|
||||
struct RProc*
|
||||
mrb_proc_new(mrb_state *mrb, mrb_irep *irep)
|
||||
{
|
||||
struct RProc *p;
|
||||
mrb_callinfo *ci = mrb->c->ci;
|
||||
|
||||
p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
|
||||
if (ci) {
|
||||
struct RClass *tc = NULL;
|
||||
|
||||
if (ci->proc) {
|
||||
tc = MRB_PROC_TARGET_CLASS(ci->proc);
|
||||
}
|
||||
if (tc == NULL) {
|
||||
tc = ci->target_class;
|
||||
}
|
||||
p->upper = ci->proc;
|
||||
p->e.target_class = tc;
|
||||
}
|
||||
p->body.irep = irep;
|
||||
mrb_irep_incref(mrb, irep);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static struct REnv*
|
||||
env_new(mrb_state *mrb, mrb_int nlocals)
|
||||
{
|
||||
struct REnv *e;
|
||||
mrb_callinfo *ci = mrb->c->ci;
|
||||
int bidx;
|
||||
|
||||
e = (struct REnv*)mrb_obj_alloc(mrb, MRB_TT_ENV, NULL);
|
||||
MRB_ENV_SET_LEN(e, nlocals);
|
||||
bidx = ci->argc;
|
||||
if (ci->argc < 0) bidx = 2;
|
||||
else bidx += 1;
|
||||
MRB_ENV_SET_BIDX(e, bidx);
|
||||
e->mid = ci->mid;
|
||||
e->stack = mrb->c->stack;
|
||||
e->cxt = mrb->c;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static void
|
||||
closure_setup(mrb_state *mrb, struct RProc *p)
|
||||
{
|
||||
mrb_callinfo *ci = mrb->c->ci;
|
||||
struct RProc *up = p->upper;
|
||||
struct REnv *e = NULL;
|
||||
|
||||
if (ci && ci->env) {
|
||||
e = ci->env;
|
||||
}
|
||||
else if (up) {
|
||||
struct RClass *tc = MRB_PROC_TARGET_CLASS(p);
|
||||
|
||||
e = env_new(mrb, up->body.irep->nlocals);
|
||||
ci->env = e;
|
||||
if (tc) {
|
||||
e->c = tc;
|
||||
mrb_field_write_barrier(mrb, (struct RBasic*)e, (struct RBasic*)tc);
|
||||
}
|
||||
if (MRB_PROC_ENV_P(up) && MRB_PROC_ENV(up)->cxt == NULL) {
|
||||
e->mid = MRB_PROC_ENV(up)->mid;
|
||||
}
|
||||
}
|
||||
if (e) {
|
||||
p->e.env = e;
|
||||
p->flags |= MRB_PROC_ENVSET;
|
||||
mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e);
|
||||
}
|
||||
}
|
||||
|
||||
struct RProc*
|
||||
mrb_closure_new(mrb_state *mrb, mrb_irep *irep)
|
||||
{
|
||||
struct RProc *p = mrb_proc_new(mrb, irep);
|
||||
|
||||
closure_setup(mrb, p);
|
||||
return p;
|
||||
}
|
||||
|
||||
MRB_API struct RProc*
|
||||
mrb_proc_new_cfunc(mrb_state *mrb, mrb_func_t func)
|
||||
{
|
||||
struct RProc *p;
|
||||
|
||||
p = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb->proc_class);
|
||||
p->body.func = func;
|
||||
p->flags |= MRB_PROC_CFUNC_FL;
|
||||
p->upper = 0;
|
||||
p->e.target_class = 0;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
MRB_API struct RProc*
|
||||
mrb_proc_new_cfunc_with_env(mrb_state *mrb, mrb_func_t func, mrb_int argc, const mrb_value *argv)
|
||||
{
|
||||
struct RProc *p = mrb_proc_new_cfunc(mrb, func);
|
||||
struct REnv *e;
|
||||
int i;
|
||||
|
||||
p->e.env = e = env_new(mrb, argc);
|
||||
p->flags |= MRB_PROC_ENVSET;
|
||||
mrb_field_write_barrier(mrb, (struct RBasic*)p, (struct RBasic*)e);
|
||||
MRB_ENV_CLOSE(e);
|
||||
|
||||
/* NOTE: Prevents keeping invalid addresses when NoMemoryError is raised from `mrb_malloc()`. */
|
||||
e->stack = NULL;
|
||||
MRB_ENV_SET_LEN(e, 0);
|
||||
|
||||
e->stack = (mrb_value*)mrb_malloc(mrb, sizeof(mrb_value) * argc);
|
||||
MRB_ENV_SET_LEN(e, argc);
|
||||
|
||||
if (argv) {
|
||||
for (i = 0; i < argc; ++i) {
|
||||
e->stack[i] = argv[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = 0; i < argc; ++i) {
|
||||
SET_NIL_VALUE(e->stack[i]);
|
||||
}
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
MRB_API struct RProc*
|
||||
mrb_closure_new_cfunc(mrb_state *mrb, mrb_func_t func, int nlocals)
|
||||
{
|
||||
return mrb_proc_new_cfunc_with_env(mrb, func, nlocals, NULL);
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx)
|
||||
{
|
||||
struct RProc *p = mrb->c->ci->proc;
|
||||
struct REnv *e;
|
||||
|
||||
if (!p || !MRB_PROC_CFUNC_P(p)) {
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from non-cfunc proc.");
|
||||
}
|
||||
e = MRB_PROC_ENV(p);
|
||||
if (!e) {
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "Can't get cfunc env from cfunc Proc without REnv.");
|
||||
}
|
||||
if (idx < 0 || MRB_ENV_LEN(e) <= idx) {
|
||||
mrb_raisef(mrb, E_INDEX_ERROR, "Env index out of range: %i (expected: 0 <= index < %i)",
|
||||
idx, MRB_ENV_LEN(e));
|
||||
}
|
||||
|
||||
return e->stack[idx];
|
||||
}
|
||||
|
||||
void
|
||||
mrb_proc_copy(struct RProc *a, struct RProc *b)
|
||||
{
|
||||
if (a->body.irep) {
|
||||
/* already initialized proc */
|
||||
return;
|
||||
}
|
||||
a->flags = b->flags;
|
||||
a->body = b->body;
|
||||
if (!MRB_PROC_CFUNC_P(a) && a->body.irep) {
|
||||
a->body.irep->refcnt++;
|
||||
}
|
||||
a->upper = b->upper;
|
||||
a->e.env = b->e.env;
|
||||
/* a->e.target_class = a->e.target_class; */
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
mrb_proc_s_new(mrb_state *mrb, mrb_value proc_class)
|
||||
{
|
||||
mrb_value blk;
|
||||
mrb_value proc;
|
||||
struct RProc *p;
|
||||
|
||||
/* Calling Proc.new without a block is not implemented yet */
|
||||
mrb_get_args(mrb, "&!", &blk);
|
||||
p = (struct RProc *)mrb_obj_alloc(mrb, MRB_TT_PROC, mrb_class_ptr(proc_class));
|
||||
mrb_proc_copy(p, mrb_proc_ptr(blk));
|
||||
proc = mrb_obj_value(p);
|
||||
mrb_funcall_with_block(mrb, proc, mrb_intern_lit(mrb, "initialize"), 0, NULL, proc);
|
||||
if (!MRB_PROC_STRICT_P(p) &&
|
||||
mrb->c->ci > mrb->c->cibase && MRB_PROC_ENV(p) == mrb->c->ci[-1].env) {
|
||||
p->flags |= MRB_PROC_ORPHAN;
|
||||
}
|
||||
return proc;
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
mrb_proc_init_copy(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value proc = mrb_get_arg1(mrb);
|
||||
|
||||
if (!mrb_proc_p(proc)) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc");
|
||||
}
|
||||
mrb_proc_copy(mrb_proc_ptr(self), mrb_proc_ptr(proc));
|
||||
return self;
|
||||
}
|
||||
|
||||
/* 15.2.17.4.2 */
|
||||
static mrb_value
|
||||
proc_arity(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
return mrb_fixnum_value(mrb_proc_arity(mrb_proc_ptr(self)));
|
||||
}
|
||||
|
||||
/* 15.3.1.2.6 */
|
||||
/* 15.3.1.3.27 */
|
||||
/*
|
||||
* call-seq:
|
||||
* lambda { |...| block } -> a_proc
|
||||
*
|
||||
* Equivalent to <code>Proc.new</code>, except the resulting Proc objects
|
||||
* check the number of parameters passed when called.
|
||||
*/
|
||||
static mrb_value
|
||||
proc_lambda(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value blk;
|
||||
struct RProc *p;
|
||||
|
||||
mrb_get_args(mrb, "&", &blk);
|
||||
if (mrb_nil_p(blk)) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "tried to create Proc object without a block");
|
||||
}
|
||||
if (!mrb_proc_p(blk)) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "not a proc");
|
||||
}
|
||||
p = mrb_proc_ptr(blk);
|
||||
if (!MRB_PROC_STRICT_P(p)) {
|
||||
struct RProc *p2 = (struct RProc*)mrb_obj_alloc(mrb, MRB_TT_PROC, p->c);
|
||||
mrb_proc_copy(p2, p);
|
||||
p2->flags |= MRB_PROC_STRICT;
|
||||
return mrb_obj_value(p2);
|
||||
}
|
||||
return blk;
|
||||
}
|
||||
|
||||
mrb_int
|
||||
mrb_proc_arity(const struct RProc *p)
|
||||
{
|
||||
struct mrb_irep *irep;
|
||||
const mrb_code *pc;
|
||||
mrb_aspec aspec;
|
||||
int ma, op, ra, pa, arity;
|
||||
|
||||
if (MRB_PROC_CFUNC_P(p)) {
|
||||
/* TODO cfunc aspec not implemented yet */
|
||||
return -1;
|
||||
}
|
||||
|
||||
irep = p->body.irep;
|
||||
if (!irep) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pc = irep->iseq;
|
||||
/* arity is depend on OP_ENTER */
|
||||
if (*pc != OP_ENTER) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
aspec = PEEK_W(pc+1);
|
||||
ma = MRB_ASPEC_REQ(aspec);
|
||||
op = MRB_ASPEC_OPT(aspec);
|
||||
ra = MRB_ASPEC_REST(aspec);
|
||||
pa = MRB_ASPEC_POST(aspec);
|
||||
arity = ra || (MRB_PROC_STRICT_P(p) && op) ? -(ma + pa + 1) : ma + pa;
|
||||
|
||||
return arity;
|
||||
}
|
||||
|
||||
static void
|
||||
tempirep_free(mrb_state *mrb, void *p)
|
||||
{
|
||||
if (p) mrb_irep_free(mrb, (mrb_irep *)p);
|
||||
}
|
||||
|
||||
static const mrb_data_type tempirep_type = { "temporary irep", tempirep_free };
|
||||
|
||||
void
|
||||
mrb_init_proc(mrb_state *mrb)
|
||||
{
|
||||
struct RProc *p;
|
||||
mrb_method_t m;
|
||||
struct RData *irep_obj = mrb_data_object_alloc(mrb, mrb->object_class, NULL, &tempirep_type);
|
||||
mrb_irep *call_irep;
|
||||
static const mrb_irep mrb_irep_zero = { 0 };
|
||||
|
||||
call_irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep));
|
||||
irep_obj->data = call_irep;
|
||||
*call_irep = mrb_irep_zero;
|
||||
call_irep->flags = MRB_ISEQ_NO_FREE;
|
||||
call_irep->iseq = call_iseq;
|
||||
call_irep->ilen = 1;
|
||||
call_irep->nregs = 2; /* receiver and block */
|
||||
|
||||
mrb_define_class_method(mrb, mrb->proc_class, "new", mrb_proc_s_new, MRB_ARGS_NONE()|MRB_ARGS_BLOCK());
|
||||
mrb_define_method(mrb, mrb->proc_class, "initialize_copy", mrb_proc_init_copy, MRB_ARGS_REQ(1));
|
||||
mrb_define_method(mrb, mrb->proc_class, "arity", proc_arity, MRB_ARGS_NONE());
|
||||
|
||||
p = mrb_proc_new(mrb, call_irep);
|
||||
irep_obj->data = NULL;
|
||||
MRB_METHOD_FROM_PROC(m, p);
|
||||
mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "call"), m);
|
||||
mrb_define_method_raw(mrb, mrb->proc_class, mrb_intern_lit(mrb, "[]"), m);
|
||||
|
||||
mrb_define_class_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); /* 15.3.1.2.6 */
|
||||
mrb_define_method(mrb, mrb->kernel_module, "lambda", proc_lambda, MRB_ARGS_NONE()|MRB_ARGS_BLOCK()); /* 15.3.1.3.27 */
|
||||
}
|
||||
+435
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
** range.c - Range class
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
*/
|
||||
|
||||
#include <mruby.h>
|
||||
#include <mruby/class.h>
|
||||
#include <mruby/range.h>
|
||||
#include <mruby/string.h>
|
||||
#include <mruby/array.h>
|
||||
|
||||
#define RANGE_INITIALIZED_MASK 1
|
||||
#define RANGE_INITIALIZED(p) ((p)->flags |= RANGE_INITIALIZED_MASK)
|
||||
#define RANGE_INITIALIZED_P(p) ((p)->flags & RANGE_INITIALIZED_MASK)
|
||||
|
||||
static void
|
||||
r_check(mrb_state *mrb, mrb_value a, mrb_value b)
|
||||
{
|
||||
enum mrb_vtype ta;
|
||||
enum mrb_vtype tb;
|
||||
mrb_int n;
|
||||
|
||||
ta = mrb_type(a);
|
||||
tb = mrb_type(b);
|
||||
#ifdef MRB_WITHOUT_FLOAT
|
||||
if (ta == MRB_TT_FIXNUM && tb == MRB_TT_FIXNUM ) {
|
||||
#else
|
||||
if ((ta == MRB_TT_FIXNUM || ta == MRB_TT_FLOAT) &&
|
||||
(tb == MRB_TT_FIXNUM || tb == MRB_TT_FLOAT)) {
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
n = mrb_cmp(mrb, a, b);
|
||||
if (n == -2) { /* can not be compared */
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "bad value for range");
|
||||
}
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
r_le(mrb_state *mrb, mrb_value a, mrb_value b)
|
||||
{
|
||||
mrb_int n = mrb_cmp(mrb, a, b);
|
||||
|
||||
if (n == 0 || n == -1) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
r_gt(mrb_state *mrb, mrb_value a, mrb_value b)
|
||||
{
|
||||
return mrb_cmp(mrb, a, b) == 1;
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
r_ge(mrb_state *mrb, mrb_value a, mrb_value b)
|
||||
{
|
||||
mrb_int n = mrb_cmp(mrb, a, b);
|
||||
|
||||
if (n == 0 || n == 1) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
range_ptr_alloc_edges(mrb_state *mrb, struct RRange *r)
|
||||
{
|
||||
#ifndef MRB_RANGE_EMBED
|
||||
r->edges = (mrb_range_edges *)mrb_malloc(mrb, sizeof(mrb_range_edges));
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct RRange *
|
||||
range_ptr_init(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
|
||||
{
|
||||
r_check(mrb, beg, end);
|
||||
|
||||
if (r) {
|
||||
if (RANGE_INITIALIZED_P(r)) {
|
||||
/* Ranges are immutable, so that they should be initialized only once. */
|
||||
mrb_name_error(mrb, mrb_intern_lit(mrb, "initialize"), "'initialize' called twice");
|
||||
}
|
||||
else {
|
||||
range_ptr_alloc_edges(mrb, r);
|
||||
}
|
||||
}
|
||||
else {
|
||||
r = (struct RRange*)mrb_obj_alloc(mrb, MRB_TT_RANGE, mrb->range_class);
|
||||
range_ptr_alloc_edges(mrb, r);
|
||||
}
|
||||
|
||||
RANGE_BEG(r) = beg;
|
||||
RANGE_END(r) = end;
|
||||
RANGE_EXCL(r) = excl;
|
||||
RANGE_INITIALIZED(r);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void
|
||||
range_ptr_replace(mrb_state *mrb, struct RRange *r, mrb_value beg, mrb_value end, mrb_bool excl)
|
||||
{
|
||||
range_ptr_init(mrb, r, beg, end, excl);
|
||||
mrb_write_barrier(mrb, (struct RBasic*)r);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* rng.first => obj
|
||||
* rng.begin => obj
|
||||
*
|
||||
* Returns the first object in <i>rng</i>.
|
||||
*/
|
||||
static mrb_value
|
||||
range_beg(mrb_state *mrb, mrb_value range)
|
||||
{
|
||||
return mrb_range_beg(mrb, range);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* rng.end => obj
|
||||
* rng.last => obj
|
||||
*
|
||||
* Returns the object that defines the end of <i>rng</i>.
|
||||
*
|
||||
* (1..10).end #=> 10
|
||||
* (1...10).end #=> 10
|
||||
*/
|
||||
static mrb_value
|
||||
range_end(mrb_state *mrb, mrb_value range)
|
||||
{
|
||||
return mrb_range_end(mrb, range);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* range.exclude_end? => true or false
|
||||
*
|
||||
* Returns <code>true</code> if <i>range</i> excludes its end value.
|
||||
*/
|
||||
static mrb_value
|
||||
range_excl(mrb_state *mrb, mrb_value range)
|
||||
{
|
||||
return mrb_bool_value(mrb_range_excl_p(mrb, range));
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* Range.new(start, end, exclusive=false) => range
|
||||
*
|
||||
* Constructs a range using the given <i>start</i> and <i>end</i>. If the third
|
||||
* parameter is omitted or is <code>false</code>, the <i>range</i> will include
|
||||
* the end object; otherwise, it will be excluded.
|
||||
*/
|
||||
static mrb_value
|
||||
range_initialize(mrb_state *mrb, mrb_value range)
|
||||
{
|
||||
mrb_value beg, end;
|
||||
mrb_bool exclusive = FALSE;
|
||||
|
||||
mrb_get_args(mrb, "oo|b", &beg, &end, &exclusive);
|
||||
range_ptr_replace(mrb, mrb_range_raw_ptr(range), beg, end, exclusive);
|
||||
return range;
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* range == obj => true or false
|
||||
*
|
||||
* Returns <code>true</code> only if
|
||||
* 1) <i>obj</i> is a Range,
|
||||
* 2) <i>obj</i> has equivalent beginning and end items (by comparing them with <code>==</code>),
|
||||
* 3) <i>obj</i> has the same #exclude_end? setting as <i>rng</t>.
|
||||
*
|
||||
* (0..2) == (0..2) #=> true
|
||||
* (0..2) == Range.new(0,2) #=> true
|
||||
* (0..2) == (0...2) #=> false
|
||||
*/
|
||||
static mrb_value
|
||||
range_eq(mrb_state *mrb, mrb_value range)
|
||||
{
|
||||
struct RRange *rr;
|
||||
struct RRange *ro;
|
||||
mrb_value obj = mrb_get_arg1(mrb);
|
||||
mrb_bool v1, v2;
|
||||
|
||||
if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
|
||||
if (!mrb_obj_is_instance_of(mrb, obj, mrb_obj_class(mrb, range))) { /* same class? */
|
||||
return mrb_false_value();
|
||||
}
|
||||
|
||||
rr = mrb_range_ptr(mrb, range);
|
||||
ro = mrb_range_ptr(mrb, obj);
|
||||
v1 = mrb_equal(mrb, RANGE_BEG(rr), RANGE_BEG(ro));
|
||||
v2 = mrb_equal(mrb, RANGE_END(rr), RANGE_END(ro));
|
||||
if (!v1 || !v2 || RANGE_EXCL(rr) != RANGE_EXCL(ro)) {
|
||||
return mrb_false_value();
|
||||
}
|
||||
return mrb_true_value();
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* range === obj => true or false
|
||||
* range.member?(val) => true or false
|
||||
* range.include?(val) => true or false
|
||||
*/
|
||||
static mrb_value
|
||||
range_include(mrb_state *mrb, mrb_value range)
|
||||
{
|
||||
mrb_value val = mrb_get_arg1(mrb);
|
||||
struct RRange *r = mrb_range_ptr(mrb, range);
|
||||
mrb_value beg, end;
|
||||
mrb_bool include_p;
|
||||
|
||||
beg = RANGE_BEG(r);
|
||||
end = RANGE_END(r);
|
||||
include_p = r_le(mrb, beg, val) && /* beg <= val */
|
||||
(RANGE_EXCL(r) ? r_gt(mrb, end, val) /* end > val */
|
||||
: r_ge(mrb, end, val)); /* end >= val */
|
||||
|
||||
return mrb_bool_value(include_p);
|
||||
}
|
||||
|
||||
/* 15.2.14.4.12(x) */
|
||||
/*
|
||||
* call-seq:
|
||||
* rng.to_s -> string
|
||||
*
|
||||
* Convert this range object to a printable form.
|
||||
*/
|
||||
static mrb_value
|
||||
range_to_s(mrb_state *mrb, mrb_value range)
|
||||
{
|
||||
mrb_value str, str2;
|
||||
struct RRange *r = mrb_range_ptr(mrb, range);
|
||||
|
||||
str = mrb_obj_as_string(mrb, RANGE_BEG(r));
|
||||
str2 = mrb_obj_as_string(mrb, RANGE_END(r));
|
||||
str = mrb_str_dup(mrb, str);
|
||||
mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
|
||||
mrb_str_cat_str(mrb, str, str2);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/* 15.2.14.4.13(x) */
|
||||
/*
|
||||
* call-seq:
|
||||
* rng.inspect -> string
|
||||
*
|
||||
* Convert this range object to a printable form (using
|
||||
* <code>inspect</code> to convert the start and end
|
||||
* objects).
|
||||
*/
|
||||
static mrb_value
|
||||
range_inspect(mrb_state *mrb, mrb_value range)
|
||||
{
|
||||
mrb_value str, str2;
|
||||
struct RRange *r = mrb_range_ptr(mrb, range);
|
||||
|
||||
str = mrb_inspect(mrb, RANGE_BEG(r));
|
||||
str2 = mrb_inspect(mrb, RANGE_END(r));
|
||||
str = mrb_str_dup(mrb, str);
|
||||
mrb_str_cat(mrb, str, "...", RANGE_EXCL(r) ? 3 : 2);
|
||||
mrb_str_cat_str(mrb, str, str2);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/* 15.2.14.4.14(x) */
|
||||
/*
|
||||
* call-seq:
|
||||
* rng.eql?(obj) -> true or false
|
||||
*
|
||||
* Returns <code>true</code> only if <i>obj</i> is a Range, has equivalent
|
||||
* beginning and end items (by comparing them with #eql?), and has the same
|
||||
* #exclude_end? setting as <i>rng</i>.
|
||||
*
|
||||
* (0..2).eql?(0..2) #=> true
|
||||
* (0..2).eql?(Range.new(0,2)) #=> true
|
||||
* (0..2).eql?(0...2) #=> false
|
||||
*/
|
||||
static mrb_value
|
||||
range_eql(mrb_state *mrb, mrb_value range)
|
||||
{
|
||||
mrb_value obj = mrb_get_arg1(mrb);
|
||||
struct RRange *r, *o;
|
||||
|
||||
if (mrb_obj_equal(mrb, range, obj)) return mrb_true_value();
|
||||
if (!mrb_obj_is_kind_of(mrb, obj, mrb->range_class)) return mrb_false_value();
|
||||
if (!mrb_range_p(obj)) return mrb_false_value();
|
||||
|
||||
r = mrb_range_ptr(mrb, range);
|
||||
o = mrb_range_ptr(mrb, obj);
|
||||
if (!mrb_eql(mrb, RANGE_BEG(r), RANGE_BEG(o)) ||
|
||||
!mrb_eql(mrb, RANGE_END(r), RANGE_END(o)) ||
|
||||
(RANGE_EXCL(r) != RANGE_EXCL(o))) {
|
||||
return mrb_false_value();
|
||||
}
|
||||
return mrb_true_value();
|
||||
}
|
||||
|
||||
/* 15.2.14.4.15(x) */
|
||||
static mrb_value
|
||||
range_initialize_copy(mrb_state *mrb, mrb_value copy)
|
||||
{
|
||||
mrb_value src = mrb_get_arg1(mrb);
|
||||
struct RRange *r;
|
||||
|
||||
if (mrb_obj_equal(mrb, copy, src)) return copy;
|
||||
if (!mrb_obj_is_instance_of(mrb, src, mrb_obj_class(mrb, copy))) {
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "wrong argument class");
|
||||
}
|
||||
|
||||
r = mrb_range_ptr(mrb, src);
|
||||
range_ptr_replace(mrb, mrb_range_raw_ptr(copy), RANGE_BEG(r), RANGE_END(r), RANGE_EXCL(r));
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
mrb_value
|
||||
mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, const mrb_value *argv, mrb_value (*func)(mrb_state*, mrb_value, mrb_int))
|
||||
{
|
||||
mrb_int i, j, beg, len;
|
||||
mrb_value result;
|
||||
result = mrb_ary_new(mrb);
|
||||
|
||||
for (i = 0; i < argc; ++i) {
|
||||
if (mrb_fixnum_p(argv[i])) {
|
||||
mrb_ary_push(mrb, result, func(mrb, obj, mrb_fixnum(argv[i])));
|
||||
}
|
||||
else if (mrb_range_beg_len(mrb, argv[i], &beg, &len, olen, FALSE) == MRB_RANGE_OK) {
|
||||
mrb_int const end = olen < beg + len ? olen : beg + len;
|
||||
for (j = beg; j < end; ++j) {
|
||||
mrb_ary_push(mrb, result, func(mrb, obj, j));
|
||||
}
|
||||
|
||||
for (; j < beg + len; ++j) {
|
||||
mrb_ary_push(mrb, result, mrb_nil_value());
|
||||
}
|
||||
}
|
||||
else {
|
||||
mrb_raisef(mrb, E_TYPE_ERROR, "invalid values selector: %v", argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
mrb_gc_mark_range(mrb_state *mrb, struct RRange *r)
|
||||
{
|
||||
if (RANGE_INITIALIZED_P(r)) {
|
||||
mrb_gc_mark_value(mrb, RANGE_BEG(r));
|
||||
mrb_gc_mark_value(mrb, RANGE_END(r));
|
||||
}
|
||||
}
|
||||
|
||||
MRB_API struct RRange*
|
||||
mrb_range_ptr(mrb_state *mrb, mrb_value range)
|
||||
{
|
||||
struct RRange *r = mrb_range_raw_ptr(range);
|
||||
|
||||
/* check for if #initialize_copy was removed [#3320] */
|
||||
if (!RANGE_INITIALIZED_P(r)) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "uninitialized range");
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_range_new(mrb_state *mrb, mrb_value beg, mrb_value end, mrb_bool excl)
|
||||
{
|
||||
struct RRange *r = range_ptr_init(mrb, NULL, beg, end, excl);
|
||||
return mrb_range_value(r);
|
||||
}
|
||||
|
||||
MRB_API enum mrb_range_beg_len
|
||||
mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp, mrb_int len, mrb_bool trunc)
|
||||
{
|
||||
mrb_int beg, end;
|
||||
struct RRange *r;
|
||||
|
||||
if (!mrb_range_p(range)) return MRB_RANGE_TYPE_MISMATCH;
|
||||
r = mrb_range_ptr(mrb, range);
|
||||
|
||||
beg = mrb_int(mrb, RANGE_BEG(r));
|
||||
end = mrb_int(mrb, RANGE_END(r));
|
||||
|
||||
if (beg < 0) {
|
||||
beg += len;
|
||||
if (beg < 0) return MRB_RANGE_OUT;
|
||||
}
|
||||
|
||||
if (trunc) {
|
||||
if (beg > len) return MRB_RANGE_OUT;
|
||||
if (end > len) end = len;
|
||||
}
|
||||
|
||||
if (end < 0) end += len;
|
||||
if (!RANGE_EXCL(r) && (!trunc || end < len)) end++; /* include end point */
|
||||
len = end - beg;
|
||||
if (len < 0) len = 0;
|
||||
|
||||
*begp = beg;
|
||||
*lenp = len;
|
||||
return MRB_RANGE_OK;
|
||||
}
|
||||
|
||||
void
|
||||
mrb_init_range(mrb_state *mrb)
|
||||
{
|
||||
struct RClass *r;
|
||||
|
||||
r = mrb_define_class(mrb, "Range", mrb->object_class); /* 15.2.14 */
|
||||
mrb->range_class = r;
|
||||
MRB_SET_INSTANCE_TT(r, MRB_TT_RANGE);
|
||||
|
||||
mrb_define_method(mrb, r, "begin", range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.3 */
|
||||
mrb_define_method(mrb, r, "end", range_end, MRB_ARGS_NONE()); /* 15.2.14.4.5 */
|
||||
mrb_define_method(mrb, r, "==", range_eq, MRB_ARGS_REQ(1)); /* 15.2.14.4.1 */
|
||||
mrb_define_method(mrb, r, "===", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.2 */
|
||||
mrb_define_method(mrb, r, "exclude_end?", range_excl, MRB_ARGS_NONE()); /* 15.2.14.4.6 */
|
||||
mrb_define_method(mrb, r, "first", range_beg, MRB_ARGS_NONE()); /* 15.2.14.4.7 */
|
||||
mrb_define_method(mrb, r, "include?", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.8 */
|
||||
mrb_define_method(mrb, r, "initialize", range_initialize, MRB_ARGS_ANY()); /* 15.2.14.4.9 */
|
||||
mrb_define_method(mrb, r, "last", range_end, MRB_ARGS_NONE()); /* 15.2.14.4.10 */
|
||||
mrb_define_method(mrb, r, "member?", range_include, MRB_ARGS_REQ(1)); /* 15.2.14.4.11 */
|
||||
mrb_define_method(mrb, r, "to_s", range_to_s, MRB_ARGS_NONE()); /* 15.2.14.4.12(x) */
|
||||
mrb_define_method(mrb, r, "inspect", range_inspect, MRB_ARGS_NONE()); /* 15.2.14.4.13(x) */
|
||||
mrb_define_method(mrb, r, "eql?", range_eql, MRB_ARGS_REQ(1)); /* 15.2.14.4.14(x) */
|
||||
mrb_define_method(mrb, r, "initialize_copy", range_initialize_copy, MRB_ARGS_REQ(1)); /* 15.2.14.4.15(x) */
|
||||
}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
** state.c - mrb_state open/close functions
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <mruby.h>
|
||||
#include <mruby/irep.h>
|
||||
#include <mruby/variable.h>
|
||||
#include <mruby/debug.h>
|
||||
#include <mruby/string.h>
|
||||
#include <mruby/class.h>
|
||||
|
||||
void mrb_init_core(mrb_state*);
|
||||
void mrb_init_mrbgems(mrb_state*);
|
||||
|
||||
void mrb_gc_init(mrb_state*, mrb_gc *gc);
|
||||
void mrb_gc_destroy(mrb_state*, mrb_gc *gc);
|
||||
|
||||
int mrb_core_init_protect(mrb_state *mrb, void (*body)(mrb_state *, void *), void *opaque);
|
||||
|
||||
static void
|
||||
init_gc_and_core(mrb_state *mrb, void *opaque)
|
||||
{
|
||||
static const struct mrb_context mrb_context_zero = { 0 };
|
||||
|
||||
mrb_gc_init(mrb, &mrb->gc);
|
||||
mrb->c = (struct mrb_context*)mrb_malloc(mrb, sizeof(struct mrb_context));
|
||||
*mrb->c = mrb_context_zero;
|
||||
mrb->root_c = mrb->c;
|
||||
|
||||
mrb_init_core(mrb);
|
||||
}
|
||||
|
||||
MRB_API mrb_state*
|
||||
mrb_open_core(mrb_allocf f, void *ud)
|
||||
{
|
||||
static const mrb_state mrb_state_zero = { 0 };
|
||||
mrb_state *mrb;
|
||||
|
||||
if (f == NULL) f = mrb_default_allocf;
|
||||
mrb = (mrb_state *)(f)(NULL, NULL, sizeof(mrb_state), ud);
|
||||
if (mrb == NULL) return NULL;
|
||||
|
||||
*mrb = mrb_state_zero;
|
||||
mrb->allocf_ud = ud;
|
||||
mrb->allocf = f;
|
||||
mrb->atexit_stack_len = 0;
|
||||
|
||||
if (mrb_core_init_protect(mrb, init_gc_and_core, NULL)) {
|
||||
mrb_close(mrb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return mrb;
|
||||
}
|
||||
|
||||
void*
|
||||
mrb_default_allocf(mrb_state *mrb, void *p, size_t size, void *ud)
|
||||
{
|
||||
if (size == 0) {
|
||||
free(p);
|
||||
return NULL;
|
||||
}
|
||||
else {
|
||||
return realloc(p, size);
|
||||
}
|
||||
}
|
||||
|
||||
MRB_API mrb_state*
|
||||
mrb_open(void)
|
||||
{
|
||||
mrb_state *mrb = mrb_open_allocf(mrb_default_allocf, NULL);
|
||||
|
||||
return mrb;
|
||||
}
|
||||
|
||||
static void
|
||||
init_mrbgems(mrb_state *mrb, void *opaque)
|
||||
{
|
||||
mrb_init_mrbgems(mrb);
|
||||
}
|
||||
|
||||
MRB_API mrb_state*
|
||||
mrb_open_allocf(mrb_allocf f, void *ud)
|
||||
{
|
||||
mrb_state *mrb = mrb_open_core(f, ud);
|
||||
|
||||
if (mrb == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_GEMS
|
||||
if (mrb_core_init_protect(mrb, init_mrbgems, NULL)) {
|
||||
mrb_close(mrb);
|
||||
return NULL;
|
||||
}
|
||||
mrb_gc_arena_restore(mrb, 0);
|
||||
#endif
|
||||
return mrb;
|
||||
}
|
||||
|
||||
void mrb_free_symtbl(mrb_state *mrb);
|
||||
|
||||
void
|
||||
mrb_irep_incref(mrb_state *mrb, mrb_irep *irep)
|
||||
{
|
||||
irep->refcnt++;
|
||||
}
|
||||
|
||||
void
|
||||
mrb_irep_decref(mrb_state *mrb, mrb_irep *irep)
|
||||
{
|
||||
irep->refcnt--;
|
||||
if (irep->refcnt == 0) {
|
||||
mrb_irep_free(mrb, irep);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mrb_irep_cutref(mrb_state *mrb, mrb_irep *irep)
|
||||
{
|
||||
mrb_irep *tmp;
|
||||
int i;
|
||||
|
||||
for (i=0; i<irep->rlen; i++) {
|
||||
tmp = irep->reps[i];
|
||||
irep->reps[i] = NULL;
|
||||
if (tmp) mrb_irep_decref(mrb, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mrb_irep_free(mrb_state *mrb, mrb_irep *irep)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!(irep->flags & MRB_ISEQ_NO_FREE))
|
||||
mrb_free(mrb, (void*)irep->iseq);
|
||||
if (irep->pool) for (i=0; i<irep->plen; i++) {
|
||||
if (mrb_string_p(irep->pool[i])) {
|
||||
mrb_gc_free_str(mrb, RSTRING(irep->pool[i]));
|
||||
mrb_free(mrb, mrb_obj_ptr(irep->pool[i]));
|
||||
}
|
||||
#if defined(MRB_WORD_BOXING) && !defined(MRB_WITHOUT_FLOAT)
|
||||
else if (mrb_float_p(irep->pool[i])) {
|
||||
mrb_free(mrb, mrb_obj_ptr(irep->pool[i]));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
mrb_free(mrb, irep->pool);
|
||||
mrb_free(mrb, irep->syms);
|
||||
if (irep->reps) {
|
||||
for (i=0; i<irep->rlen; i++) {
|
||||
if (irep->reps[i])
|
||||
mrb_irep_decref(mrb, irep->reps[i]);
|
||||
}
|
||||
}
|
||||
mrb_free(mrb, irep->reps);
|
||||
mrb_free(mrb, irep->lv);
|
||||
mrb_debug_info_free(mrb, irep->debug_info);
|
||||
mrb_free(mrb, irep);
|
||||
}
|
||||
|
||||
void mrb_free_backtrace(mrb_state *mrb);
|
||||
|
||||
MRB_API void
|
||||
mrb_free_context(mrb_state *mrb, struct mrb_context *c)
|
||||
{
|
||||
if (!c) return;
|
||||
mrb_free(mrb, c->stbase);
|
||||
mrb_free(mrb, c->cibase);
|
||||
mrb_free(mrb, c->rescue);
|
||||
mrb_free(mrb, c->ensure);
|
||||
mrb_free(mrb, c);
|
||||
}
|
||||
|
||||
MRB_API void
|
||||
mrb_close(mrb_state *mrb)
|
||||
{
|
||||
if (!mrb) return;
|
||||
if (mrb->atexit_stack_len > 0) {
|
||||
mrb_int i;
|
||||
for (i = mrb->atexit_stack_len; i > 0; --i) {
|
||||
mrb->atexit_stack[i - 1](mrb);
|
||||
}
|
||||
#ifndef MRB_FIXED_STATE_ATEXIT_STACK
|
||||
mrb_free(mrb, mrb->atexit_stack);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* free */
|
||||
mrb_gc_destroy(mrb, &mrb->gc);
|
||||
mrb_free_context(mrb, mrb->root_c);
|
||||
mrb_gc_free_gv(mrb);
|
||||
mrb_free_symtbl(mrb);
|
||||
mrb_free(mrb, mrb);
|
||||
}
|
||||
|
||||
MRB_API mrb_irep*
|
||||
mrb_add_irep(mrb_state *mrb)
|
||||
{
|
||||
static const mrb_irep mrb_irep_zero = { 0 };
|
||||
mrb_irep *irep;
|
||||
|
||||
irep = (mrb_irep *)mrb_malloc(mrb, sizeof(mrb_irep));
|
||||
*irep = mrb_irep_zero;
|
||||
irep->refcnt = 1;
|
||||
|
||||
return irep;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_top_self(mrb_state *mrb)
|
||||
{
|
||||
return mrb_obj_value(mrb->top_self);
|
||||
}
|
||||
|
||||
MRB_API void
|
||||
mrb_state_atexit(mrb_state *mrb, mrb_atexit_func f)
|
||||
{
|
||||
#ifdef MRB_FIXED_STATE_ATEXIT_STACK
|
||||
if (mrb->atexit_stack_len + 1 > MRB_FIXED_STATE_ATEXIT_STACK_SIZE) {
|
||||
mrb_raise(mrb, E_RUNTIME_ERROR, "exceeded fixed state atexit stack limit");
|
||||
}
|
||||
#else
|
||||
size_t stack_size;
|
||||
|
||||
stack_size = sizeof(mrb_atexit_func) * (mrb->atexit_stack_len + 1);
|
||||
if (mrb->atexit_stack_len == 0) {
|
||||
mrb->atexit_stack = (mrb_atexit_func*)mrb_malloc(mrb, stack_size);
|
||||
}
|
||||
else {
|
||||
mrb->atexit_stack = (mrb_atexit_func*)mrb_realloc(mrb, mrb->atexit_stack, stack_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
mrb->atexit_stack[mrb->atexit_stack_len++] = f;
|
||||
}
|
||||
+3230
File diff suppressed because it is too large
Load Diff
+608
@@ -0,0 +1,608 @@
|
||||
/*
|
||||
** symbol.c - Symbol class
|
||||
**
|
||||
** See Copyright Notice in mruby.h
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <mruby.h>
|
||||
#include <mruby/khash.h>
|
||||
#include <mruby/string.h>
|
||||
#include <mruby/dump.h>
|
||||
#include <mruby/class.h>
|
||||
|
||||
/* ------------------------------------------------------ */
|
||||
typedef struct symbol_name {
|
||||
mrb_bool lit : 1;
|
||||
uint8_t prev;
|
||||
uint16_t len;
|
||||
const char *name;
|
||||
} symbol_name;
|
||||
|
||||
#define SYMBOL_INLINE_BIT_POS 1
|
||||
#define SYMBOL_INLINE_LOWER_BIT_POS 2
|
||||
#define SYMBOL_INLINE (1 << (SYMBOL_INLINE_BIT_POS - 1))
|
||||
#define SYMBOL_INLINE_LOWER (1 << (SYMBOL_INLINE_LOWER_BIT_POS - 1))
|
||||
#define SYMBOL_NORMAL_SHIFT SYMBOL_INLINE_BIT_POS
|
||||
#define SYMBOL_INLINE_SHIFT SYMBOL_INLINE_LOWER_BIT_POS
|
||||
#ifdef MRB_ENABLE_ALL_SYMBOLS
|
||||
# define SYMBOL_INLINE_P(sym) FALSE
|
||||
# define SYMBOL_INLINE_LOWER_P(sym) FALSE
|
||||
# define sym_inline_pack(name, len) 0
|
||||
# define sym_inline_unpack(sym, buf, lenp) NULL
|
||||
#else
|
||||
# define SYMBOL_INLINE_P(sym) ((sym) & SYMBOL_INLINE)
|
||||
# define SYMBOL_INLINE_LOWER_P(sym) ((sym) & SYMBOL_INLINE_LOWER)
|
||||
#endif
|
||||
|
||||
static void
|
||||
sym_validate_len(mrb_state *mrb, size_t len)
|
||||
{
|
||||
if (len >= RITE_LV_NULL_MARK) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "symbol length too long");
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef MRB_ENABLE_ALL_SYMBOLS
|
||||
static const char pack_table[] = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
static mrb_sym
|
||||
sym_inline_pack(const char *name, size_t len)
|
||||
{
|
||||
const size_t lower_length_max = (MRB_SYMBOL_BIT - 2) / 5;
|
||||
const size_t mix_length_max = (MRB_SYMBOL_BIT - 2) / 6;
|
||||
|
||||
char c;
|
||||
const char *p;
|
||||
size_t i;
|
||||
mrb_sym sym = 0;
|
||||
mrb_bool lower = TRUE;
|
||||
|
||||
if (len > lower_length_max) return 0; /* too long */
|
||||
for (i=0; i<len; i++) {
|
||||
uint32_t bits;
|
||||
|
||||
c = name[i];
|
||||
if (c == 0) return 0; /* NUL in name */
|
||||
p = strchr(pack_table, (int)c);
|
||||
if (p == 0) return 0; /* non alnum char */
|
||||
bits = (uint32_t)(p - pack_table)+1;
|
||||
if (bits > 27) lower = FALSE;
|
||||
if (i >= mix_length_max) break;
|
||||
sym |= bits<<(i*6+SYMBOL_INLINE_SHIFT);
|
||||
}
|
||||
if (lower) {
|
||||
sym = 0;
|
||||
for (i=0; i<len; i++) {
|
||||
uint32_t bits;
|
||||
|
||||
c = name[i];
|
||||
p = strchr(pack_table, (int)c);
|
||||
bits = (uint32_t)(p - pack_table)+1;
|
||||
sym |= bits<<(i*5+SYMBOL_INLINE_SHIFT);
|
||||
}
|
||||
return sym | SYMBOL_INLINE | SYMBOL_INLINE_LOWER;
|
||||
}
|
||||
if (len > mix_length_max) return 0;
|
||||
return sym | SYMBOL_INLINE;
|
||||
}
|
||||
|
||||
static const char*
|
||||
sym_inline_unpack(mrb_sym sym, char *buf, mrb_int *lenp)
|
||||
{
|
||||
int bit_per_char = SYMBOL_INLINE_LOWER_P(sym) ? 5 : 6;
|
||||
int i;
|
||||
|
||||
mrb_assert(SYMBOL_INLINE_P(sym));
|
||||
|
||||
for (i=0; i<30/bit_per_char; i++) {
|
||||
uint32_t bits = sym>>(i*bit_per_char+SYMBOL_INLINE_SHIFT) & ((1<<bit_per_char)-1);
|
||||
if (bits == 0) break;
|
||||
buf[i] = pack_table[bits-1];;
|
||||
}
|
||||
buf[i] = '\0';
|
||||
if (lenp) *lenp = i;
|
||||
return buf;
|
||||
}
|
||||
#endif
|
||||
|
||||
static uint8_t
|
||||
symhash(const char *key, size_t len)
|
||||
{
|
||||
uint32_t hash, i;
|
||||
|
||||
for(hash = i = 0; i < len; ++i) {
|
||||
hash += key[i];
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
}
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 11);
|
||||
hash += (hash << 15);
|
||||
return hash & 0xff;
|
||||
}
|
||||
|
||||
static mrb_sym
|
||||
find_symbol(mrb_state *mrb, const char *name, size_t len, uint8_t *hashp)
|
||||
{
|
||||
mrb_sym i;
|
||||
symbol_name *sname;
|
||||
uint8_t hash;
|
||||
|
||||
/* inline symbol */
|
||||
i = sym_inline_pack(name, len);
|
||||
if (i > 0) return i;
|
||||
|
||||
hash = symhash(name, len);
|
||||
if (hashp) *hashp = hash;
|
||||
|
||||
i = mrb->symhash[hash];
|
||||
if (i == 0) return 0;
|
||||
do {
|
||||
sname = &mrb->symtbl[i];
|
||||
if (sname->len == len && memcmp(sname->name, name, len) == 0) {
|
||||
return i<<SYMBOL_NORMAL_SHIFT;
|
||||
}
|
||||
if (sname->prev == 0xff) {
|
||||
i -= 0xff;
|
||||
sname = &mrb->symtbl[i];
|
||||
while (mrb->symtbl < sname) {
|
||||
if (sname->len == len && memcmp(sname->name, name, len) == 0) {
|
||||
return (mrb_sym)(sname - mrb->symtbl)<<SYMBOL_NORMAL_SHIFT;
|
||||
}
|
||||
sname--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
i -= sname->prev;
|
||||
} while (sname->prev > 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static mrb_sym
|
||||
sym_intern(mrb_state *mrb, const char *name, size_t len, mrb_bool lit)
|
||||
{
|
||||
mrb_sym sym;
|
||||
symbol_name *sname;
|
||||
uint8_t hash;
|
||||
|
||||
sym_validate_len(mrb, len);
|
||||
sym = find_symbol(mrb, name, len, &hash);
|
||||
if (sym > 0) return sym;
|
||||
|
||||
/* registering a new symbol */
|
||||
sym = mrb->symidx + 1;
|
||||
if (mrb->symcapa < sym) {
|
||||
size_t symcapa = mrb->symcapa;
|
||||
if (symcapa == 0) symcapa = 100;
|
||||
else symcapa = (size_t)(symcapa * 6 / 5);
|
||||
mrb->symtbl = (symbol_name*)mrb_realloc(mrb, mrb->symtbl, sizeof(symbol_name)*(symcapa+1));
|
||||
mrb->symcapa = symcapa;
|
||||
}
|
||||
sname = &mrb->symtbl[sym];
|
||||
sname->len = (uint16_t)len;
|
||||
if (lit || mrb_ro_data_p(name)) {
|
||||
sname->name = name;
|
||||
sname->lit = TRUE;
|
||||
}
|
||||
else {
|
||||
char *p = (char *)mrb_malloc(mrb, len+1);
|
||||
memcpy(p, name, len);
|
||||
p[len] = 0;
|
||||
sname->name = (const char*)p;
|
||||
sname->lit = FALSE;
|
||||
}
|
||||
if (mrb->symhash[hash]) {
|
||||
mrb_sym i = sym - mrb->symhash[hash];
|
||||
if (i > 0xff)
|
||||
sname->prev = 0xff;
|
||||
else
|
||||
sname->prev = i;
|
||||
}
|
||||
else {
|
||||
sname->prev = 0;
|
||||
}
|
||||
mrb->symhash[hash] = mrb->symidx = sym;
|
||||
|
||||
return sym<<SYMBOL_NORMAL_SHIFT;
|
||||
}
|
||||
|
||||
MRB_API mrb_sym
|
||||
mrb_intern(mrb_state *mrb, const char *name, size_t len)
|
||||
{
|
||||
return sym_intern(mrb, name, len, FALSE);
|
||||
}
|
||||
|
||||
MRB_API mrb_sym
|
||||
mrb_intern_static(mrb_state *mrb, const char *name, size_t len)
|
||||
{
|
||||
return sym_intern(mrb, name, len, TRUE);
|
||||
}
|
||||
|
||||
MRB_API mrb_sym
|
||||
mrb_intern_cstr(mrb_state *mrb, const char *name)
|
||||
{
|
||||
return mrb_intern(mrb, name, strlen(name));
|
||||
}
|
||||
|
||||
MRB_API mrb_sym
|
||||
mrb_intern_str(mrb_state *mrb, mrb_value str)
|
||||
{
|
||||
return mrb_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str));
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_check_intern(mrb_state *mrb, const char *name, size_t len)
|
||||
{
|
||||
mrb_sym sym;
|
||||
|
||||
sym_validate_len(mrb, len);
|
||||
sym = find_symbol(mrb, name, len, NULL);
|
||||
if (sym > 0) return mrb_symbol_value(sym);
|
||||
return mrb_nil_value();
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_check_intern_cstr(mrb_state *mrb, const char *name)
|
||||
{
|
||||
return mrb_check_intern(mrb, name, strlen(name));
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_check_intern_str(mrb_state *mrb, mrb_value str)
|
||||
{
|
||||
return mrb_check_intern(mrb, RSTRING_PTR(str), RSTRING_LEN(str));
|
||||
}
|
||||
|
||||
static const char*
|
||||
sym2name_len(mrb_state *mrb, mrb_sym sym, char *buf, mrb_int *lenp)
|
||||
{
|
||||
if (SYMBOL_INLINE_P(sym)) return sym_inline_unpack(sym, buf, lenp);
|
||||
|
||||
sym >>= SYMBOL_NORMAL_SHIFT;
|
||||
if (sym == 0 || mrb->symidx < sym) {
|
||||
if (lenp) *lenp = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (lenp) *lenp = mrb->symtbl[sym].len;
|
||||
return mrb->symtbl[sym].name;
|
||||
}
|
||||
|
||||
MRB_API const char*
|
||||
mrb_sym_name_len(mrb_state *mrb, mrb_sym sym, mrb_int *lenp)
|
||||
{
|
||||
return sym2name_len(mrb, sym, mrb->symbuf, lenp);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_free_symtbl(mrb_state *mrb)
|
||||
{
|
||||
mrb_sym i, lim;
|
||||
|
||||
for (i=1, lim=mrb->symidx+1; i<lim; i++) {
|
||||
if (!mrb->symtbl[i].lit) {
|
||||
mrb_free(mrb, (char*)mrb->symtbl[i].name);
|
||||
}
|
||||
}
|
||||
mrb_free(mrb, mrb->symtbl);
|
||||
}
|
||||
|
||||
void
|
||||
mrb_init_symtbl(mrb_state *mrb)
|
||||
{
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* Document-class: Symbol
|
||||
*
|
||||
* <code>Symbol</code> objects represent names and some strings
|
||||
* inside the Ruby
|
||||
* interpreter. They are generated using the <code>:name</code> and
|
||||
* <code>:"string"</code> literals
|
||||
* syntax, and by the various <code>to_sym</code> methods. The same
|
||||
* <code>Symbol</code> object will be created for a given name or string
|
||||
* for the duration of a program's execution, regardless of the context
|
||||
* or meaning of that name. Thus if <code>Fred</code> is a constant in
|
||||
* one context, a method in another, and a class in a third, the
|
||||
* <code>Symbol</code> <code>:Fred</code> will be the same object in
|
||||
* all three contexts.
|
||||
*
|
||||
* module One
|
||||
* class Fred
|
||||
* end
|
||||
* $f1 = :Fred
|
||||
* end
|
||||
* module Two
|
||||
* Fred = 1
|
||||
* $f2 = :Fred
|
||||
* end
|
||||
* def Fred()
|
||||
* end
|
||||
* $f3 = :Fred
|
||||
* $f1.object_id #=> 2514190
|
||||
* $f2.object_id #=> 2514190
|
||||
* $f3.object_id #=> 2514190
|
||||
*
|
||||
*/
|
||||
|
||||
/* 15.2.11.3.2 */
|
||||
/* 15.2.11.3.3 */
|
||||
/*
|
||||
* call-seq:
|
||||
* sym.id2name -> string
|
||||
* sym.to_s -> string
|
||||
*
|
||||
* Returns the name or string corresponding to <i>sym</i>.
|
||||
*
|
||||
* :fred.id2name #=> "fred"
|
||||
*/
|
||||
static mrb_value
|
||||
sym_to_s(mrb_state *mrb, mrb_value sym)
|
||||
{
|
||||
return mrb_sym_str(mrb, mrb_symbol(sym));
|
||||
}
|
||||
|
||||
/* 15.2.11.3.4 */
|
||||
/*
|
||||
* call-seq:
|
||||
* sym.to_sym -> sym
|
||||
* sym.intern -> sym
|
||||
*
|
||||
* In general, <code>to_sym</code> returns the <code>Symbol</code> corresponding
|
||||
* to an object. As <i>sym</i> is already a symbol, <code>self</code> is returned
|
||||
* in this case.
|
||||
*/
|
||||
|
||||
static mrb_value
|
||||
sym_to_sym(mrb_state *mrb, mrb_value sym)
|
||||
{
|
||||
return sym;
|
||||
}
|
||||
|
||||
/* 15.2.11.3.5(x) */
|
||||
/*
|
||||
* call-seq:
|
||||
* sym.inspect -> string
|
||||
*
|
||||
* Returns the representation of <i>sym</i> as a symbol literal.
|
||||
*
|
||||
* :fred.inspect #=> ":fred"
|
||||
*/
|
||||
|
||||
#if __STDC__
|
||||
# define SIGN_EXTEND_CHAR(c) ((signed char)(c))
|
||||
#else /* not __STDC__ */
|
||||
/* As in Harbison and Steele. */
|
||||
# define SIGN_EXTEND_CHAR(c) ((((unsigned char)(c)) ^ 128) - 128)
|
||||
#endif
|
||||
#define is_identchar(c) (SIGN_EXTEND_CHAR(c)!=-1&&(ISALNUM(c) || (c) == '_'))
|
||||
|
||||
static mrb_bool
|
||||
is_special_global_name(const char* m)
|
||||
{
|
||||
switch (*m) {
|
||||
case '~': case '*': case '$': case '?': case '!': case '@':
|
||||
case '/': case '\\': case ';': case ',': case '.': case '=':
|
||||
case ':': case '<': case '>': case '\"':
|
||||
case '&': case '`': case '\'': case '+':
|
||||
case '0':
|
||||
++m;
|
||||
break;
|
||||
case '-':
|
||||
++m;
|
||||
if (is_identchar(*m)) m += 1;
|
||||
break;
|
||||
default:
|
||||
if (!ISDIGIT(*m)) return FALSE;
|
||||
do ++m; while (ISDIGIT(*m));
|
||||
break;
|
||||
}
|
||||
return !*m;
|
||||
}
|
||||
|
||||
static mrb_bool
|
||||
symname_p(const char *name)
|
||||
{
|
||||
const char *m = name;
|
||||
mrb_bool localid = FALSE;
|
||||
|
||||
if (!m) return FALSE;
|
||||
switch (*m) {
|
||||
case '\0':
|
||||
return FALSE;
|
||||
|
||||
case '$':
|
||||
if (is_special_global_name(++m)) return TRUE;
|
||||
goto id;
|
||||
|
||||
case '@':
|
||||
if (*++m == '@') ++m;
|
||||
goto id;
|
||||
|
||||
case '<':
|
||||
switch (*++m) {
|
||||
case '<': ++m; break;
|
||||
case '=': if (*++m == '>') ++m; break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
|
||||
case '>':
|
||||
switch (*++m) {
|
||||
case '>': case '=': ++m; break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
|
||||
case '=':
|
||||
switch (*++m) {
|
||||
case '~': ++m; break;
|
||||
case '=': if (*++m == '=') ++m; break;
|
||||
default: return FALSE;
|
||||
}
|
||||
break;
|
||||
|
||||
case '*':
|
||||
if (*++m == '*') ++m;
|
||||
break;
|
||||
case '!':
|
||||
switch (*++m) {
|
||||
case '=': case '~': ++m;
|
||||
}
|
||||
break;
|
||||
case '+': case '-':
|
||||
if (*++m == '@') ++m;
|
||||
break;
|
||||
case '|':
|
||||
if (*++m == '|') ++m;
|
||||
break;
|
||||
case '&':
|
||||
if (*++m == '&') ++m;
|
||||
break;
|
||||
|
||||
case '^': case '/': case '%': case '~': case '`':
|
||||
++m;
|
||||
break;
|
||||
|
||||
case '[':
|
||||
if (*++m != ']') return FALSE;
|
||||
if (*++m == '=') ++m;
|
||||
break;
|
||||
|
||||
default:
|
||||
localid = !ISUPPER(*m);
|
||||
id:
|
||||
if (*m != '_' && !ISALPHA(*m)) return FALSE;
|
||||
while (is_identchar(*m)) m += 1;
|
||||
if (localid) {
|
||||
switch (*m) {
|
||||
case '!': case '?': case '=': ++m;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return *m ? FALSE : TRUE;
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
sym_inspect(mrb_state *mrb, mrb_value sym)
|
||||
{
|
||||
mrb_value str;
|
||||
const char *name;
|
||||
mrb_int len;
|
||||
mrb_sym id = mrb_symbol(sym);
|
||||
char *sp;
|
||||
|
||||
name = mrb_sym_name_len(mrb, id, &len);
|
||||
str = mrb_str_new(mrb, 0, len+1);
|
||||
sp = RSTRING_PTR(str);
|
||||
sp[0] = ':';
|
||||
memcpy(sp+1, name, len);
|
||||
mrb_assert_int_fit(mrb_int, len, size_t, SIZE_MAX);
|
||||
if (!symname_p(name) || strlen(name) != (size_t)len) {
|
||||
str = mrb_str_inspect(mrb, str);
|
||||
sp = RSTRING_PTR(str);
|
||||
sp[0] = ':';
|
||||
sp[1] = '"';
|
||||
}
|
||||
#ifdef MRB_UTF8_STRING
|
||||
if (SYMBOL_INLINE_P(id)) RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
|
||||
#endif
|
||||
return str;
|
||||
}
|
||||
|
||||
MRB_API mrb_value
|
||||
mrb_sym_str(mrb_state *mrb, mrb_sym sym)
|
||||
{
|
||||
mrb_int len;
|
||||
const char *name = mrb_sym_name_len(mrb, sym, &len);
|
||||
|
||||
if (!name) return mrb_undef_value(); /* can't happen */
|
||||
if (SYMBOL_INLINE_P(sym)) {
|
||||
mrb_value str = mrb_str_new(mrb, name, len);
|
||||
RSTR_SET_ASCII_FLAG(mrb_str_ptr(str));
|
||||
return str;
|
||||
}
|
||||
return mrb_str_new_static(mrb, name, len);
|
||||
}
|
||||
|
||||
static const char*
|
||||
sym_name(mrb_state *mrb, mrb_sym sym, mrb_bool dump)
|
||||
{
|
||||
mrb_int len;
|
||||
const char *name = mrb_sym_name_len(mrb, sym, &len);
|
||||
|
||||
if (!name) return NULL;
|
||||
if (strlen(name) == (size_t)len && (!dump || symname_p(name))) {
|
||||
return name;
|
||||
}
|
||||
else {
|
||||
mrb_value str = SYMBOL_INLINE_P(sym) ?
|
||||
mrb_str_new(mrb, name, len) : mrb_str_new_static(mrb, name, len);
|
||||
str = mrb_str_dump(mrb, str);
|
||||
return RSTRING_PTR(str);
|
||||
}
|
||||
}
|
||||
|
||||
MRB_API const char*
|
||||
mrb_sym_name(mrb_state *mrb, mrb_sym sym)
|
||||
{
|
||||
return sym_name(mrb, sym, FALSE);
|
||||
}
|
||||
|
||||
MRB_API const char*
|
||||
mrb_sym_dump(mrb_state *mrb, mrb_sym sym)
|
||||
{
|
||||
return sym_name(mrb, sym, TRUE);
|
||||
}
|
||||
|
||||
#define lesser(a,b) (((a)>(b))?(b):(a))
|
||||
|
||||
static mrb_value
|
||||
sym_cmp(mrb_state *mrb, mrb_value s1)
|
||||
{
|
||||
mrb_value s2 = mrb_get_arg1(mrb);
|
||||
mrb_sym sym1, sym2;
|
||||
|
||||
if (!mrb_symbol_p(s2)) return mrb_nil_value();
|
||||
sym1 = mrb_symbol(s1);
|
||||
sym2 = mrb_symbol(s2);
|
||||
if (sym1 == sym2) return mrb_fixnum_value(0);
|
||||
else {
|
||||
const char *p1, *p2;
|
||||
int retval;
|
||||
mrb_int len, len1, len2;
|
||||
char buf1[8], buf2[8];
|
||||
|
||||
p1 = sym2name_len(mrb, sym1, buf1, &len1);
|
||||
p2 = sym2name_len(mrb, sym2, buf2, &len2);
|
||||
len = lesser(len1, len2);
|
||||
retval = memcmp(p1, p2, len);
|
||||
if (retval == 0) {
|
||||
if (len1 == len2) return mrb_fixnum_value(0);
|
||||
if (len1 > len2) return mrb_fixnum_value(1);
|
||||
return mrb_fixnum_value(-1);
|
||||
}
|
||||
if (retval > 0) return mrb_fixnum_value(1);
|
||||
return mrb_fixnum_value(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mrb_init_symbol(mrb_state *mrb)
|
||||
{
|
||||
struct RClass *sym;
|
||||
|
||||
mrb->symbol_class = sym = mrb_define_class(mrb, "Symbol", mrb->object_class); /* 15.2.11 */
|
||||
MRB_SET_INSTANCE_TT(sym, MRB_TT_SYMBOL);
|
||||
mrb_undef_class_method(mrb, sym, "new");
|
||||
|
||||
mrb_define_method(mrb, sym, "id2name", sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.2 */
|
||||
mrb_define_method(mrb, sym, "to_s", sym_to_s, MRB_ARGS_NONE()); /* 15.2.11.3.3 */
|
||||
mrb_define_method(mrb, sym, "to_sym", sym_to_sym, MRB_ARGS_NONE()); /* 15.2.11.3.4 */
|
||||
mrb_define_method(mrb, sym, "inspect", sym_inspect, MRB_ARGS_NONE()); /* 15.2.11.3.5(x) */
|
||||
mrb_define_method(mrb, sym, "<=>", sym_cmp, MRB_ARGS_REQ(1));
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
#ifndef MRB_VALUE_ARRAY_H__
|
||||
#define MRB_VALUE_ARRAY_H__
|
||||
|
||||
#include <mruby.h>
|
||||
|
||||
static inline void
|
||||
value_move(mrb_value *s1, const mrb_value *s2, size_t n)
|
||||
{
|
||||
if (s1 > s2 && s1 < s2 + n)
|
||||
{
|
||||
s1 += n;
|
||||
s2 += n;
|
||||
while (n-- > 0) {
|
||||
*--s1 = *--s2;
|
||||
}
|
||||
}
|
||||
else if (s1 != s2) {
|
||||
while (n-- > 0) {
|
||||
*s1++ = *s2++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* nothing to do. */
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MRB_VALUE_ARRAY_H__ */
|
||||
+1142
File diff suppressed because it is too large
Load Diff
+17
@@ -0,0 +1,17 @@
|
||||
#include <mruby.h>
|
||||
#include <mruby/variable.h>
|
||||
|
||||
void
|
||||
mrb_init_version(mrb_state* mrb)
|
||||
{
|
||||
mrb_value mruby_version = mrb_str_new_lit(mrb, MRUBY_VERSION);
|
||||
|
||||
mrb_define_global_const(mrb, "RUBY_VERSION", mrb_str_new_lit(mrb, MRUBY_RUBY_VERSION));
|
||||
mrb_define_global_const(mrb, "RUBY_ENGINE", mrb_str_new_lit(mrb, MRUBY_RUBY_ENGINE));
|
||||
mrb_define_global_const(mrb, "RUBY_ENGINE_VERSION", mruby_version);
|
||||
mrb_define_global_const(mrb, "MRUBY_VERSION", mruby_version);
|
||||
mrb_define_global_const(mrb, "MRUBY_RELEASE_NO", mrb_fixnum_value(MRUBY_RELEASE_NO));
|
||||
mrb_define_global_const(mrb, "MRUBY_RELEASE_DATE", mrb_str_new_lit(mrb, MRUBY_RELEASE_DATE));
|
||||
mrb_define_global_const(mrb, "MRUBY_DESCRIPTION", mrb_str_new_lit(mrb, MRUBY_DESCRIPTION));
|
||||
mrb_define_global_const(mrb, "MRUBY_COPYRIGHT", mrb_str_new_lit(mrb, MRUBY_COPYRIGHT));
|
||||
}
|
||||
Vendored
+2865
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user