aboutsummaryrefslogtreecommitdiffstats
path: root/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'eval.c')
-rw-r--r--eval.c5402
1 files changed, 5402 insertions, 0 deletions
diff --git a/eval.c b/eval.c
new file mode 100644
index 0000000000..92113a7ce5
--- /dev/null
+++ b/eval.c
@@ -0,0 +1,5402 @@
+/************************************************
+
+ eval.c -
+
+ $Author$
+ $Date$
+ created at: Thu Jun 10 14:22:17 JST 1993
+
+ Copyright (C) 1993-1997 Yukihiro Matsumoto
+
+************************************************/
+
+#include "ruby.h"
+#include "node.h"
+#include "env.h"
+#include "sig.h"
+
+#include <stdio.h>
+#include <setjmp.h>
+#include "st.h"
+#include "dln.h"
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+char *strrchr();
+#endif
+
+#ifndef setjmp
+#ifdef HAVE__SETJMP
+#define setjmp(env) _setjmp(env)
+#define longjmp(env,val) _longjmp(env,val)
+#endif
+#endif
+
+extern VALUE cData;
+VALUE cProc;
+static VALUE proc_call();
+static VALUE f_binding();
+
+#define CACHE_SIZE 0x200
+#define CACHE_MASK 0x1ff
+#define EXPR1(c,m) ((((int)(c)>>3)^(m))&CACHE_MASK)
+
+struct cache_entry { /* method hash table. */
+ ID mid; /* method's id */
+ ID mid0; /* method's original id */
+ struct RClass *class; /* receiver's class */
+ struct RClass *origin; /* where method defined */
+ NODE *method;
+ int noex;
+};
+
+static struct cache_entry cache[CACHE_SIZE];
+
+void
+rb_clear_cache()
+{
+ struct cache_entry *ent, *end;
+
+ ent = cache; end = ent + CACHE_SIZE;
+ while (ent < end) {
+ ent->mid = 0;
+ ent++;
+ }
+}
+
+void
+rb_add_method(class, mid, node, noex)
+ struct RClass *class;
+ ID mid;
+ NODE *node;
+ int noex;
+{
+ NODE *body;
+
+ if (NIL_P(class)) class = (struct RClass*)cObject;
+ body = NEW_METHOD(node, noex);
+ st_insert(class->m_tbl, mid, body);
+}
+
+static NODE*
+search_method(class, id, origin)
+ struct RClass *class, **origin;
+ ID id;
+{
+ NODE *body;
+
+ while (!st_lookup(class->m_tbl, id, &body)) {
+ class = class->super;
+ if (!class) return 0;
+ }
+
+ if (origin) *origin = class;
+ return body;
+}
+
+static NODE*
+rb_get_method_body(classp, idp, noexp)
+ struct RClass **classp;
+ ID *idp;
+ int *noexp;
+{
+ ID id = *idp;
+ struct RClass *class = *classp;
+ NODE *body;
+ struct RClass *origin;
+ struct cache_entry *ent;
+
+ if ((body = search_method(class, id, &origin)) == 0) {
+ return 0;
+ }
+ if (!body->nd_body) return 0;
+
+ /* store in cache */
+ ent = cache + EXPR1(class, id);
+ ent->class = class;
+ ent->noex = body->nd_noex;
+ body = body->nd_body;
+ if (nd_type(body) == NODE_FBODY) {
+ ent->mid = id;
+ *classp = ent->origin = (struct RClass*)body->nd_orig;
+ *idp = ent->mid0 = body->nd_mid;
+ body = ent->method = body->nd_head;
+ }
+ else {
+ *classp = ent->origin = origin;
+ ent->mid = ent->mid0 = id;
+ ent->method = body;
+ }
+
+ if (noexp) *noexp = ent->noex;
+ return body;
+}
+
+void
+rb_alias(class, name, def)
+ struct RClass *class;
+ ID name, def;
+{
+ struct RClass *origin;
+ NODE *orig, *body;
+
+ if (name == def) return;
+ orig = search_method(class, def, &origin);
+ if (!orig || !orig->nd_body) {
+ if (TYPE(class) == T_MODULE) {
+ orig = search_method(cObject, def, &origin);
+ }
+ }
+ if (!orig || !orig->nd_body) {
+ NameError("undefined method `%s' for `%s'",
+ rb_id2name(def), rb_class2name((VALUE)class));
+ }
+ body = orig->nd_body;
+ if (nd_type(body) == NODE_FBODY) { /* was alias */
+ body = body->nd_head;
+ def = body->nd_mid;
+ origin = (struct RClass*)body->nd_orig;
+ }
+
+ st_insert(class->m_tbl, name,
+ NEW_METHOD(NEW_FBODY(body, def, origin), orig->nd_noex));
+}
+
+static void
+rb_export_method(class, name, noex)
+ struct RClass *class;
+ ID name;
+ int noex;
+{
+ NODE *body;
+ struct RClass *origin;
+
+ body = search_method(class, name, &origin);
+ if (!body && TYPE(class) == T_MODULE) {
+ body = search_method(cObject, name, &origin);
+ }
+ if (!body) {
+ NameError("undefined method `%s' for `%s'",
+ rb_id2name(name), rb_class2name((VALUE)class));
+ }
+ if (body->nd_noex != noex) {
+ if (class == origin) {
+ body->nd_noex = noex;
+ }
+ else {
+ rb_clear_cache();
+ rb_add_method(class, name, NEW_ZSUPER(), noex);
+ }
+ }
+}
+
+static VALUE
+method_boundp(class, id, ex)
+ struct RClass *class;
+ ID id;
+ int ex;
+{
+ int noex;
+
+ if (rb_get_method_body(&class, &id, &noex)) {
+ if (ex && noex == NOEX_PRIVATE)
+ return FALSE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int
+rb_method_boundp(class, id, priv)
+ VALUE class;
+ ID id;
+ int priv;
+{
+ if (method_boundp(class, id, priv?NOEX_PRIVATE:NOEX_PUBLIC))
+ return TRUE;
+ return FALSE;
+}
+
+static ID init, eqq, each, aref, aset;
+VALUE errinfo = Qnil, errat = Qnil;
+extern NODE *eval_tree;
+extern int nerrs;
+
+extern VALUE mKernel;
+extern VALUE cModule;
+extern VALUE cClass;
+extern VALUE eFatal;
+extern VALUE eGlobalExit;
+extern VALUE eInterrupt;
+extern VALUE eSystemExit;
+extern VALUE eException;
+extern VALUE eRuntimeError;
+extern VALUE eSyntaxError;
+static VALUE eLocalJumpError;
+extern VALUE eSecurityError;
+
+extern VALUE TopSelf;
+
+struct FRAME *the_frame;
+struct SCOPE *the_scope;
+static struct FRAME *top_frame;
+static struct SCOPE *top_scope;
+
+#define PUSH_FRAME() { \
+ struct FRAME _frame; \
+ _frame.prev = the_frame; \
+ _frame.file = sourcefile; \
+ _frame.line = sourceline; \
+ _frame.iter = the_iter->iter; \
+ _frame.cbase = the_frame->cbase; \
+ the_frame = &_frame; \
+
+#define POP_FRAME() the_frame = _frame.prev; }
+
+struct BLOCK {
+ NODE *var;
+ NODE *body;
+ VALUE self;
+ struct FRAME frame;
+ struct SCOPE *scope;
+ struct RClass *class;
+ int level;
+ int iter;
+ struct RVarmap *d_vars;
+#ifdef THREAD
+ VALUE orig_thread;
+#endif
+ struct BLOCK *prev;
+} *the_block;
+
+#define PUSH_BLOCK(v,b) { \
+ struct BLOCK _block; \
+ _block.level = (int)prot_tag; \
+ _block.var = v; \
+ _block.body = b; \
+ _block.self = self; \
+ _block.frame = *the_frame; \
+ _block.class = the_class; \
+ _block.frame.file = sourcefile; \
+ _block.frame.line = sourceline; \
+ _block.scope = the_scope; \
+ _block.d_vars = the_dyna_vars; \
+ _block.prev = the_block; \
+ _block.iter = the_iter->iter; \
+ the_block = &_block; \
+
+#define PUSH_BLOCK2(b) { \
+ struct BLOCK _block; \
+ _block = *b; \
+ _block.prev = the_block; \
+ the_block = &_block;
+
+#define POP_BLOCK() \
+ the_block = the_block->prev; \
+}
+
+struct RVarmap *the_dyna_vars;
+#define PUSH_VARS() { \
+ struct RVarmap *_old; \
+ _old = the_dyna_vars; \
+ the_dyna_vars = 0;
+
+#define POP_VARS() \
+ the_dyna_vars = _old; \
+}
+
+VALUE
+dyna_var_defined(id)
+ ID id;
+{
+ struct RVarmap *vars = the_dyna_vars;
+
+ while (vars) {
+ if (vars->id == id) return TRUE;
+ vars = vars->next;
+ }
+ return FALSE;
+}
+
+VALUE
+dyna_var_ref(id)
+ ID id;
+{
+ struct RVarmap *vars = the_dyna_vars;
+
+ while (vars) {
+ if (vars->id == id) {
+ return vars->val;
+ }
+ vars = vars->next;
+ }
+ return Qnil;
+}
+
+VALUE
+dyna_var_asgn(id, value)
+ ID id;
+ VALUE value;
+{
+ struct RVarmap *vars = the_dyna_vars;
+
+ while (vars) {
+ if (vars->id == id) {
+ vars->val = value;
+ return value;
+ }
+ vars = vars->next;
+ }
+ {
+ NEWOBJ(_vars, struct RVarmap);
+ OBJSETUP(_vars, 0, T_VARMAP);
+ _vars->id = id;
+ _vars->val = value;
+ _vars->next = the_dyna_vars;
+ the_dyna_vars = _vars;
+ }
+ return value;
+}
+
+static struct iter {
+ int iter;
+ struct iter *prev;
+} *the_iter;
+
+#define ITER_NOT 0
+#define ITER_PRE 1
+#define ITER_CUR 2
+
+#define PUSH_ITER(i) { \
+ struct iter _iter; \
+ _iter.prev = the_iter; \
+ _iter.iter = (i); \
+ the_iter = &_iter; \
+
+#define POP_ITER() \
+ the_iter = _iter.prev; \
+}
+
+static struct tag {
+ jmp_buf buf;
+ struct FRAME *frame;
+ struct iter *iter;
+ struct tag *prev;
+} *prot_tag;
+
+#define PUSH_TAG() { \
+ struct tag _tag; \
+ _tag.frame = the_frame; \
+ _tag.iter = the_iter; \
+ _tag.prev = prot_tag; \
+ prot_tag = &_tag;
+
+#define EXEC_TAG() ((NODE*)setjmp(prot_tag->buf))
+
+#define JUMP_TAG(st) { \
+ the_frame = prot_tag->frame; \
+ the_iter = prot_tag->iter; \
+ longjmp(prot_tag->buf,(int)(st)); \
+}
+
+#define JUMP_TAG3(val,data1,data2) \
+ JUMP_TAG(node_newnode(NODE_TAG,(val),(data1),(data2)))
+
+#define JUMP_TAG2(val,data) JUMP_TAG3((val),(data),0)
+
+#define POP_TAG() \
+ prot_tag = _tag.prev; \
+}
+
+#define TAG_RETURN 0x1
+#define TAG_BREAK 0x2
+#define TAG_NEXT 0x3
+#define TAG_RETRY 0x4
+#define TAG_REDO 0x5
+#define TAG_RAISE 0x6
+#define TAG_THROW 0x7
+#define TAG_FATAL 0x8
+
+#define IN_BLOCK 0x10
+
+struct RClass *the_class;
+
+#define PUSH_CLASS() { \
+ struct RClass *_class = the_class; \
+
+#define POP_CLASS() the_class = _class; }
+
+#define PUSH_SCOPE() { \
+ struct SCOPE *_old; \
+ NEWOBJ(_scope, struct SCOPE); \
+ OBJSETUP(_scope, 0, T_SCOPE); \
+ _scope->local_tbl = 0; \
+ _scope->local_vars = 0; \
+ _scope->flag = 0; \
+ _old = the_scope; \
+ the_scope = _scope; \
+
+#define POP_SCOPE() \
+ if (the_scope->flag == SCOPE_ALLOCA) {\
+ the_scope->local_vars = 0;\
+ the_scope->local_tbl = 0;\
+ if (the_scope != top_scope)\
+ gc_force_recycle(the_scope);\
+ }\
+ else {\
+ the_scope->flag |= SCOPE_NOSTACK;\
+ }\
+ the_scope = _old;\
+}
+
+static VALUE rb_eval();
+static VALUE eval();
+static NODE *compile();
+
+static VALUE rb_call();
+VALUE rb_apply();
+VALUE rb_funcall2();
+
+static VALUE module_setup();
+
+static VALUE massign();
+static void assign();
+
+static int safe_level = 0;
+/* safe-level:
+ 0 - strings from streams/environment/ARGV are tainted (default)
+ 1 - no dangerous operation by tainted string
+ 2 - some process operations prohibited
+ 3 - all genetated strings are tainted
+ 4 - no global variable value modification/no direct output
+ 5 - no instance variable value modification
+*/
+
+int
+rb_safe_level()
+{
+ return safe_level;
+}
+
+void
+rb_set_safe_level(level)
+ int level;
+{
+ if (level > safe_level) {
+ safe_level = level;
+ }
+}
+
+static VALUE
+safe_getter()
+{
+ return INT2FIX(safe_level);
+}
+
+static void
+safe_setter(val)
+ VALUE val;
+{
+ int level = NUM2INT(val);
+
+ if (level < safe_level) {
+ Raise(eSecurityError, "tried to downgrade safe level from %d to %d",
+ safe_level, level);
+ }
+ safe_level = level;
+}
+
+void
+rb_check_safe_str(x)
+ VALUE x;
+{
+ if (TYPE(x)!= T_STRING) {
+ TypeError("wrong argument type %s (expected String)",
+ rb_class2name(CLASS_OF(x)));
+ }
+ if (rb_safe_level() > 0 && str_tainted(x)) {
+ Raise(eSecurityError, "Insecure operation - %s",
+ rb_id2name(the_frame->last_func));
+ }
+}
+
+void
+rb_secure(level)
+ int level;
+{
+ if (level <= safe_level) {
+ Raise(eSecurityError, "Insecure operation `%s' for level %d",
+ rb_id2name(the_frame->last_func), level);
+ }
+}
+
+extern int sourceline;
+extern char *sourcefile;
+
+static VALUE trace_func = 0;
+static void call_trace_func();
+
+static void
+error_pos()
+{
+ if (sourcefile) {
+ if (the_frame->last_func) {
+ fprintf(stderr, "%s:%d:in `%s'", sourcefile, sourceline,
+ rb_id2name(the_frame->last_func));
+ }
+ else {
+ fprintf(stderr, "%s:%d", sourcefile, sourceline);
+ }
+ }
+}
+
+static void
+error_print()
+{
+ VALUE eclass;
+
+ if (NIL_P(errinfo)) return;
+
+ if (!NIL_P(errat)) {
+ VALUE mesg = Qnil;
+
+ switch (TYPE(errat)) {
+ case T_STRING:
+ mesg = errat;
+ errat = Qnil;
+ break;
+ case T_ARRAY:
+ mesg = RARRAY(errat)->ptr[0];
+ break;
+ }
+ if (NIL_P(mesg)) error_pos();
+ else {
+ fwrite(RSTRING(mesg)->ptr, 1, RSTRING(mesg)->len, stderr);
+ }
+ }
+
+ eclass = CLASS_OF(errinfo);
+ if (eclass == eRuntimeError && RSTRING(errinfo)->len == 0) {
+ fprintf(stderr, ": unhandled exception\n");
+ }
+ else {
+ PUSH_TAG();
+ if (EXEC_TAG() == 0) {
+ VALUE epath = rb_class_path(eclass);
+ if (RSTRING(epath)->ptr[0] != '#') {
+ fprintf(stderr, ": ");
+ fwrite(RSTRING(epath)->ptr, 1, RSTRING(epath)->len, stderr);
+ }
+ }
+ POP_TAG();
+
+ if (RSTRING(errinfo)->len > 0) {
+ fprintf(stderr, ": ");
+ fwrite(RSTRING(errinfo)->ptr, 1, RSTRING(errinfo)->len, stderr);
+ }
+ if (RSTRING(errinfo)->ptr[RSTRING(errinfo)->len - 1] != '\n') {
+ putc('\n', stderr);
+ }
+ }
+
+ if (!NIL_P(errat)) {
+ int i;
+ struct RArray *ep = RARRAY(errat);
+
+#define TRACE_MAX (TRACE_HEAD+TRACE_TAIL+5)
+#define TRACE_HEAD 8
+#define TRACE_TAIL 5
+
+ for (i=1; i<ep->len; i++) {
+ fprintf(stderr, "\tfrom %s\n", RSTRING(ep->ptr[i])->ptr);
+ if (i == TRACE_HEAD && ep->len > TRACE_MAX) {
+ fprintf(stderr, "\t ... %d levels...\n",
+ ep->len - TRACE_HEAD - TRACE_TAIL);
+ i = ep->len - TRACE_TAIL;
+ }
+ }
+ }
+}
+
+#ifndef NT
+extern char **environ;
+#endif
+char **origenviron;
+
+void
+ruby_init()
+{
+ static struct FRAME frame;
+ static struct iter iter;
+ NODE *state;
+
+ the_frame = top_frame = &frame;
+ the_iter = &iter;
+
+ origenviron = environ;
+
+ init_heap();
+ PUSH_SCOPE();
+ the_scope->local_vars = 0;
+ the_scope->local_tbl = 0;
+ top_scope = the_scope;
+
+ PUSH_TAG()
+ if ((state = EXEC_TAG()) == 0) {
+ rb_call_inits();
+ the_class = (struct RClass*)cObject;
+ the_frame->cbase = (VALUE)node_newnode(NODE_CREF,cObject,0,0);
+ rb_define_global_const("TOPLEVEL_BINDING", f_binding(TopSelf));
+ ruby_prog_init();
+ }
+ POP_TAG();
+ if (state) error_print();
+ POP_SCOPE();
+ the_scope = top_scope;
+}
+
+static int ext_init = 0;
+
+void
+ruby_options(argc, argv)
+ int argc;
+ char **argv;
+{
+ NODE *state;
+
+ PUSH_TAG()
+ if ((state = EXEC_TAG()) == 0) {
+ NODE *save;
+
+ Init_ext();
+ ext_init = 1;
+ ruby_process_options(argc, argv);
+ save = eval_tree;
+ rb_require_modules();
+ eval_tree = save;
+ }
+ POP_TAG();
+ if (state) {
+ error_print();
+ exit(1);
+ }
+}
+
+static VALUE
+eval_node(self)
+ VALUE self;
+{
+ VALUE result = Qnil;
+ NODE *tree;
+
+ if (!eval_tree) return Qnil;
+
+ tree = eval_tree;
+ eval_tree = 0;
+
+ result = rb_eval(self, tree);
+ return result;
+}
+
+int rb_in_eval;
+
+#ifdef THREAD
+static void thread_cleanup();
+static void thread_wait_other_threads();
+static VALUE thread_current();
+#endif
+
+static int exit_status;
+
+void
+ruby_run()
+{
+ NODE *state;
+ static NODE *ex;
+
+ if (nerrs > 0) exit(nerrs);
+
+ init_stack();
+ errat = Qnil; /* clear for execution */
+
+ PUSH_TAG();
+ PUSH_ITER(ITER_NOT);
+ if ((state = EXEC_TAG()) == 0) {
+ if (!ext_init) Init_ext();
+ eval_node(TopSelf);
+ }
+ POP_ITER();
+ POP_TAG();
+
+ if (state && !ex) ex = state;
+ PUSH_TAG();
+ PUSH_ITER(ITER_NOT);
+ if ((state = EXEC_TAG()) == 0) {
+ rb_trap_exit();
+#ifdef THREAD
+ thread_cleanup();
+ thread_wait_other_threads();
+#endif
+ }
+ else {
+ ex = state;
+ }
+ POP_ITER();
+ POP_TAG();
+
+ if (!ex) {
+ exit(0);
+ }
+
+ switch (ex->nd_tag) {
+ case IN_BLOCK|TAG_RETURN:
+ case TAG_RETURN:
+ error_pos();
+ fprintf(stderr, "unexpected return\n");
+ exit(1);
+ break;
+ case TAG_NEXT:
+ error_pos();
+ fprintf(stderr, "unexpected next\n");
+ exit(1);
+ break;
+ case IN_BLOCK|TAG_BREAK:
+ case TAG_BREAK:
+ error_pos();
+ fprintf(stderr, "unexpected break\n");
+ exit(1);
+ break;
+ case TAG_REDO:
+ error_pos();
+ fprintf(stderr, "unexpected redo\n");
+ exit(1);
+ break;
+ case TAG_RETRY:
+ error_pos();
+ fprintf(stderr, "retry outside of rescue clause\n");
+ exit(1);
+ break;
+ case TAG_RAISE:
+ case TAG_FATAL:
+ if (obj_is_kind_of(errinfo, eSystemExit)) {
+ exit(exit_status);
+ }
+ error_print();
+ exit(1);
+ break;
+ case TAG_THROW:
+ error_pos();
+ fprintf(stderr, "uncaught throw `%s'\n", rb_id2name(ex->nd_tlev));
+ exit(1);
+ break;
+ default:
+ Bug("Unknown longjmp status %d", ex->nd_tag);
+ break;
+ }
+}
+
+static void
+compile_error(at)
+ char *at;
+{
+ VALUE mesg;
+
+ mesg = errinfo;
+ nerrs = 0;
+ errinfo = exc_new2(eSyntaxError, "compile error in ");
+ str_cat(errinfo, at, strlen(at));
+ str_cat(errinfo, ":\n", 2);
+ str_cat(errinfo, RSTRING(mesg)->ptr, RSTRING(mesg)->len);
+ rb_raise(errinfo);
+}
+
+VALUE
+rb_eval_string(str)
+ char *str;
+{
+ VALUE v;
+ char *oldsrc = sourcefile;
+
+ sourcefile = "(eval)";
+ v = eval(TopSelf, str_new2(str), Qnil);
+ sourcefile = oldsrc;
+
+ return v;
+}
+
+void
+rb_eval_cmd(cmd, arg)
+ VALUE cmd, arg;
+{
+ NODE *state;
+ struct SCOPE *saved_scope;
+ volatile int safe = rb_safe_level();
+
+ if (TYPE(cmd) != T_STRING) {
+ if (obj_is_kind_of(cmd, cProc)) {
+ proc_call(cmd, arg);
+ return;
+ }
+ }
+
+ PUSH_CLASS();
+ PUSH_TAG();
+ saved_scope = the_scope;
+ the_scope = top_scope;
+
+ the_class = (struct RClass*)cObject;
+ if (str_tainted(cmd)) {
+ safe_level = 5;
+ }
+
+ if ((state = EXEC_TAG()) == 0) {
+ eval(TopSelf, cmd, Qnil);
+ }
+
+ the_scope = saved_scope;
+ safe_level = safe;
+ POP_TAG();
+ POP_CLASS();
+
+ if (state == 0) return;
+ switch (state->nd_tag) {
+ case TAG_RETURN:
+ Raise(eLocalJumpError, "unexpected return");
+ break;
+ case TAG_NEXT:
+ Raise(eLocalJumpError, "unexpected next");
+ break;
+ case TAG_BREAK:
+ Raise(eLocalJumpError, "unexpected break");
+ break;
+ case TAG_REDO:
+ Raise(eLocalJumpError, "unexpected redo");
+ break;
+ case TAG_RETRY:
+ Raise(eLocalJumpError, "retry outside of rescue clause");
+ break;
+ default:
+ JUMP_TAG(state);
+ break;
+ }
+}
+
+void
+rb_trap_eval(cmd, sig)
+ VALUE cmd;
+ int sig;
+{
+ NODE *state;
+
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ rb_eval_cmd(cmd, ary_new3(1, INT2FIX(sig)));
+ }
+ POP_TAG();
+ if (state) {
+ trap_immediate = 0;
+ JUMP_TAG(state);
+ }
+}
+
+static VALUE
+superclass(self, node)
+ VALUE self;
+ NODE *node;
+{
+ VALUE val = 0;
+ NODE *state;
+
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ val = rb_eval(self, node);
+ }
+ POP_TAG();
+ if (state) {
+ if (state->nd_tag == TAG_RAISE) {
+ superclass_error:
+ switch (nd_type(node)) {
+ case NODE_COLON2:
+ TypeError("undefined superclass `%s'", rb_id2name(node->nd_mid));
+ case NODE_CVAR:
+ TypeError("undefined superclass `%s'", rb_id2name(node->nd_vid));
+ default:
+ TypeError("superclass undefined");
+ }
+ }
+ JUMP_TAG(state);
+ }
+ if (TYPE(val) != T_CLASS) goto superclass_error;
+ if (FL_TEST(val, FL_SINGLETON)) {
+ TypeError("can't make subclass of virtual class");
+ }
+
+ return val;
+}
+
+static VALUE
+ev_const_defined(cref, id)
+ NODE *cref;
+ ID id;
+{
+ NODE *cbase = cref;
+
+ while (cbase && cbase->nd_clss != cObject) {
+ struct RClass *class = RCLASS(cbase->nd_clss);
+
+ if (class->iv_tbl &&
+ st_lookup(class->iv_tbl, id, 0)) {
+ return TRUE;
+ }
+ cbase = cbase->nd_next;
+ }
+ return rb_const_defined(cref->nd_clss, id);
+}
+
+static VALUE
+ev_const_get(cref, id)
+ NODE *cref;
+ ID id;
+{
+ NODE *cbase = cref;
+ VALUE result;
+
+ while (cbase && cbase->nd_clss != cObject) {
+ struct RClass *class = RCLASS(cbase->nd_clss);
+
+ if (class->iv_tbl &&
+ st_lookup(class->iv_tbl, id, &result)) {
+ return result;
+ }
+ cbase = cbase->nd_next;
+ }
+ return rb_const_get(cref->nd_clss, id);
+}
+
+#define SETUP_ARGS(anode) {\
+ NODE *n = anode;\
+ if (!n) {\
+ argc = 0;\
+ argv = 0;\
+ }\
+ else if (nd_type(n) == NODE_ARRAY) {\
+ argc=n->nd_alen;\
+ if (argc > 0) {\
+ int i;\
+ int line = sourceline;\
+ n = anode;\
+ argv = ALLOCA_N(VALUE,argc);\
+ for (i=0;i<argc;i++) {\
+ argv[i] = rb_eval(self,n->nd_head);\
+ n=n->nd_next;\
+ }\
+ sourcefile = anode->file;\
+ sourceline = line;\
+ }\
+ else {\
+ argc = 0;\
+ argv = 0;\
+ }\
+ }\
+ else {\
+ VALUE args = rb_eval(self,n);\
+ int line = sourceline;\
+ if (TYPE(args) != T_ARRAY)\
+ args = rb_to_a(args);\
+ argc = RARRAY(args)->len;\
+ argv = ALLOCA_N(VALUE, argc);\
+ MEMCPY(argv, RARRAY(args)->ptr, VALUE, argc);\
+ sourcefile = anode->file;\
+ sourceline = line;\
+ }\
+}
+
+int
+rb_test_false_or_nil(v)
+ VALUE v;
+{
+ return (v != Qnil) && (v != FALSE);
+}
+
+#define MATCH_DATA the_scope->local_vars[node->nd_cnt]
+
+static char*
+is_defined(self, node, buf)
+ VALUE self;
+ NODE *node; /* OK */
+ char *buf;
+{
+ VALUE val; /* OK */
+ NODE *state;
+
+ node = node->nd_head;
+
+ switch (nd_type(node)) {
+ case NODE_SUPER:
+ case NODE_ZSUPER:
+ if (the_frame->last_func == 0) return 0;
+ else if (method_boundp(the_frame->last_class->super,
+ the_frame->last_func, 1)) {
+ return "super";
+ }
+ break;
+
+ case NODE_FCALL:
+ case NODE_VCALL:
+ val = CLASS_OF(self);
+ goto check_bound;
+
+ case NODE_CALL:
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ val = rb_eval(self, node->nd_recv);
+ val = CLASS_OF(val);
+ }
+ POP_TAG();
+ if (state) {
+ return 0;
+ }
+ check_bound:
+ if (method_boundp(val, node->nd_mid,
+ nd_type(node)== NODE_CALL)) {
+ return "method";
+ }
+ break;
+
+ case NODE_YIELD:
+ if (iterator_p()) {
+ return "yield";
+ }
+ break;
+
+ case NODE_SELF:
+ return "self";
+
+ case NODE_NIL:
+ return "nil";
+
+ case NODE_ATTRSET:
+ case NODE_OP_ASGN1:
+ case NODE_OP_ASGN2:
+ case NODE_MASGN:
+ case NODE_LASGN:
+ case NODE_DASGN:
+ case NODE_GASGN:
+ case NODE_IASGN:
+ case NODE_CASGN:
+ return "assignment";
+
+ case NODE_LVAR:
+ case NODE_DVAR:
+ return "local-variable";
+
+ case NODE_GVAR:
+ if (rb_gvar_defined(node->nd_entry)) {
+ return "global-variable";
+ }
+ break;
+
+ case NODE_IVAR:
+ if (rb_ivar_defined(self, node->nd_vid)) {
+ return "instance-variable";
+ }
+ break;
+
+ case NODE_CVAR:
+ if (ev_const_defined(the_frame->cbase, node->nd_vid)) {
+ return "constant";
+ }
+ break;
+
+ case NODE_COLON2:
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ val = rb_eval(self, node->nd_head);
+ }
+ POP_TAG();
+ if (state) {
+ return 0;
+ }
+ else {
+ switch (TYPE(val)) {
+ case T_CLASS:
+ case T_MODULE:
+ if (rb_const_defined_at(val, node->nd_mid))
+ return "constant";
+ }
+ }
+ break;
+
+ case NODE_NTH_REF:
+ if (reg_nth_defined(node->nd_nth, MATCH_DATA)) {
+ sprintf(buf, "$%d", node->nd_nth);
+ return buf;
+ }
+ break;
+
+ case NODE_BACK_REF:
+ if (reg_nth_defined(0, MATCH_DATA)) {
+ sprintf(buf, "$%c", node->nd_nth);
+ return buf;
+ }
+ break;
+
+ default:
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ rb_eval(self, node);
+ }
+ POP_TAG();
+ if (!state) {
+ return "expression";
+ }
+ break;
+ }
+ return 0;
+}
+
+static int handle_rescue();
+VALUE rb_yield_0();
+
+static void blk_free();
+
+static VALUE
+set_trace_func(obj, trace)
+ VALUE obj;
+ struct RData *trace;
+{
+ if (NIL_P(trace)) {
+ trace_func = 0;
+ return Qnil;
+ }
+ if (TYPE(trace) != T_DATA || trace->dfree != blk_free) {
+ TypeError("trace_func needs to be Proc");
+ }
+ return trace_func = (VALUE)trace;
+}
+
+static void
+call_trace_func(event, file, line, self, id)
+ char *event;
+ char *file;
+ int line;
+ VALUE self;
+ ID id;
+{
+ NODE *state;
+ volatile VALUE trace;
+ struct FRAME *prev;
+
+ if (!trace_func) return;
+
+ trace = trace_func;
+ trace_func = 0;
+#ifdef THREAD
+ thread_critical++;
+#endif
+
+ prev = the_frame;
+ PUSH_FRAME();
+ *the_frame = *_frame.prev;
+ the_frame->prev = prev;
+
+ the_frame->line = sourceline = line;
+ the_frame->file = sourcefile = file;
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ proc_call(trace, ary_new3(5, str_new2(event),
+ str_new2(sourcefile),
+ INT2FIX(sourceline),
+ INT2FIX(id),
+ self?f_binding(self):Qnil));
+ }
+ POP_TAG();
+ POP_FRAME();
+
+#ifdef THREAD
+ thread_critical--;
+#endif
+ if (!trace_func) trace_func = trace;
+ if (state) JUMP_TAG(state);
+}
+
+static VALUE
+rb_eval(self, node)
+ VALUE self;
+ NODE * volatile node;
+{
+ NODE *state;
+ volatile VALUE result = Qnil;
+
+#define RETURN(v) { result = (v); goto finish; }
+
+ again:
+ if (!node) RETURN(Qnil);
+
+#if 0
+ sourceline = nd_line(node);
+ sourcefile = node->file;
+#endif
+ switch (nd_type(node)) {
+ case NODE_BLOCK:
+ while (node) {
+ result = rb_eval(self, node->nd_head);
+ node = node->nd_next;
+ }
+ break;
+
+ /* begin .. end without clauses */
+ case NODE_BEGIN:
+ node = node->nd_body;
+ goto again;
+
+ /* nodes for speed-up(default match) */
+ case NODE_MATCH:
+ result = reg_match2(node->nd_head->nd_lit);
+ break;
+
+ /* nodes for speed-up(top-level loop for -n/-p) */
+ case NODE_OPT_N:
+ while (!NIL_P(f_gets())) {
+ rb_eval(self, node->nd_body);
+ }
+ RETURN(Qnil);
+
+ case NODE_SELF:
+ RETURN(self);
+
+ case NODE_NIL:
+ RETURN(Qnil);
+
+ case NODE_IF:
+ if (RTEST(rb_eval(self, node->nd_cond))) {
+ node = node->nd_body;
+ }
+ else {
+ node = node->nd_else;
+ }
+ goto again;
+
+ case NODE_CASE:
+ {
+ VALUE val;
+
+ val = rb_eval(self, node->nd_head);
+ node = node->nd_body;
+ while (node) {
+ NODE *tag;
+
+ if (nd_type(node) != NODE_WHEN) {
+ goto again;
+ }
+ tag = node->nd_head;
+ while (tag) {
+ if (trace_func) {
+ call_trace_func("line", tag->file, nd_line(tag),
+ self, the_frame->last_func);
+ }
+ if (RTEST(rb_funcall2(rb_eval(self, tag->nd_head),eqq,1,&val))){
+ node = node->nd_body;
+ goto again;
+ }
+ tag = tag->nd_next;
+ }
+ node = node->nd_next;
+ }
+ }
+ RETURN(Qnil);
+
+ case NODE_WHILE:
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ if (node->nd_state && !RTEST(rb_eval(self, node->nd_cond)))
+ goto while_out;
+ do {
+ while_redo:
+ rb_eval(self, node->nd_body);
+ while_next:
+ ;
+ } while (RTEST(rb_eval(self, node->nd_cond)));
+ }
+ else {
+ switch (state->nd_tag) {
+ case TAG_REDO:
+ state = 0;
+ goto while_redo;
+ case TAG_NEXT:
+ state = 0;
+ goto while_next;
+ case TAG_BREAK:
+ state = 0;
+ default:
+ break;
+ }
+ }
+ while_out:
+ POP_TAG();
+ if (state) {
+ JUMP_TAG(state);
+ }
+ RETURN(Qnil);
+
+ case NODE_UNTIL:
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ if (node->nd_state && RTEST(rb_eval(self, node->nd_cond)))
+ goto until_out;
+ do {
+ until_redo:
+ rb_eval(self, node->nd_body);
+ until_next:
+ ;
+ } while (!RTEST(rb_eval(self, node->nd_cond)));
+ }
+ else {
+ switch (state->nd_tag) {
+ case TAG_REDO:
+ state = 0;
+ goto until_redo;
+ case TAG_NEXT:
+ state = 0;
+ goto until_next;
+ case TAG_BREAK:
+ state = 0;
+ default:
+ break;
+ }
+ }
+ until_out:
+ POP_TAG();
+ if (state) {
+ JUMP_TAG(state);
+ }
+ RETURN(Qnil);
+
+ case NODE_ITER:
+ case NODE_FOR:
+ {
+ int tag_level;
+
+ iter_retry:
+ PUSH_BLOCK(node->nd_var, node->nd_body);
+ PUSH_TAG();
+
+ state = EXEC_TAG();
+ if (state == 0) {
+ if (nd_type(node) == NODE_ITER) {
+ PUSH_ITER(ITER_PRE);
+ result = rb_eval(self, node->nd_iter);
+ POP_ITER();
+ }
+ else {
+ VALUE recv;
+ int line = sourceline;
+
+ recv = rb_eval(self, node->nd_iter);
+ PUSH_ITER(ITER_PRE);
+ sourcefile = node->file;
+ sourceline = line;
+ result = rb_call(CLASS_OF(recv),recv,each,0,0,0);
+ POP_ITER();
+ }
+ }
+ POP_TAG();
+ tag_level = the_block->level;
+ POP_BLOCK();
+ if (state == 0) break;
+ switch (state->nd_tag) {
+ case TAG_RETRY:
+ goto iter_retry;
+
+ case IN_BLOCK|TAG_BREAK:
+ if (state->nd_tlev != tag_level) {
+ JUMP_TAG(state);
+ }
+ result = Qnil;
+ break;
+ case IN_BLOCK|TAG_RETURN:
+ if (state->nd_tlev == tag_level) {
+ state->nd_tag &= ~IN_BLOCK;
+ }
+ /* fall through */
+ default:
+ JUMP_TAG(state);
+ }
+ }
+ break;
+
+ case NODE_YIELD:
+ result = rb_yield_0(rb_eval(self, node->nd_stts), 0);
+ break;
+
+ case NODE_RESCUE:
+ retry_entry:
+ {
+ volatile VALUE e_info = errinfo, e_at = errat;
+
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ result = rb_eval(self, node->nd_head);
+ }
+ POP_TAG();
+ if (state) {
+ if (state->nd_tag == TAG_RAISE) {
+ NODE * volatile resq = node->nd_resq;
+ while (resq) {
+ if (handle_rescue(self, resq)) {
+ state = 0;
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ result = rb_eval(self, resq->nd_body);
+ }
+ POP_TAG();
+ if (state == 0) {
+ errinfo = e_info;
+ errat = e_at;
+ }
+ else if (state->nd_tag == TAG_RETRY) {
+ state = 0;
+ goto retry_entry;
+ }
+ break;
+ }
+ resq = resq->nd_head; /* next rescue */
+ }
+ }
+ if (state) {
+ JUMP_TAG(state);
+ }
+ }
+ }
+ break;
+
+ case NODE_ENSURE:
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ result = rb_eval(self, node->nd_head);
+ }
+ POP_TAG();
+ rb_eval(self, node->nd_ensr);
+ if (state) {
+ JUMP_TAG(state);
+ }
+ break;
+
+ case NODE_AND:
+ result = rb_eval(self, node->nd_1st);
+ if (!RTEST(result)) break;
+ node = node->nd_2nd;
+ goto again;
+
+ case NODE_OR:
+ result = rb_eval(self, node->nd_1st);
+ if (RTEST(result)) break;
+ node = node->nd_2nd;
+ goto again;
+
+ case NODE_NOT:
+ if (RTEST(rb_eval(self, node->nd_body))) result = FALSE;
+ else result = TRUE;
+ break;
+
+ case NODE_DOT2:
+ case NODE_DOT3:
+ RETURN(range_new(rb_eval(self, node->nd_beg), rb_eval(self, node->nd_end)));
+
+ case NODE_FLIP2: /* like AWK */
+ if (node->nd_state == 0) {
+ if (RTEST(rb_eval(self, node->nd_beg))) {
+ node->nd_state = rb_eval(self, node->nd_end)?0:1;
+ result = TRUE;
+ }
+ else {
+ result = FALSE;
+ }
+ }
+ else {
+ if (RTEST(rb_eval(self, node->nd_end))) {
+ node->nd_state = 0;
+ }
+ result = TRUE;
+ }
+ break;
+
+ case NODE_FLIP3: /* like SED */
+ if (node->nd_state == 0) {
+ if (RTEST(rb_eval(self, node->nd_beg))) {
+ node->nd_state = 1;
+ result = TRUE;
+ }
+ result = FALSE;
+ }
+ else {
+ if (RTEST(rb_eval(self, node->nd_end))) {
+ node->nd_state = 0;
+ }
+ result = TRUE;
+ }
+ break;
+
+ case NODE_RETURN:
+ JUMP_TAG2(TAG_RETURN,(node->nd_stts)?rb_eval(self, node->nd_stts):Qnil);
+ break;
+
+ case NODE_CALL:
+ {
+ VALUE recv;
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+
+ PUSH_ITER(ITER_NOT);
+ recv = rb_eval(self, node->nd_recv);
+ SETUP_ARGS(node->nd_args);
+ POP_ITER();
+ result = rb_call(CLASS_OF(recv),recv,node->nd_mid,argc,argv,0);
+ }
+ break;
+
+ case NODE_FCALL:
+ {
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+
+ PUSH_ITER(ITER_NOT);
+ SETUP_ARGS(node->nd_args);
+ POP_ITER();
+ result = rb_call(CLASS_OF(self),self,node->nd_mid,argc,argv,1);
+ }
+ break;
+
+ case NODE_VCALL:
+ result = rb_call(CLASS_OF(self),self,node->nd_mid,0,0,2);
+ break;
+
+ case NODE_SUPER:
+ case NODE_ZSUPER:
+ {
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+
+ if (nd_type(node) == NODE_ZSUPER) {
+ argc = the_frame->argc;
+ argv = the_frame->argv;
+ }
+ else {
+ PUSH_ITER(ITER_NOT);
+ SETUP_ARGS(node->nd_args);
+ POP_ITER();
+ }
+
+ PUSH_ITER(the_iter->iter?ITER_PRE:ITER_NOT);
+ result = rb_call(the_frame->last_class->super, self,
+ the_frame->last_func, argc, argv, 1);
+ POP_ITER();
+ }
+ break;
+
+ case NODE_SCOPE:
+ {
+ VALUE save = the_frame->cbase;
+
+ PUSH_SCOPE();
+ PUSH_TAG();
+ if (node->nd_rval) the_frame->cbase = (VALUE)node->nd_rval;
+ if (node->nd_tbl) {
+ VALUE *vars = ALLOCA_N(VALUE, node->nd_tbl[0]+1);
+ *vars++ = (VALUE)node;
+ the_scope->local_vars = vars;
+ memclear(the_scope->local_vars, node->nd_tbl[0]);
+ the_scope->local_tbl = node->nd_tbl;
+ }
+ else {
+ the_scope->local_vars = 0;
+ the_scope->local_tbl = 0;
+ }
+ if ((state = EXEC_TAG()) == 0) {
+ result = rb_eval(self, node->nd_body);
+ }
+ POP_TAG();
+ POP_SCOPE();
+ the_frame->cbase = save;
+ if (state) JUMP_TAG(state);
+ }
+ break;
+
+ case NODE_OP_ASGN1:
+ {
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+ VALUE recv, val;
+ NODE *rval;
+
+ recv = rb_eval(self, node->nd_recv);
+ rval = node->nd_args->nd_head;
+ SETUP_ARGS(node->nd_args->nd_next);
+ val = rb_funcall2(recv, aref, argc-1, argv);
+ val = rb_funcall(val, node->nd_mid, 1, rb_eval(self, rval));
+ argv[argc-1] = val;
+ val = rb_funcall2(recv, aset, argc, argv);
+ result = val;
+ }
+ break;
+
+ case NODE_OP_ASGN2:
+ {
+ ID id = node->nd_next->nd_vid;
+ VALUE recv, val;
+
+ recv = rb_eval(self, node->nd_recv);
+ val = rb_funcall(recv, id, 0);
+
+ val = rb_funcall(val, node->nd_next->nd_mid, 1,
+ rb_eval(self, node->nd_value));
+
+ rb_funcall2(recv, id_attrset(id), 1, &val);
+ result = val;
+ }
+ break;
+
+ case NODE_MASGN:
+ result = massign(self, node, rb_eval(self, node->nd_value));
+ break;
+
+ case NODE_LASGN:
+ if (the_scope->local_vars == 0)
+ Bug("unexpected local variable assignment");
+ the_scope->local_vars[node->nd_cnt] = rb_eval(self, node->nd_value);
+ result = the_scope->local_vars[node->nd_cnt];
+ break;
+
+ case NODE_DASGN:
+ result = dyna_var_asgn(node->nd_vid, rb_eval(self, node->nd_value));
+ break;
+
+ case NODE_GASGN:
+ {
+ VALUE val;
+
+ val = rb_eval(self, node->nd_value);
+ rb_gvar_set(node->nd_entry, val);
+ result = val;
+ }
+ break;
+
+ case NODE_IASGN:
+ {
+ VALUE val;
+
+ val = rb_eval(self, node->nd_value);
+ rb_ivar_set(self, node->nd_vid, val);
+ result = val;
+ }
+ break;
+
+ case NODE_CASGN:
+ {
+ VALUE val;
+
+ val = rb_eval(self, node->nd_value);
+ /* check for static scope constants */
+ if (verbose && ev_const_defined(the_frame->cbase, node->nd_vid)) {
+ Warning("already initialized constant %s",
+ rb_id2name(node->nd_vid));
+ }
+ rb_const_set(the_class, node->nd_vid, val);
+ result = val;
+ }
+ break;
+
+ case NODE_LVAR:
+ if (the_scope->local_vars == 0) {
+ Bug("unexpected local variable");
+ }
+ result = the_scope->local_vars[node->nd_cnt];
+ break;
+
+ case NODE_DVAR:
+ result = dyna_var_ref(node->nd_vid);
+ break;
+
+ case NODE_GVAR:
+ result = rb_gvar_get(node->nd_entry);
+ break;
+
+ case NODE_IVAR:
+ result = rb_ivar_get(self, node->nd_vid);
+ break;
+
+ case NODE_CVAR:
+ result = ev_const_get(the_frame->cbase, node->nd_vid);
+ break;
+
+ case NODE_COLON2:
+ {
+ VALUE cls;
+
+ cls = rb_eval(self, node->nd_head);
+ switch (TYPE(cls)) {
+ case T_CLASS:
+ case T_MODULE:
+ break;
+ default:
+ Check_Type(cls, T_CLASS);
+ break;
+ }
+ result = rb_const_get_at(cls, node->nd_mid);
+ }
+ break;
+
+ case NODE_NTH_REF:
+ result = reg_nth_match(node->nd_nth, MATCH_DATA);
+ break;
+
+ case NODE_BACK_REF:
+ switch (node->nd_nth) {
+ case '&':
+ result = reg_last_match(MATCH_DATA);
+ break;
+ case '`':
+ result = reg_match_pre(MATCH_DATA);
+ break;
+ case '\'':
+ result = reg_match_post(MATCH_DATA);
+ break;
+ case '+':
+ result = reg_match_last(MATCH_DATA);
+ break;
+ default:
+ Bug("unexpected back-ref");
+ }
+ break;
+
+ case NODE_HASH:
+ {
+ NODE *list;
+ VALUE hash = hash_new();
+ VALUE key, val;
+
+ list = node->nd_head;
+ while (list) {
+ key = rb_eval(self, list->nd_head);
+ list = list->nd_next;
+ if (list == 0)
+ Bug("odd number list for Hash");
+ val = rb_eval(self, list->nd_head);
+ list = list->nd_next;
+ hash_aset(hash, key, val);
+ }
+ result = hash;
+ }
+ break;
+
+ case NODE_ZARRAY: /* zero length list */
+ result = ary_new();
+ break;
+
+ case NODE_ARRAY:
+ {
+ VALUE ary;
+ int i;
+
+ i = node->nd_alen;
+ ary = ary_new2(i);
+ for (i=0;node;node=node->nd_next) {
+ RARRAY(ary)->ptr[i++] = rb_eval(self, node->nd_head);
+ RARRAY(ary)->len = i;
+ }
+
+ result = ary;
+ }
+ break;
+
+ case NODE_STR:
+ result = str_new3(node->nd_lit);
+ break;
+
+ case NODE_DSTR:
+ case NODE_DXSTR:
+ case NODE_DREGX:
+ case NODE_DREGX_ONCE:
+ {
+ VALUE str, str2;
+ NODE *list = node->nd_next;
+
+ str = str_new3(node->nd_lit);
+ while (list) {
+ if (nd_type(list->nd_head) == NODE_STR) {
+ str2 = list->nd_head->nd_lit;
+ }
+ else {
+ if (nd_type(list->nd_head) == NODE_EVSTR) {
+ rb_in_eval++;
+ list->nd_head = compile(list->nd_head->nd_lit);
+ rb_in_eval--;
+ if (nerrs > 0) {
+ compile_error("string expand");
+ }
+ }
+ str2 = rb_eval(self, list->nd_head);
+ str2 = obj_as_string(str2);
+ }
+ if (str2) {
+ str_cat(str, RSTRING(str2)->ptr, RSTRING(str2)->len);
+ }
+ list = list->nd_next;
+ }
+ switch (nd_type(node)) {
+ case NODE_DREGX:
+ result = reg_new(RSTRING(str)->ptr, RSTRING(str)->len,
+ node->nd_cflag);
+ break;
+ case NODE_DREGX_ONCE: /* regexp expand once */
+ result = reg_new(RSTRING(str)->ptr, RSTRING(str)->len,
+ node->nd_cflag);
+ nd_set_type(node, NODE_LIT);
+ node->nd_lit = result;
+ break;
+ case NODE_DXSTR:
+ result = rb_funcall(self, '`', 1, str);
+ break;
+ default:
+ result = str;
+ break;
+ }
+ }
+ break;
+
+ case NODE_XSTR:
+ result = rb_funcall(self, '`', 1, node->nd_lit);
+ break;
+
+ case NODE_LIT:
+ result = node->nd_lit;
+ break;
+
+ case NODE_ATTRSET:
+ if (the_frame->argc != 1)
+ ArgError("Wrong # of arguments(%d for 1)", the_frame->argc);
+ result = rb_ivar_set(self, node->nd_vid, the_frame->argv[0]);
+ break;
+
+ case NODE_DEFN:
+ if (node->nd_defn) {
+ NODE *body;
+ VALUE origin;
+ int noex;
+
+ body = search_method(the_class, node->nd_mid, &origin);
+ if (body) {
+ if (origin == (VALUE)the_class) {
+ Warning("redefine %s", rb_id2name(node->nd_mid));
+ }
+ rb_clear_cache();
+ }
+
+ if (body) noex = body->nd_noex;
+ else noex = node->nd_noex; /* default(1 for toplevel) */
+
+ rb_add_method(the_class, node->nd_mid, node->nd_defn, noex);
+ result = Qnil;
+ }
+ break;
+
+ case NODE_DEFS:
+ if (node->nd_defn) {
+ VALUE recv = rb_eval(self, node->nd_recv);
+ VALUE class;
+ NODE *body;
+
+ if (FIXNUM_P(recv)) {
+ TypeError("Can't define method \"%s\" for Fixnum",
+ rb_id2name(node->nd_mid));
+ }
+ if (NIL_P(recv)) {
+ TypeError("Can't define method \"%s\" for nil",
+ rb_id2name(node->nd_mid));
+ }
+ if (rb_special_const_p(recv)) {
+ TypeError("Can't define method \"%s\" for special constants",
+ rb_id2name(node->nd_mid));
+ }
+
+ class = rb_singleton_class(recv);
+ if (st_lookup(RCLASS(class)->m_tbl, node->nd_mid, &body)) {
+ Warning("redefine %s", rb_id2name(node->nd_mid));
+ }
+ rb_clear_cache();
+ rb_funcall(recv, rb_intern("singleton_method_added"),
+ 1, INT2FIX(node->nd_mid));
+ rb_add_method(class, node->nd_mid, node->nd_defn, NOEX_PUBLIC);
+ result = Qnil;
+ }
+ break;
+
+ case NODE_UNDEF:
+ {
+ struct RClass *origin;
+ NODE *body;
+
+ body = search_method(the_class, node->nd_mid, &origin);
+ if (!body || !body->nd_body) {
+ NameError("undefined method `%s' for class `%s'",
+ rb_id2name(node->nd_mid), rb_class2name((VALUE)the_class));
+ }
+ rb_clear_cache();
+ rb_add_method(the_class, node->nd_mid, 0, NOEX_PUBLIC);
+ result = Qnil;
+ }
+ break;
+
+ case NODE_ALIAS:
+ rb_alias(the_class, node->nd_new, node->nd_old);
+ result = Qnil;
+ break;
+
+ case NODE_VALIAS:
+ rb_alias_variable(node->nd_new, node->nd_old);
+ result = Qnil;
+ break;
+
+ case NODE_CLASS:
+ {
+ VALUE super, class;
+ struct RClass *tmp;
+
+ if (node->nd_super) {
+ super = superclass(self, node->nd_super);
+ }
+ else {
+ super = 0;
+ }
+
+ if (rb_const_defined_at(the_class, node->nd_cname) &&
+ ((VALUE)the_class != cObject ||
+ !rb_autoload_defined(node->nd_cname))) {
+
+ class = rb_const_get_at(the_class, node->nd_cname);
+ if (TYPE(class) != T_CLASS) {
+ TypeError("%s is not a class", rb_id2name(node->nd_cname));
+ }
+ if (super) {
+ tmp = RCLASS(class)->super;
+ if (FL_TEST(tmp, FL_SINGLETON)) {
+ tmp = RCLASS(tmp)->super;
+ }
+ while (TYPE(tmp) == T_ICLASS) {
+ tmp = RCLASS(tmp)->super;
+ }
+ if (tmp != RCLASS(super)) {
+ TypeError("superclass mismatch for %s",
+ rb_id2name(node->nd_cname));
+ }
+ }
+ if (safe_level >= 4) {
+ Raise(eSecurityError, "extending class prohibited");
+ }
+ rb_clear_cache();
+ Warning("extending class %s", rb_id2name(node->nd_cname));
+ }
+ else {
+ if (!super) super = cObject;
+ class = rb_define_class_id(node->nd_cname, super);
+ rb_const_set(the_class, node->nd_cname, class);
+ rb_set_class_path(class,the_class,rb_id2name(node->nd_cname));
+ }
+
+ result = module_setup(class, node->nd_body);
+ }
+ break;
+
+ case NODE_MODULE:
+ {
+ VALUE module;
+
+ if (rb_const_defined_at(the_class, node->nd_cname) &&
+ ((VALUE)the_class != cObject ||
+ !rb_autoload_defined(node->nd_cname))) {
+
+ module = rb_const_get_at(the_class, node->nd_cname);
+ if (TYPE(module) != T_MODULE) {
+ TypeError("%s is not a module", rb_id2name(node->nd_cname));
+ }
+ if (safe_level >= 4) {
+ Raise(eSecurityError, "extending module prohibited");
+ }
+ Warning("extending module %s", rb_id2name(node->nd_cname));
+ }
+ else {
+ module = rb_define_module_id(node->nd_cname);
+ rb_const_set(the_class, node->nd_cname, module);
+ rb_set_class_path(module,the_class,rb_id2name(node->nd_cname));
+ }
+
+ result = module_setup(module, node->nd_body);
+ }
+ break;
+
+ case NODE_SCLASS:
+ {
+ VALUE class;
+
+ class = rb_eval(self, node->nd_recv);
+ if (FIXNUM_P(class)) {
+ TypeError("No virtual class for Fixnums");
+ }
+ if (NIL_P(class)) {
+ TypeError("No virtual class for nil");
+ }
+ if (rb_special_const_p(class)) {
+ TypeError("No virtual class for special constants");
+ }
+ if (FL_TEST(CLASS_OF(class), FL_SINGLETON)) {
+ rb_clear_cache();
+ }
+ class = rb_singleton_class(class);
+
+ result = module_setup(class, node->nd_body);
+ }
+ break;
+
+ case NODE_DEFINED:
+ {
+ char buf[20];
+ char *desc = is_defined(self, node, buf);
+
+ if (desc) result = str_new2(desc);
+ else result = FALSE;
+ }
+ break;
+
+ case NODE_NEWLINE:
+ sourcefile = node->file;
+ sourceline = node->nd_nth;
+ if (trace_func) {
+ call_trace_func("line", sourcefile, sourceline,
+ self, the_frame->last_func);
+ }
+ node = node->nd_next;
+ goto again;
+
+ default:
+ Bug("unknown node type %d", nd_type(node));
+ }
+ finish:
+ CHECK_INTS;
+ return result;
+}
+
+static VALUE
+module_setup(module, node)
+ VALUE module;
+ NODE * volatile node;
+{
+ NODE *state;
+ VALUE save = the_frame->cbase;
+ VALUE result; /* OK */
+
+ /* fill c-ref */
+ node->nd_clss = module;
+ node = node->nd_body;
+
+ PUSH_CLASS();
+ the_class = (struct RClass*)module;
+ PUSH_SCOPE();
+
+ if (node->nd_rval) the_frame->cbase = node->nd_rval;
+ if (node->nd_tbl) {
+ VALUE *vars = ALLOCA_N(VALUE, node->nd_tbl[0]+1);
+ *vars++ = (VALUE)node;
+ the_scope->local_vars = vars;
+ memclear(the_scope->local_vars, node->nd_tbl[0]);
+ the_scope->local_tbl = node->nd_tbl;
+ }
+ else {
+ the_scope->local_vars = 0;
+ the_scope->local_tbl = 0;
+ }
+
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ if (trace_func) {
+ call_trace_func("class", node->file, nd_line(node),
+ the_class, the_frame->last_func);
+ }
+ result = rb_eval((VALUE)the_class, node->nd_body);
+ }
+ POP_TAG();
+ POP_SCOPE();
+ POP_CLASS();
+ the_frame->cbase = save;
+ if (trace_func) {
+ call_trace_func("end", node->file, nd_line(node), 0,
+ the_frame->last_func);
+ }
+ if (state) JUMP_TAG(state);
+
+ return result;
+}
+
+int
+rb_respond_to(obj, id)
+ VALUE obj;
+ ID id;
+{
+ if (rb_method_boundp(CLASS_OF(obj), id, 0)) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static VALUE
+obj_respond_to(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE mid, priv;
+ ID id;
+
+ rb_scan_args(argc, argv, "11", &mid, &priv);
+ id = rb_to_id(mid);
+ if (rb_method_boundp(CLASS_OF(obj), id, !RTEST(priv))) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static VALUE
+mod_method_defined(mod, mid)
+ VALUE mod, mid;
+{
+ if (rb_method_boundp(mod, rb_to_id(mid), 1)) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void
+rb_exit(status)
+ int status;
+{
+ if (prot_tag) {
+ exit_status = status;
+ rb_raise(exc_new(eSystemExit, 0, 0));
+ }
+ exit(status);
+}
+
+static VALUE
+f_exit(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE status;
+
+ rb_secure(2);
+ if (rb_scan_args(argc, argv, "01", &status) == 1) {
+ status = NUM2INT(status);
+ }
+ else {
+ status = 0;
+ }
+ rb_exit(status);
+ /* not reached */
+}
+
+static VALUE
+f_abort()
+{
+ rb_secure(2);
+ if (errinfo) {
+ error_print();
+ }
+ rb_exit(1);
+ /* not reached */
+}
+
+void
+rb_break()
+{
+ JUMP_TAG2(TAG_BREAK, 0);
+}
+
+static VALUE
+f_break()
+{
+ JUMP_TAG2(TAG_BREAK, 0);
+}
+
+static VALUE
+f_next()
+{
+ JUMP_TAG2(TAG_NEXT, 0);
+}
+
+static VALUE
+f_redo()
+{
+ JUMP_TAG2(TAG_REDO, 0);
+}
+
+static VALUE
+f_retry()
+{
+ JUMP_TAG2(TAG_RETRY, 0);
+}
+
+#ifdef __GNUC__
+static volatile voidfn rb_longjmp;
+#endif
+
+static VALUE make_backtrace();
+
+static void
+rb_longjmp(tag, mesg)
+ int tag;
+ VALUE mesg;
+{
+ if (NIL_P(errinfo) && NIL_P(mesg)) {
+ errinfo = exc_new(eRuntimeError, 0, 0);
+ }
+
+ if (sourcefile && (NIL_P(errat) || !NIL_P(mesg))) {
+ errat = make_backtrace();
+ }
+
+ if (!NIL_P(mesg)) {
+ if (obj_is_kind_of(mesg, eGlobalExit)) {
+ errinfo = mesg;
+ }
+ else {
+ errinfo = exc_new3(eRuntimeError, mesg);
+ }
+ str_freeze(errinfo);
+ }
+
+ JUMP_TAG2(tag, 0);
+}
+
+void
+rb_raise(mesg)
+ VALUE mesg;
+{
+ rb_longjmp(TAG_RAISE, mesg);
+}
+
+void
+rb_fatal(mesg)
+ VALUE mesg;
+{
+ rb_longjmp(TAG_FATAL, mesg);
+}
+
+void
+rb_interrupt()
+{
+ Raise(eInterrupt, "");
+}
+
+static VALUE
+f_raise(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE arg1, arg2;
+ VALUE etype, mesg;
+ int n;
+
+ etype = eRuntimeError;
+ mesg = Qnil;
+ switch (n = rb_scan_args(argc, argv, "02", &arg1, &arg2)) {
+ case 1:
+ mesg = arg1;
+ break;
+ case 2:
+ etype = arg1;
+ if (obj_is_kind_of(etype, eGlobalExit)) {
+ etype = CLASS_OF(etype);
+ }
+ else {
+ Check_Type(etype, T_CLASS);
+ }
+ mesg = arg2;
+ break;
+ }
+
+ if (!NIL_P(mesg)) {
+ Check_Type(mesg, T_STRING);
+ if (n == 2 || !obj_is_kind_of(mesg, eException)) {
+ mesg = exc_new3(etype, mesg);
+ }
+ }
+
+ PUSH_FRAME(); /* fake frame */
+ *the_frame = *_frame.prev->prev;
+ rb_raise(mesg);
+ POP_FRAME();
+}
+
+int
+iterator_p()
+{
+ if (the_frame->iter) return TRUE;
+ return FALSE;
+}
+
+static VALUE
+f_iterator_p()
+{
+ if (the_frame->prev && the_frame->prev->iter) return TRUE;
+ return FALSE;
+}
+
+VALUE
+rb_yield_0(val, self)
+ VALUE val;
+ volatile VALUE self;
+{
+ NODE *node;
+ NODE *state;
+ volatile VALUE result = Qnil;
+ struct BLOCK *block;
+ struct SCOPE *old_scope;
+ struct FRAME frame;
+
+ if (!iterator_p()) {
+ Raise(eLocalJumpError, "yield called out of iterator");
+ }
+
+ PUSH_VARS();
+ PUSH_CLASS();
+ block = the_block;
+ frame = block->frame;
+ frame.prev = the_frame;
+ the_frame = &(frame);
+ old_scope = the_scope;
+ the_scope = block->scope;
+ the_block = block->prev;
+ the_dyna_vars = block->d_vars;
+ the_class = block->class;
+ if (!self) self = block->self;
+ node = block->body;
+ if (block->var) {
+ if (nd_type(block->var) == NODE_MASGN)
+ massign(self, block->var, val);
+ else
+ assign(self, block->var, val);
+ }
+ PUSH_ITER(block->iter);
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ redo:
+ if (!node) {
+ result = Qnil;
+ }
+ else if (nd_type(node) == NODE_CFUNC) {
+ result = (*node->nd_cfnc)(val, node->nd_argc, self);
+ }
+ else {
+ result = rb_eval(self, node);
+ }
+ }
+ else {
+ switch (state->nd_tag) {
+ case TAG_REDO:
+ state = 0;
+ goto redo;
+ case TAG_NEXT:
+ state = 0;
+ result = Qnil;
+ break;
+ case TAG_BREAK:
+ case TAG_RETURN:
+ state->nd_tlev = block->level;
+ state->nd_tag = IN_BLOCK|state->nd_tag;
+ break;
+ default:
+ break;
+ }
+ }
+ POP_TAG();
+ POP_ITER();
+ POP_CLASS();
+ POP_VARS();
+ the_block = block;
+ the_frame = the_frame->prev;
+ the_scope = old_scope;
+ if (state) JUMP_TAG(state);
+ return result;
+}
+
+VALUE
+rb_yield(val)
+ VALUE val;
+{
+ return rb_yield_0(val, 0);
+}
+
+static VALUE
+f_loop()
+{
+ for (;;) { rb_yield(Qnil); }
+}
+
+static VALUE
+massign(self, node, val)
+ VALUE self;
+ NODE *node;
+ VALUE val;
+{
+ NODE *list;
+ int i, len;
+
+ list = node->nd_head;
+
+ if (val) {
+ if (TYPE(val) != T_ARRAY) {
+ val = rb_to_a(val);
+ }
+ len = RARRAY(val)->len;
+ for (i=0; list && i<len; i++) {
+ assign(self, list->nd_head, RARRAY(val)->ptr[i]);
+ list = list->nd_next;
+ }
+ if (node->nd_args) {
+ if (!list && i<len) {
+ assign(self, node->nd_args, ary_new4(len-i, RARRAY(val)->ptr+i));
+ }
+ else {
+ assign(self, node->nd_args, ary_new2(0));
+ }
+ }
+ }
+ else if (node->nd_args) {
+ assign(self, node->nd_args, Qnil);
+ }
+ while (list) {
+ assign(self, list->nd_head, Qnil);
+ list = list->nd_next;
+ }
+ return val;
+}
+
+static void
+assign(self, lhs, val)
+ VALUE self;
+ NODE *lhs;
+ VALUE val;
+{
+ switch (nd_type(lhs)) {
+ case NODE_GASGN:
+ rb_gvar_set(lhs->nd_entry, val);
+ break;
+
+ case NODE_IASGN:
+ rb_ivar_set(self, lhs->nd_vid, val);
+ break;
+
+ case NODE_LASGN:
+ if (the_scope->local_vars == 0)
+ Bug("unexpected iterator variable assignment");
+ the_scope->local_vars[lhs->nd_cnt] = val;
+ break;
+
+ case NODE_DASGN:
+ dyna_var_asgn(lhs->nd_vid, val);
+ break;
+
+ case NODE_CASGN:
+ rb_const_set(the_class, lhs->nd_vid, val);
+ break;
+
+ case NODE_CALL:
+ {
+ VALUE recv;
+ recv = rb_eval(self, lhs->nd_recv);
+ if (!lhs->nd_args->nd_head) {
+ /* attr set */
+ rb_funcall2(recv, lhs->nd_mid, 1, &val);
+ }
+ else {
+ /* array set */
+ VALUE args;
+
+ args = rb_eval(self, lhs->nd_args);
+ RARRAY(args)->ptr[RARRAY(args)->len-1] = val;
+ rb_apply(recv, lhs->nd_mid, args);
+ }
+ }
+ break;
+
+ default:
+ Bug("bug in variable assignment");
+ break;
+ }
+}
+
+VALUE
+rb_iterate(it_proc, data1, bl_proc, data2)
+ VALUE (*it_proc)(), (*bl_proc)();
+ void *data1, *data2;
+{
+ NODE *state;
+ volatile VALUE retval = Qnil;
+ NODE *node = NEW_CFUNC(bl_proc, data2);
+ VALUE self = TopSelf;
+ int tag_level;
+
+ iter_retry:
+ PUSH_ITER(ITER_PRE);
+ PUSH_BLOCK(0, node);
+ PUSH_TAG();
+
+ state = EXEC_TAG();
+ if (state == 0) {
+ retval = (*it_proc)(data1);
+ }
+ POP_TAG();
+
+ tag_level = the_block->level;
+ POP_BLOCK();
+ POP_ITER();
+
+ if (state) {
+ switch (state->nd_tag) {
+ case TAG_RETRY:
+ goto iter_retry;
+
+ case IN_BLOCK|TAG_BREAK:
+ if (state->nd_tlev != tag_level) {
+ JUMP_TAG(state);
+ }
+ retval = Qnil;
+ break;
+
+ case IN_BLOCK|TAG_RETURN:
+ if (state->nd_tlev == tag_level) {
+ state->nd_tag &= ~IN_BLOCK;
+ }
+ /* fall through */
+ default:
+ JUMP_TAG(state);
+ }
+ }
+ return retval;
+}
+
+static int
+handle_rescue(self, node)
+ VALUE self;
+ NODE *node;
+{
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+
+ if (!node->nd_args) {
+ return obj_is_kind_of(errinfo, eException);
+ }
+
+ PUSH_ITER(ITER_NOT);
+ SETUP_ARGS(node->nd_args);
+ POP_ITER();
+ while (argc--) {
+ if (!obj_is_kind_of(argv[0], cModule)) {
+ TypeError("class or module required for rescue clause");
+ }
+ if (obj_is_kind_of(errinfo, argv[0])) return 1;
+ argv++;
+ }
+ return 0;
+}
+
+VALUE
+rb_rescue(b_proc, data1, r_proc, data2)
+ VALUE (*b_proc)(), (*r_proc)();
+ void *data1, *data2;
+{
+ NODE *state;
+ volatile VALUE result;
+
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ retry_entry:
+ result = (*b_proc)(data1);
+ }
+ else {
+ if (state->nd_tag == TAG_RAISE) {
+ if (r_proc) {
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ result = (*r_proc)(data2, errinfo);
+ }
+ POP_TAG();
+ if (state && state->nd_tag == TAG_RETRY) {
+ state = 0;
+ goto retry_entry;
+ }
+ }
+ else {
+ result = Qnil;
+ state = 0;
+ }
+ if (state == 0) {
+ errat = Qnil;
+ }
+ }
+ }
+ POP_TAG();
+ if (state) JUMP_TAG(state);
+
+ return result;
+}
+
+VALUE
+rb_ensure(b_proc, data1, e_proc, data2)
+ VALUE (*b_proc)(), (*e_proc)();
+ void *data1, *data2;
+{
+ NODE *state;
+ volatile VALUE result = Qnil;
+
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ result = (*b_proc)(data1);
+ }
+ POP_TAG();
+
+ (*e_proc)(data2);
+ if (state) {
+ JUMP_TAG(state);
+ }
+ return result;
+}
+
+static int last_call_status;
+#define CSTAT_NOEX 1
+#define CSTAT_VCALL 2
+
+static VALUE
+f_missing(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE desc = 0;
+ ID id;
+ char *format = 0;
+ char *file = sourcefile;
+ int line = sourceline;
+
+ id = FIX2INT(argv[0]);
+ argc--; argv++;
+
+ switch (TYPE(obj)) {
+ case T_NIL:
+ format = "undefined method `%s' for nil";
+ break;
+ case T_TRUE:
+ format = "undefined method `%s' for TRUE";
+ break;
+ case T_FALSE:
+ format = "undefined method `%s' for FALSE";
+ break;
+ case T_OBJECT:
+ desc = obj_as_string(obj);
+ break;
+ default:
+ desc = rb_inspect(obj);
+ break;
+ }
+ if (desc) {
+ if (last_call_status & CSTAT_NOEX) {
+ format = "private method `%s' called for %s(%s)";
+ }
+ else if (iterator_p()) {
+ format = "undefined iterator `%s' for %s(%s)";
+ }
+ else if (last_call_status & CSTAT_VCALL) {
+ char *mname = rb_id2name(id);
+
+ if (('a' <= mname[0] && mname[0] <= 'z') || mname[0] == '_') {
+ format = "undefined local variable or method `%s' for %s(%s)";
+ }
+ }
+ if (!format) {
+ format = "undefined method `%s' for %s(%s)";
+ }
+ if (RSTRING(desc)->len > 65) {
+ desc = any_to_s(obj);
+ }
+ }
+
+ sourcefile = file;
+ sourceline = line;
+ PUSH_FRAME(); /* fake frame */
+ *the_frame = *_frame.prev->prev;
+
+ NameError(format,
+ rb_id2name(id),
+ desc?(char*)RSTRING(desc)->ptr:"",
+ desc?rb_class2name(CLASS_OF(obj)):"");
+ POP_FRAME();
+
+ return Qnil; /* not reached */
+}
+
+static VALUE
+rb_undefined(obj, id, argc, argv, call_status)
+ VALUE obj;
+ ID id;
+ int argc;
+ VALUE*argv;
+ int call_status;
+{
+ VALUE *nargv;
+
+ nargv = ALLOCA_N(VALUE, argc+1);
+ nargv[0] = INT2FIX(id);
+ MEMCPY(nargv+1, argv, VALUE, argc);
+
+ last_call_status = call_status;
+
+ return rb_funcall2(obj, rb_intern("method_missing"), argc+1, nargv);
+}
+
+#ifdef DJGPP
+# define STACK_LEVEL_MAX 65535
+#else
+#ifdef __human68k__
+extern int _stacksize;
+# define STACK_LEVEL_MAX (_stacksize - 4096)
+#else
+# define STACK_LEVEL_MAX 655350
+#endif
+#endif
+extern VALUE *gc_stack_start;
+static int
+stack_length()
+{
+ VALUE pos;
+
+#ifdef sparc
+ return gc_stack_start - &pos + 0x80;
+#else
+ return (&pos < gc_stack_start) ? gc_stack_start - &pos
+ : &pos - gc_stack_start;
+#endif
+}
+
+static VALUE
+rb_call(class, recv, mid, argc, argv, scope)
+ struct RClass *class;
+ VALUE recv;
+ ID mid;
+ int argc; /* OK */
+ VALUE *argv; /* OK */
+ int scope;
+{
+ NODE *body, *b2; /* OK */
+ int noex;
+ ID id = mid;
+ struct cache_entry *ent;
+ volatile VALUE result = Qnil;
+ int itr;
+ enum node_type type;
+ static int tick;
+
+ again:
+ /* is it in the method cache? */
+ ent = cache + EXPR1(class, mid);
+ if (ent->mid == mid && ent->class == class) {
+ class = ent->origin;
+ id = ent->mid0;
+ noex = ent->noex;
+ body = ent->method;
+ }
+ else if ((body = rb_get_method_body(&class, &id, &noex)) == 0) {
+ return rb_undefined(recv, mid, argc, argv, scope==2?CSTAT_VCALL:0);
+ }
+
+ /* receiver specified form for private method */
+ if (noex == NOEX_PRIVATE && scope == 0)
+ return rb_undefined(recv, mid, argc, argv, CSTAT_NOEX);
+
+ switch (the_iter->iter) {
+ case ITER_PRE:
+ itr = ITER_CUR;
+ break;
+ case ITER_CUR:
+ default:
+ itr = ITER_NOT;
+ break;
+ }
+
+ type = nd_type(body);
+ if (type == NODE_ZSUPER) {
+ /* for re-scoped/renamed method */
+ mid = id;
+ if (scope == 0) scope = 1;
+ if (class->super == 0) {
+ /* origin is the Module, so need to scan superclass hierarchy. */
+ struct RClass *cl = class;
+
+ class = (struct RClass*)RBASIC(recv)->class;
+ while (class) {
+ if (class->m_tbl == cl->m_tbl)
+ break;
+ class = class->super;
+ }
+ }
+ else {
+ class = class->super;
+ }
+ goto again;
+ }
+
+ if ((++tick & 0xfff) == 0 && stack_length() > STACK_LEVEL_MAX)
+ Fatal("stack level too deep");
+
+ PUSH_ITER(itr);
+ PUSH_FRAME();
+ the_frame->last_func = id;
+ the_frame->last_class = class;
+ the_frame->argc = argc;
+ the_frame->argv = argv;
+
+ switch (type) {
+ case NODE_CFUNC:
+ {
+ int len = body->nd_argc;
+
+ if (len >= 0 && argc != len) {
+ ArgError("Wrong # of arguments(%d for %d)", argc, len);
+ }
+
+ switch (len) {
+ case -2:
+ result = (*body->nd_cfnc)(recv, ary_new4(argc, argv));
+ break;
+ case -1:
+ result = (*body->nd_cfnc)(argc, argv, recv);
+ break;
+ case 0:
+ result = (*body->nd_cfnc)(recv);
+ break;
+ case 1:
+ result = (*body->nd_cfnc)(recv, argv[0]);
+ break;
+ case 2:
+ result = (*body->nd_cfnc)(recv, argv[0], argv[1]);
+ break;
+ case 3:
+ result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2]);
+ break;
+ case 4:
+ result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2],
+ argv[3]);
+ break;
+ case 5:
+ result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2],
+ argv[3], argv[4]);
+ break;
+ case 6:
+ result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2],
+ argv[3], argv[4], argv[5]);
+ break;
+ case 7:
+ result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2],
+ argv[3], argv[4], argv[5],
+ argv[6]);
+ break;
+ case 8:
+ result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2],
+ argv[3], argv[4], argv[5],
+ argv[6], argv[7]);
+ break;
+ case 9:
+ result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2],
+ argv[3], argv[4], argv[5],
+ argv[6], argv[7], argv[8]);
+ break;
+ case 10:
+ result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2],
+ argv[3], argv[4], argv[5],
+ argv[6], argv[7], argv[8],
+ argv[6], argv[7], argv[8],
+ argv[9]);
+ break;
+ case 11:
+ result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2],
+ argv[3], argv[4], argv[5],
+ argv[6], argv[7], argv[8],
+ argv[6], argv[7], argv[8],
+ argv[9], argv[10]);
+ break;
+ case 12:
+ result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2],
+ argv[3], argv[4], argv[5],
+ argv[6], argv[7], argv[8],
+ argv[6], argv[7], argv[8],
+ argv[9], argv[10], argv[11]);
+ break;
+ case 13:
+ result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2],
+ argv[3], argv[4], argv[5],
+ argv[6], argv[7], argv[8],
+ argv[6], argv[7], argv[8],
+ argv[9], argv[10], argv[11],
+ argv[12]);
+ break;
+ case 14:
+ result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2],
+ argv[3], argv[4], argv[5],
+ argv[6], argv[7], argv[8],
+ argv[6], argv[7], argv[8],
+ argv[9], argv[10], argv[11],
+ argv[12], argv[13]);
+ break;
+ case 15:
+ result = (*body->nd_cfnc)(recv, argv[0], argv[1], argv[2],
+ argv[3], argv[4], argv[5],
+ argv[6], argv[7], argv[8],
+ argv[6], argv[7], argv[8],
+ argv[9], argv[10], argv[11],
+ argv[12], argv[13], argv[14]);
+ break;
+ default:
+ if (len < 0) {
+ Bug("bad argc(%d) specified for `%s(%s)'",
+ len, rb_class2name((VALUE)class), rb_id2name(mid));
+ }
+ else {
+ ArgError("too many arguments(%d)", len);
+ }
+ break;
+ }
+ }
+ break;
+
+ /* for attr get/set */
+ case NODE_ATTRSET:
+ case NODE_IVAR:
+ result = rb_eval(recv, body);
+ break;
+
+ default:
+ {
+ NODE *state;
+ VALUE *local_vars;
+
+ PUSH_SCOPE();
+
+ if (body->nd_rval) the_frame->cbase = body->nd_rval;
+ if (body->nd_tbl) {
+ local_vars = ALLOCA_N(VALUE, body->nd_tbl[0]+1);
+ *local_vars++ = (VALUE)body;
+ memclear(local_vars, body->nd_tbl[0]);
+ the_scope->local_tbl = body->nd_tbl;
+ the_scope->local_vars = local_vars;
+ }
+ else {
+ local_vars = the_scope->local_vars = 0;
+ the_scope->local_tbl = 0;
+ }
+ b2 = body = body->nd_body;
+
+ PUSH_TAG();
+ PUSH_VARS();
+
+ if ((state = EXEC_TAG()) == 0) {
+ if (nd_type(body) == NODE_BLOCK) {
+ NODE *node = body->nd_head;
+ int i;
+
+ if (nd_type(node) != NODE_ARGS) {
+ Bug("no argument-node");
+ }
+
+ body = body->nd_next;
+ i = node->nd_cnt;
+ if (i > argc) {
+ ArgError("Wrong # of arguments(%d for %d)", argc, i);
+ }
+ if (node->nd_rest == -1) {
+ int opt = argc - i;
+ NODE *optnode = node->nd_opt;
+
+ while (optnode) {
+ opt--;
+ optnode = optnode->nd_next;
+ }
+ if (opt > 0) {
+ ArgError("Wrong # of arguments(%d for %d)",
+ argc, argc-opt);
+ }
+ }
+
+ if (local_vars) {
+ if (i > 0) {
+ MEMCPY(local_vars, argv, VALUE, i);
+ }
+ argv += i; argc -= i;
+ if (node->nd_opt) {
+ NODE *opt = node->nd_opt;
+
+ while (opt && argc) {
+ assign(recv, opt->nd_head, *argv);
+ argv++; argc--;
+ opt = opt->nd_next;
+ }
+ rb_eval(recv, opt);
+ }
+ if (node->nd_rest >= 0) {
+ if (argc > 0)
+ local_vars[node->nd_rest]=ary_new4(argc,argv);
+ else
+ local_vars[node->nd_rest]=ary_new2(0);
+ }
+ }
+ }
+ else if (nd_type(body) == NODE_ARGS) {
+ body = 0;
+ }
+ if (trace_func) {
+ call_trace_func("call", b2->file, nd_line(b2),
+ recv, the_frame->last_func);
+ }
+ result = rb_eval(recv, body);
+ }
+ POP_VARS();
+ POP_TAG();
+ POP_SCOPE();
+ if (trace_func) {
+ char *file = the_frame->prev->file;
+ int line = the_frame->prev->line;
+ if (!file) {
+ file = sourcefile;
+ line = sourceline;
+ }
+ call_trace_func("return", file, line, 0, the_frame->last_func);
+ }
+ if (state) {
+ switch (state->nd_tag) {
+ case TAG_NEXT:
+ Raise(eLocalJumpError, "unexpected next");
+ break;
+ case TAG_BREAK:
+ Raise(eLocalJumpError, "unexpected break");
+ break;
+ case TAG_REDO:
+ Raise(eLocalJumpError, "unexpected redo");
+ break;
+ case TAG_RETURN:
+ result = state->nd_tval;
+ break;
+ case TAG_RETRY:
+ if (!iterator_p()) {
+ Raise(eLocalJumpError, "retry outside of rescue clause");
+ }
+ default:
+ JUMP_TAG(state);
+ }
+ }
+ }
+ }
+ POP_FRAME();
+ POP_ITER();
+ return result;
+}
+
+VALUE
+rb_apply(recv, mid, args)
+ VALUE recv;
+ struct RArray *args;
+ ID mid;
+{
+ int argc;
+ VALUE *argv;
+
+ argc = RARRAY(args)->len;
+ argv = ALLOCA_N(VALUE, argc);
+ MEMCPY(argv, RARRAY(args)->ptr, VALUE, argc);
+ return rb_call(CLASS_OF(recv), recv, mid, argc, argv, 1);
+}
+
+static VALUE
+f_send(argc, argv, recv)
+ int argc;
+ VALUE *argv;
+ VALUE recv;
+{
+ VALUE vid;
+ ID mid;
+
+ if (argc == 0) ArgError("no method name given");
+
+ vid = argv[0]; argc--; argv++;
+ if (TYPE(vid) == T_STRING) {
+ mid = rb_intern(RSTRING(vid)->ptr);
+ }
+ else {
+ mid = NUM2INT(vid);
+ }
+ PUSH_ITER(iterator_p()?ITER_PRE:ITER_NOT);
+ vid = rb_call(CLASS_OF(recv), recv, mid, argc, argv, 1);
+ POP_ITER();
+
+ return vid;
+}
+
+#include <varargs.h>
+
+VALUE
+rb_funcall(recv, mid, n, va_alist)
+ VALUE recv;
+ ID mid;
+ int n;
+ va_dcl
+{
+ va_list ar;
+ VALUE *argv;
+
+ if (n > 0) {
+ int i;
+
+ argv = ALLOCA_N(VALUE, n);
+
+ va_start(ar);
+ for (i=0;i<n;i++) {
+ argv[i] = va_arg(ar, VALUE);
+ }
+ va_end(ar);
+ }
+ else {
+ argv = 0;
+ }
+
+ return rb_call(CLASS_OF(recv), recv, mid, n, argv, 1);
+}
+
+VALUE
+rb_funcall2(recv, mid, argc, argv)
+ VALUE recv;
+ ID mid;
+ int argc;
+ VALUE *argv;
+{
+ return rb_call(CLASS_OF(recv), recv, mid, argc, argv, 1);
+}
+
+static VALUE
+backtrace(lev)
+ int lev;
+{
+ struct FRAME *frame = the_frame;
+ char buf[BUFSIZ];
+ VALUE ary;
+ int slev = safe_level;
+
+ safe_level = 0;
+ ary = ary_new();
+ if (lev < 0) {
+ if (frame->last_func) {
+ sprintf(buf, "%s:%d:in `%s'", sourcefile, sourceline,
+ rb_id2name(frame->last_func));
+ }
+ else {
+ sprintf(buf, "%s:%d", sourcefile, sourceline);
+ }
+ ary_push(ary, str_new2(buf));
+ }
+ else {
+ while (lev-- > 0) {
+ frame = frame->prev;
+ if (!frame) return Qnil;
+ }
+ }
+ while (frame && frame->file) {
+ if (frame->prev && frame->prev->last_func) {
+ sprintf(buf, "%s:%d:in `%s'",
+ frame->file, frame->line,
+ rb_id2name(frame->prev->last_func));
+ }
+ else {
+ sprintf(buf, "%s:%d", frame->file, frame->line);
+ }
+ ary_push(ary, str_new2(buf));
+ frame = frame->prev;
+ }
+ safe_level = slev;
+ return ary;
+}
+
+static VALUE
+f_caller(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE level;
+ int lev;
+
+ rb_scan_args(argc, argv, "01", &level);
+
+ if (NIL_P(level)) lev = 1;
+ else lev = NUM2INT(level);
+ if (lev < 0) ArgError("negative level(%d)", lev);
+
+ return backtrace(lev);
+}
+
+void
+rb_backtrace()
+{
+ int i, lev;
+ VALUE ary;
+
+ lev = INT2FIX(0);
+ ary = backtrace(-1);
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ printf("\tfrom %s\n", RSTRING(RARRAY(ary)->ptr[i])->ptr);
+ }
+}
+
+static VALUE
+make_backtrace()
+{
+ VALUE lev;
+
+ lev = INT2FIX(0);
+ return backtrace(-1);
+}
+
+ID
+rb_frame_last_func()
+{
+ return the_frame->last_func;
+}
+
+static NODE*
+compile(src)
+ struct RString *src;
+{
+ NODE *node;
+
+ Check_Type(src, T_STRING);
+
+ node = compile_string(sourcefile, src->ptr, src->len);
+
+ if (nerrs == 0) return node;
+ return 0;
+}
+
+static VALUE
+eval(self, src, scope)
+ VALUE self;
+ struct RString *src;
+ struct RData *scope;
+{
+ struct BLOCK *data;
+ volatile VALUE result = Qnil;
+ NODE *state;
+ volatile VALUE old_block;
+ volatile VALUE old_scope;
+ volatile VALUE old_d_vars;
+ struct FRAME frame;
+ char *file = sourcefile;
+ int line = sourceline;
+ volatile int iter = the_frame->iter;
+
+ if (!NIL_P(scope)) {
+ if (TYPE(scope) != T_DATA || scope->dfree != blk_free) {
+ TypeError("wrong argument type %s (expected Proc/Binding)",
+ rb_class2name(CLASS_OF(scope)));
+ }
+
+ Data_Get_Struct(scope, struct BLOCK, data);
+
+ /* PUSH BLOCK from data */
+ frame = data->frame;
+ frame.prev = the_frame;
+ the_frame = &(frame);
+ old_scope = (VALUE)the_scope;
+ the_scope = data->scope;
+ old_block = (VALUE)the_block;
+ the_block = data->prev;
+ old_d_vars = (VALUE)the_dyna_vars;
+ the_dyna_vars = data->d_vars;
+
+ self = data->self;
+ the_frame->iter = data->iter;
+ }
+ else {
+ if (the_frame->prev) {
+ the_frame->iter = the_frame->prev->iter;
+ }
+ }
+ PUSH_CLASS();
+ the_class = (struct RClass*)((NODE*)the_frame->cbase)->nd_clss;
+
+ rb_in_eval++;
+ if (TYPE(the_class) == T_ICLASS) {
+ the_class = (struct RClass*)RBASIC(the_class)->class;
+ }
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ compile(src);
+ if (nerrs > 0) {
+ compile_error("eval()");
+ }
+ result = eval_node(self);
+ }
+ POP_TAG();
+ POP_CLASS();
+ rb_in_eval--;
+ if (!NIL_P(scope)) {
+ the_frame = the_frame->prev;
+ the_scope = (struct SCOPE*)old_scope;
+ the_block = (struct BLOCK*)old_block;
+ the_dyna_vars = (struct RVarmap*)old_d_vars;
+ }
+ else {
+ the_frame->iter = iter;
+ }
+ if (state) {
+ VALUE err ;
+
+ switch (state->nd_tag) {
+ case TAG_RAISE:
+ sourcefile = file;
+ sourceline = line;
+ if (strcmp(sourcefile, "(eval)") == 0) {
+ err = errinfo;
+ if (sourceline > 1) {
+ err = RARRAY(errat)->ptr[0];
+ str_cat(err, ": ", 2);
+ str_cat(err, RSTRING(errinfo)->ptr, RSTRING(errinfo)->len);
+ }
+ errat = Qnil;
+ rb_raise(exc_new3(CLASS_OF(errinfo), err));
+ }
+ rb_raise(Qnil);
+ }
+ JUMP_TAG(state);
+ }
+
+ return result;
+}
+
+static VALUE
+f_eval(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE src, scope;
+
+ rb_scan_args(argc, argv, "11", &src, &scope);
+
+ Check_SafeStr(src);
+ return eval(self, src, scope);
+}
+
+VALUE rb_load_path;
+
+char *dln_find_file();
+
+static char*
+find_file(file)
+ char *file;
+{
+ extern VALUE rb_load_path;
+ VALUE vpath;
+ char *path;
+
+ if (file[0] == '/') return file;
+#if defined(MSDOS) || defined(NT) || defined(__human68k__)
+ if (file[0] == '\\') return file;
+ if (file[1] == ':') return file;
+#endif
+
+ if (rb_load_path) {
+ int i;
+
+ Check_Type(rb_load_path, T_ARRAY);
+ for (i=0;i<RARRAY(rb_load_path)->len;i++) {
+ Check_SafeStr(RARRAY(rb_load_path)->ptr[i]);
+ }
+#if !defined(MSDOS) && !defined(NT) && !defined(__human68k__)
+ vpath = ary_join(rb_load_path, str_new2(":"));
+#else
+ vpath = ary_join(rb_load_path, str_new2(";"));
+#endif
+ Check_SafeStr(vpath);
+ path = RSTRING(vpath)->ptr;
+ }
+ else {
+ path = 0;
+ }
+
+ return dln_find_file(file, path);
+}
+
+VALUE
+f_load(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ NODE *state;
+ char *file;
+ volatile ID last_func;
+
+ Check_SafeStr(fname);
+ if (fname->ptr[0] == '~') {
+ fname = (struct RString*)file_s_expand_path(0, fname);
+ }
+ file = find_file(fname->ptr);
+ if (!file) LoadError("No such file to load -- %s", fname->ptr);
+
+ PUSH_TAG();
+ PUSH_CLASS();
+ the_class = (struct RClass*)cObject;
+ PUSH_SCOPE();
+ if (top_scope->local_tbl) {
+ int len = top_scope->local_tbl[0]+1;
+ ID *tbl = ALLOC_N(ID, len);
+ VALUE *vars = ALLOCA_N(VALUE, len);
+ *vars++ = 0;
+ MEMCPY(tbl, top_scope->local_tbl, ID, len);
+ MEMCPY(vars, top_scope->local_vars, ID, len-1);
+ the_scope->local_tbl = tbl;
+ the_scope->local_vars = vars;
+ }
+
+ state = EXEC_TAG();
+ last_func = the_frame->last_func;
+ the_frame->last_func = 0;
+ if (state == 0) {
+ rb_in_eval++;
+ rb_load_file(file);
+ rb_in_eval--;
+ if (nerrs == 0) {
+ eval_node(TopSelf);
+ }
+ }
+ the_frame->last_func = last_func;
+ if (the_scope->flag == SCOPE_ALLOCA && the_scope->local_tbl) {
+ free(the_scope->local_tbl);
+ }
+ POP_SCOPE();
+ POP_CLASS();
+ POP_TAG();
+ if (nerrs > 0) {
+ rb_raise(errinfo);
+ }
+ if (state) JUMP_TAG(state);
+
+ return TRUE;
+}
+
+static VALUE rb_features;
+
+static int
+rb_provided(feature)
+ char *feature;
+{
+ struct RArray *features = RARRAY(rb_features);
+ VALUE *p, *pend;
+ char *f;
+ int len;
+
+ p = features->ptr;
+ pend = p + features->len;
+ while (p < pend) {
+ Check_Type(*p, T_STRING);
+ f = RSTRING(*p)->ptr;
+ if (strcmp(f, feature) == 0) return TRUE;
+ len = strlen(feature);
+ if (strncmp(f, feature, len) == 0
+ && (strcmp(f+len, ".rb") == 0 ||strcmp(f+len, ".o") == 0)) {
+ return TRUE;
+ }
+ p++;
+ }
+ return FALSE;
+}
+
+#ifdef THREAD
+static int thread_loading();
+static void thread_loading_done();
+#endif
+
+void
+rb_provide(feature)
+ char *feature;
+{
+ char *buf, *ext;
+
+ if (!rb_provided(feature)) {
+ ext = strrchr(feature, '.');
+ if (strcmp(DLEXT, ext) == 0) {
+ buf = ALLOCA_N(char, strlen(feature)+1);
+ strcpy(buf, feature);
+ ext = strrchr(buf, '.');
+ strcpy(ext, ".o");
+ feature = buf;
+ }
+ ary_push(rb_features, str_new2(feature));
+ }
+}
+
+VALUE
+f_require(obj, fname)
+ VALUE obj;
+ struct RString *fname;
+{
+ char *ext, *file, *feature, *buf;
+ volatile VALUE load;
+
+ Check_SafeStr(fname);
+ if (rb_provided(fname->ptr))
+ return FALSE;
+
+ ext = strrchr(fname->ptr, '.');
+ if (ext) {
+ if (strcmp(".rb", ext) == 0) {
+ feature = file = fname->ptr;
+ file = find_file(file);
+ if (file) goto rb_load;
+ }
+ else if (strcmp(".o", ext) == 0) {
+ file = feature = fname->ptr;
+ if (strcmp(".o", DLEXT) != 0) {
+ buf = ALLOCA_N(char, strlen(fname->ptr)+sizeof(DLEXT)+1);
+ strcpy(buf, feature);
+ ext = strrchr(buf, '.');
+ strcpy(ext, DLEXT);
+ file = find_file(buf);
+ }
+ if (file) goto dyna_load;
+ }
+ else if (strcmp(DLEXT, ext) == 0) {
+ feature = fname->ptr;
+ file = find_file(feature);
+ if (file) goto dyna_load;
+ }
+ }
+ buf = ALLOCA_N(char, strlen(fname->ptr) + 5);
+ sprintf(buf, "%s.rb", fname->ptr);
+ file = find_file(buf);
+ if (file) {
+ fname = (struct RString*)str_new2(file);
+ feature = buf;
+ goto rb_load;
+ }
+ sprintf(buf, "%s%s", fname->ptr, DLEXT);
+ file = find_file(buf);
+ if (file) {
+ feature = buf;
+ goto dyna_load;
+ }
+ LoadError("No such file to load -- %s", fname->ptr);
+
+ dyna_load:
+#ifdef THREAD
+ if (thread_loading(feature)) return FALSE;
+ else {
+ NODE *state;
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+#endif
+ load = str_new2(file);
+ file = RSTRING(load)->ptr;
+ dln_load(file);
+ rb_provide(feature);
+#ifdef THREAD
+ }
+ POP_TAG();
+ thread_loading_done();
+ if (state) JUMP_TAG(state);
+ }
+#endif
+ return TRUE;
+
+ rb_load:
+#ifdef THREAD
+ if (thread_loading(feature)) return FALSE;
+ else {
+ NODE *state;
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+#endif
+ f_load(obj, fname);
+ rb_provide(feature);
+#ifdef THREAD
+ }
+ POP_TAG();
+ thread_loading_done();
+ if (state) JUMP_TAG(state);
+ }
+#endif
+ return TRUE;
+}
+
+static void
+set_method_visibility(self, argc, argv, ex)
+ VALUE self;
+ int argc;
+ VALUE *argv;
+ int ex;
+{
+ int i;
+
+ for (i=0; i<argc; i++) {
+ rb_export_method(self, rb_to_id(argv[i]), ex);
+ }
+}
+
+static VALUE
+mod_public(argc, argv, module)
+ int argc;
+ VALUE *argv;
+ VALUE module;
+{
+ set_method_visibility(module, argc, argv, NOEX_PUBLIC);
+ return module;
+}
+
+static VALUE
+mod_private(argc, argv, module)
+ int argc;
+ VALUE *argv;
+ VALUE module;
+{
+ set_method_visibility(module, argc, argv, NOEX_PRIVATE);
+ return module;
+}
+
+static VALUE
+mod_public_method(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ set_method_visibility(CLASS_OF(obj), argc, argv, NOEX_PUBLIC);
+ return obj;
+}
+
+static VALUE
+mod_private_method(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ set_method_visibility(CLASS_OF(obj), argc, argv, NOEX_PRIVATE);
+ return obj;
+}
+
+static VALUE
+mod_modfunc(argc, argv, module)
+ int argc;
+ VALUE *argv;
+ VALUE module;
+{
+ int i;
+ ID id;
+ NODE *body;
+
+ rb_clear_cache();
+ set_method_visibility(module, argc, argv, NOEX_PRIVATE);
+ for (i=0; i<argc; i++) {
+ id = rb_to_id(argv[i]);
+ body = search_method(module, id, 0);
+ if (body == 0 || body->nd_body == 0) {
+ NameError("undefined method `%s' for module `%s'",
+ rb_id2name(id), rb_class2name(module));
+ }
+ rb_add_method(rb_singleton_class(module), id, body->nd_body, NOEX_PUBLIC);
+ }
+ return module;
+}
+
+static VALUE
+mod_include(argc, argv, module)
+ int argc;
+ VALUE *argv;
+ VALUE module;
+{
+ int i;
+
+ for (i=0; i<argc; i++) {
+ Check_Type(argv[i], T_MODULE);
+ rb_include_module(module, argv[i]);
+ }
+ return module;
+}
+
+VALUE
+class_s_new(argc, argv, class)
+ int argc;
+ VALUE *argv;
+ VALUE class;
+{
+ VALUE obj = obj_alloc(class);
+
+ if (FL_TEST(class, FL_SINGLETON)) {
+ TypeError("can't create instance of virtual class");
+ }
+ obj = obj_alloc(class);
+ PUSH_ITER(iterator_p()?ITER_PRE:ITER_NOT);
+ rb_funcall2(obj, init, argc, argv);
+ POP_ITER();
+ return obj;
+}
+
+
+VALUE
+class_new_instance(argc, argv, class)
+ int argc;
+ VALUE *argv;
+ VALUE class;
+{
+ VALUE obj;
+
+ if (FL_TEST(class, FL_SINGLETON)) {
+ TypeError("can't create instance of virtual class");
+ }
+ obj = obj_alloc(class);
+ PUSH_ITER(iterator_p()?ITER_PRE:ITER_NOT);
+ rb_funcall2(obj, init, argc, argv);
+ POP_ITER();
+ return obj;
+}
+
+static VALUE
+top_include(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ rb_secure(4);
+ return mod_include(argc, argv, cObject);
+}
+
+void
+rb_extend_object(obj, module)
+ VALUE obj, module;
+{
+ rb_include_module(rb_singleton_class(obj), module);
+}
+
+static VALUE
+mod_extend_object(mod, obj)
+ VALUE mod, obj;
+{
+ rb_extend_object(obj, mod);
+ return obj;
+}
+
+static VALUE
+obj_extend(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ int i;
+
+ for (i=0; i<argc; i++) Check_Type(argv[i], T_MODULE);
+ for (i=0; i<argc; i++) {
+ rb_funcall(argv[i], rb_intern("extend_object"), 1, obj);
+ }
+ return obj;
+}
+
+VALUE f_trace_var();
+VALUE f_untrace_var();
+
+extern void rb_str_setter();
+
+static void
+errat_setter(val, id, var)
+ VALUE val;
+ ID id;
+ VALUE *var;
+{
+ int i;
+ static char *err = "value of $@ must be Array of String";
+
+ if (!NIL_P(val)) {
+ if (TYPE(val) != T_ARRAY) {
+ TypeError(err);
+ }
+ for (i=0;i<RARRAY(val)->len;i++) {
+ if (TYPE(RARRAY(val)->ptr[i]) != T_STRING) {
+ TypeError(err);
+ }
+ }
+ }
+ *var = val;
+}
+
+static VALUE
+f_catch(dmy, tag)
+ VALUE dmy, tag;
+{
+ NODE *state;
+ ID t;
+ VALUE val;
+
+ t = rb_to_id(tag);
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ val = rb_yield(tag);
+ }
+ POP_TAG();
+ if (state) {
+ if (state->nd_tag == TAG_THROW && state->nd_tlev == t) {
+ return state->nd_tval;
+ }
+ JUMP_TAG(state);
+ }
+ return val;
+}
+
+static VALUE
+f_throw(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE tag, value;
+ ID t;
+
+ rb_scan_args(argc, argv, "11", &tag, &value);
+ t = rb_to_id(tag);
+ JUMP_TAG3(TAG_THROW, value, t);
+ /* not reached */
+}
+
+void
+Init_eval()
+{
+ init = rb_intern("initialize");
+ eqq = rb_intern("===");
+ each = rb_intern("each");
+
+ aref = rb_intern("[]");
+ aset = rb_intern("[]=");
+
+ rb_global_variable(&top_scope);
+ rb_global_variable(&eval_tree);
+ rb_global_variable(&the_dyna_vars);
+
+ rb_define_hooked_variable("$@", &errat, 0, errat_setter);
+ rb_define_hooked_variable("$!", &errinfo, 0, rb_str_setter);
+
+ rb_define_global_function("eval", f_eval, -1);
+ rb_define_global_function("iterator?", f_iterator_p, 0);
+ rb_define_global_function("method_missing", f_missing, -1);
+ rb_define_global_function("loop", f_loop, 0);
+
+ rb_define_method(mKernel, "respond_to?", obj_respond_to, -1);
+
+ rb_define_global_function("break", f_break, 0);
+ rb_define_alias(mKernel, "break!", "break");
+ rb_define_global_function("next", f_next, 0);
+ rb_define_alias(mKernel, "next!", "next");
+ rb_define_alias(mKernel, "continue", "next");
+ rb_define_global_function("redo", f_redo, 0);
+ rb_define_alias(mKernel, "redo!", "redo");
+ rb_define_global_function("retry", f_retry, 0);
+ rb_define_alias(mKernel, "retry!", "retry");
+ rb_define_global_function("raise", f_raise, -1);
+ rb_define_alias(mKernel, "fail", "raise");
+
+ rb_define_global_function("caller", f_caller, -1);
+
+ rb_define_global_function("exit", f_exit, -1);
+ rb_define_global_function("abort", f_abort, 0);
+
+ rb_define_global_function("catch", f_catch, 1);
+ rb_define_global_function("throw", f_throw, -1);
+
+ rb_define_method(mKernel, "send", f_send, -1);
+
+ rb_define_private_method(cModule, "include", mod_include, -1);
+ rb_define_private_method(cModule, "public", mod_public, -1);
+ rb_define_private_method(cModule, "private", mod_private, -1);
+ rb_define_private_method(cModule, "module_function", mod_modfunc, -1);
+ rb_define_method(cModule, "method_defined?", mod_method_defined, 1);
+ rb_define_method(cModule, "extend_object", mod_extend_object, 1);
+ rb_define_method(cModule, "public_class_method", mod_public_method, -1);
+ rb_define_method(cModule, "private_class_method", mod_private_method, -1);
+
+ rb_define_method(CLASS_OF(TopSelf), "include", top_include, -1);
+ rb_define_method(mKernel, "extend", obj_extend, -1);
+
+ rb_define_global_function("trace_var", f_trace_var, -1);
+ rb_define_global_function("untrace_var", f_untrace_var, -1);
+
+ rb_define_global_function("set_trace_func", set_trace_func, 1);
+
+ rb_define_virtual_variable("$SAFE", safe_getter, safe_setter);
+}
+
+VALUE f_autoload();
+
+void
+Init_load()
+{
+ rb_load_path = ary_new();
+ rb_define_readonly_variable("$:", &rb_load_path);
+ rb_define_readonly_variable("$-I", &rb_load_path);
+ rb_define_readonly_variable("$LOAD_PATH", &rb_load_path);
+
+ rb_features = ary_new();
+ rb_define_readonly_variable("$\"", &rb_features);
+
+ rb_define_global_function("load", f_load, 1);
+ rb_define_global_function("require", f_require, 1);
+ rb_define_global_function("autoload", f_autoload, 2);
+}
+
+static void
+scope_dup(scope)
+ struct SCOPE *scope;
+{
+ ID *tbl;
+ VALUE *vars;
+
+ if (scope->flag & SCOPE_MALLOC) return;
+
+ if (scope->local_tbl) {
+ tbl = scope->local_tbl;
+ vars = ALLOC_N(VALUE, tbl[0]+1);
+ *vars++ = scope->local_vars[-1];
+ MEMCPY(vars, scope->local_vars, VALUE, tbl[0]);
+ scope->local_vars = vars;
+ scope->flag = SCOPE_MALLOC;
+ }
+ else {
+ scope->flag = SCOPE_NOSTACK;
+ }
+}
+
+static void
+blk_mark(data)
+ struct BLOCK *data;
+{
+ gc_mark_frame(&data->frame);
+ gc_mark(data->scope);
+ gc_mark(data->var);
+ gc_mark(data->body);
+ gc_mark(data->self);
+ gc_mark(data->d_vars);
+}
+
+static void
+blk_free(data)
+ struct BLOCK *data;
+{
+ free(data->frame.argv);
+}
+
+static VALUE
+f_binding(self)
+ VALUE self;
+{
+ struct BLOCK *data;
+ VALUE bind;
+
+ PUSH_BLOCK(0,0);
+ bind = Data_Make_Struct(cData, struct BLOCK, blk_mark, blk_free, data);
+ MEMCPY(data, the_block, struct BLOCK, 1);
+
+ data->iter = f_iterator_p();
+ data->frame.last_func = 0;
+ data->frame.argv = ALLOC_N(VALUE, data->frame.argc);
+ MEMCPY(data->frame.argv, the_block->frame.argv, VALUE, data->frame.argc);
+
+ scope_dup(data->scope);
+ POP_BLOCK();
+
+ return bind;
+}
+
+#define PROC_TAINT FL_USER0
+#define PROC_T3 FL_USER1
+#define PROC_T4 FL_USER2
+#define PROC_T5 (FL_USER1|FL_USER2)
+#define PROC_TMASK (FL_USER1|FL_USER2)
+
+static VALUE
+proc_s_new(class)
+ VALUE class;
+{
+ VALUE proc;
+ struct BLOCK *data;
+
+ if (!iterator_p() && !f_iterator_p()) {
+ ArgError("tryed to create Procedure-Object out of iterator");
+ }
+
+ proc = Data_Make_Struct(class, struct BLOCK, blk_mark, blk_free, data);
+ *data = *the_block;
+
+#ifdef THREAD
+ data->orig_thread = thread_current();
+#endif
+ data->iter = f_iterator_p();
+ data->frame.argv = ALLOC_N(VALUE, data->frame.argc);
+ MEMCPY(data->frame.argv, the_block->frame.argv, VALUE, data->frame.argc);
+
+ scope_dup(data->scope);
+ if (safe_level >= 3) {
+ FL_SET(proc, PROC_TAINT);
+ switch (safe_level) {
+ case 3:
+ FL_SET(proc, PROC_T3);
+ break;
+ case 4:
+ FL_SET(proc, PROC_T4);
+ break;
+ case 5:
+ FL_SET(proc, PROC_T5);
+ break;
+ }
+ }
+
+ return proc;
+}
+
+VALUE
+f_lambda()
+{
+ return proc_s_new(cProc);
+}
+
+static VALUE
+proc_call(proc, args)
+ VALUE proc, args;
+{
+ struct BLOCK *data;
+ volatile VALUE result = Qnil;
+ NODE *state;
+ int tag_level;
+ volatile int orphan;
+ volatile int safe = safe_level;
+
+ if (TYPE(args) == T_ARRAY) {
+ switch (RARRAY(args)->len) {
+ case 0:
+ args = 0;
+ break;
+ case 1:
+ args = RARRAY(args)->ptr[0];
+ break;
+ }
+ }
+
+ Data_Get_Struct(proc, struct BLOCK, data);
+
+ if (data->scope && (data->scope->flag & SCOPE_NOSTACK)) {
+ orphan = 1;
+ }
+ else {
+#ifdef THREAD
+ if (data->orig_thread != thread_current()) {
+ orphan = 1;
+ }
+ else
+#endif
+ orphan = 0;
+ }
+ if (orphan) {/* orphan procedure */
+ if (iterator_p()) {
+ data->frame.iter = ITER_CUR;
+ }
+ else {
+ data->frame.iter = ITER_NOT;
+ }
+ }
+
+ /* PUSH BLOCK from data */
+ PUSH_BLOCK2(data);
+ PUSH_ITER(ITER_CUR);
+ the_frame->iter = ITER_CUR;
+ if (FL_TEST(proc, PROC_TAINT)) {
+ switch (RBASIC(proc)->flags & PROC_TMASK) {
+ case PROC_T3:
+ safe_level = 3;
+ break;
+ case PROC_T4:
+ safe_level = 4;
+ break;
+ case PROC_T5:
+ safe_level = 5;
+ break;
+ }
+ }
+
+ PUSH_TAG();
+ state = EXEC_TAG();
+ if (state == 0) {
+ result = rb_yield(args);
+ }
+ POP_TAG();
+
+ POP_ITER();
+ tag_level = the_block->level;
+ POP_BLOCK();
+ safe_level = safe;
+
+ if (state) {
+ if (orphan) {/* orphan procedure */
+ switch (state->nd_tag) {
+ case TAG_BREAK: /* never happen */
+ case IN_BLOCK|TAG_BREAK:
+ if (state->nd_tlev == tag_level)
+ Raise(eLocalJumpError, "break from proc-closure");
+ break;
+ case TAG_RETRY:
+ Raise(eLocalJumpError, "retry from proc-closure");
+ break;
+ case TAG_RETURN: /* never happen */
+ case IN_BLOCK|TAG_RETURN:
+ if (state->nd_tlev == tag_level)
+ Raise(eLocalJumpError, "return from proc-closure");
+ break;
+ }
+ }
+ else if (state->nd_tlev == tag_level) {
+ state->nd_tag &= ~IN_BLOCK;
+ }
+ JUMP_TAG(state);
+ }
+ return result;
+}
+
+void
+Init_Proc()
+{
+ eLocalJumpError = rb_define_class("LocalJumpError", eException);
+
+ cProc = rb_define_class("Proc", cObject);
+ rb_define_singleton_method(cProc, "new", proc_s_new, 0);
+
+ rb_define_method(cProc, "call", proc_call, -2);
+ rb_define_global_function("proc", f_lambda, 0);
+ rb_define_global_function("lambda", f_lambda, 0);
+ rb_define_global_function("binding", f_binding, 0);
+}
+
+#ifdef THREAD
+
+static VALUE eThreadError;
+
+int thread_pending = 0;
+
+static VALUE cThread;
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#else
+#ifndef NT
+struct timeval {
+ long tv_sec; /* seconds */
+ long tv_usec; /* and microseconds */
+};
+#endif /* NT */
+#endif
+#include <signal.h>
+#include <errno.h>
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+extern VALUE last_status;
+
+enum thread_status {
+ THREAD_RUNNABLE,
+ THREAD_STOPPED,
+ THREAD_TO_KILL,
+ THREAD_KILLED,
+};
+
+#define WAIT_FD (1<<0)
+#define WAIT_TIME (1<<1)
+#define WAIT_JOIN (1<<2)
+
+/* +infty, for this purpose */
+#define DELAY_INFTY 1E30
+
+typedef struct thread * thread_t;
+
+struct thread {
+ struct thread *next, *prev;
+ jmp_buf context;
+
+ VALUE result;
+
+ int stk_len;
+ int stk_max;
+ VALUE*stk_ptr;
+ VALUE*stk_pos;
+
+ struct FRAME *frame;
+ struct SCOPE *scope;
+ struct RClass *class;
+ struct RVarmap *dyna_vars;
+ struct BLOCK *block;
+ struct iter *iter;
+ struct tag *tag;
+
+ VALUE trace;
+
+ char *file;
+ int line;
+
+ VALUE errat, errinfo;
+ VALUE last_status;
+ VALUE last_line;
+ VALUE last_match;
+
+ int safe;
+
+ enum thread_status status;
+ int wait_for;
+ int fd;
+ double delay;
+ thread_t join;
+
+ int abort;
+
+ VALUE thread;
+};
+
+static thread_t curr_thread;
+static int num_waiting_on_fd;
+static int num_waiting_on_timer;
+static int num_waiting_on_join;
+
+#define FOREACH_THREAD_FROM(f,x) x = f; do { x = x->next;
+#define END_FOREACH_FROM(f,x) } while (x != f)
+
+#define FOREACH_THREAD(x) FOREACH_THREAD_FROM(curr_thread,x)
+#define END_FOREACH(x) END_FOREACH_FROM(curr_thread,x)
+
+/* Return the current time as a floating-point number */
+static double
+timeofday()
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (double)tv.tv_sec + (double)tv.tv_usec * 1e-6;
+}
+
+static thread_t main_thread;
+
+#define ADJ(addr) (void*)(((VALUE*)(addr)-th->stk_pos)+th->stk_ptr)
+#define STACK(addr) (th->stk_pos<(addr) && (addr)<th->stk_pos+th->stk_len)
+
+static void
+thread_mark(th)
+ thread_t th;
+{
+ struct FRAME *frame;
+ struct BLOCK *block;
+
+ gc_mark(th->result);
+ if (th->stk_ptr) {
+ gc_mark_locations(th->stk_ptr, th->stk_ptr+th->stk_len);
+#if defined(THINK_C) || defined(__human68k__)
+ gc_mark_locations(th->stk_ptr+2, th->stk_ptr+th->stk_len+2);
+#endif
+ }
+ gc_mark(th->thread);
+ if (th->join) gc_mark(th->join->thread);
+
+ gc_mark(th->scope);
+ gc_mark(th->dyna_vars);
+ gc_mark(th->errat);
+ gc_mark(th->errinfo);
+ gc_mark(th->last_line);
+ gc_mark(th->last_match);
+
+ /* mark data in copied stack */
+ frame = th->frame;
+ while (frame && frame != top_frame) {
+ frame = ADJ(frame);
+ if (frame->argv && !STACK(frame->argv)) {
+ gc_mark_frame(frame);
+ }
+ frame = frame->prev;
+ }
+ block = th->block;
+ while (block) {
+ block = ADJ(block);
+ if (block->frame.argv && !STACK(block->frame.argv)) {
+ gc_mark_frame(&block->frame);
+ }
+ block = block->prev;
+ }
+}
+
+void
+gc_mark_threads()
+{
+ thread_t th;
+
+ FOREACH_THREAD(th) {
+ thread_mark(th);
+ } END_FOREACH(th);
+}
+
+static void
+thread_free(th)
+ thread_t th;
+{
+ if (th->stk_ptr) free(th->stk_ptr);
+ th->stk_ptr = 0;
+}
+
+static thread_t
+thread_check(data)
+ struct RData *data;
+{
+ if (TYPE(data) != T_DATA || data->dfree != thread_free) {
+ TypeError("wrong argument type %s (expected Thread)",
+ rb_class2name(CLASS_OF(data)));
+ }
+ return (thread_t)data->data;
+}
+
+VALUE lastline_get();
+void lastline_set();
+VALUE backref_get();
+void backref_set();
+
+static void
+thread_save_context(th)
+ thread_t th;
+{
+ VALUE v;
+
+ th->stk_len = stack_length();
+ th->stk_pos = (gc_stack_start<(VALUE*)&v)?gc_stack_start
+ :gc_stack_start - th->stk_len;
+ if (th->stk_len > th->stk_max) {
+ th->stk_max = th->stk_len;
+ REALLOC_N(th->stk_ptr, VALUE, th->stk_max);
+ }
+ FLUSH_REGISTER_WINDOWS;
+ MEMCPY(th->stk_ptr, th->stk_pos, VALUE, th->stk_len);
+
+ th->frame = the_frame;
+ th->scope = the_scope;
+ th->class = the_class;
+ th->dyna_vars = the_dyna_vars;
+ th->block = the_block;
+ th->iter = the_iter;
+ th->tag = prot_tag;
+ th->errat = errat;
+ th->errinfo = errinfo;
+ th->last_status = last_status;
+ th->last_line = lastline_get();
+ th->last_match = backref_get();
+ th->safe = safe_level;
+
+ th->trace = trace_func;
+ th->file = sourcefile;
+ th->line = sourceline;
+}
+
+static void thread_restore_context();
+
+static void
+stack_extend(th, exit)
+ thread_t th;
+ int exit;
+{
+ VALUE space[1024];
+
+ memset(space, 0, 1); /* prevent array from optimization */
+ thread_restore_context(th, exit);
+}
+
+static int th_raise_argc;
+static VALUE th_raise_argv[2];
+static char *th_raise_file;
+static int th_raise_line;
+
+static void
+thread_restore_context(th, exit)
+ thread_t th;
+ int exit;
+{
+ VALUE v;
+ static thread_t tmp;
+ static int ex;
+
+ if (!th->stk_ptr) Bug("unsaved context");
+
+ if (&v < gc_stack_start) {
+ /* Stack grows downward */
+ if (&v > th->stk_pos) stack_extend(th, exit);
+ }
+ else {
+ /* Stack grows upward */
+ if (&v < th->stk_pos + th->stk_len) stack_extend(th, exit);
+ }
+
+ the_frame = th->frame;
+ the_scope = th->scope;
+ the_class = th->class;
+ the_dyna_vars = th->dyna_vars;
+ the_block = th->block;
+ the_iter = th->iter;
+ prot_tag = th->tag;
+ the_class = th->class;
+ errat = th->errat;
+ errinfo = th->errinfo;
+ last_status = th->last_status;
+ safe_level = th->safe;
+
+ trace_func = th->trace;
+ sourcefile = th->file;
+ sourceline = th->line;
+
+ tmp = th;
+ ex = exit;
+ FLUSH_REGISTER_WINDOWS;
+ MEMCPY(tmp->stk_pos, tmp->stk_ptr, VALUE, tmp->stk_len);
+
+ lastline_set(tmp->last_line);
+ backref_set(tmp->last_match);
+
+ switch (ex) {
+ case 1:
+ JUMP_TAG2(TAG_FATAL, INT2FIX(0));
+ break;
+
+ case 2:
+ rb_interrupt();
+ break;
+
+ case 3:
+ the_frame->last_func = 0;
+ sourcefile = th_raise_file;
+ sourceline = th_raise_line;
+ f_raise(th_raise_argc, th_raise_argv);
+ break;
+
+ default:
+ longjmp(tmp->context, 1);
+ }
+}
+
+static void
+thread_ready(th)
+ thread_t th;
+{
+ /* The thread is no longer waiting on anything */
+ if (th->wait_for & WAIT_FD) {
+ num_waiting_on_fd--;
+ }
+ if (th->wait_for & WAIT_TIME) {
+ num_waiting_on_timer--;
+ }
+ if (th->wait_for & WAIT_JOIN) {
+ num_waiting_on_join--;
+ }
+ th->wait_for = 0;
+ th->status = THREAD_RUNNABLE;
+}
+
+static void
+thread_remove()
+{
+ thread_ready(curr_thread);
+ curr_thread->status = THREAD_KILLED;
+ curr_thread->prev->next = curr_thread->next;
+ curr_thread->next->prev = curr_thread->prev;
+ thread_schedule();
+}
+
+static int
+thread_dead(th)
+ thread_t th;
+{
+ return th->status == THREAD_KILLED;
+}
+
+static void
+thread_deadlock()
+{
+ curr_thread = main_thread;
+ th_raise_argc = 1;
+ th_raise_argv[0] = exc_new2(eFatal, "Thread: deadlock");
+ th_raise_file = sourcefile;
+ th_raise_line = sourceline;
+ f_abort();
+}
+
+void
+thread_schedule()
+{
+ thread_t next;
+ thread_t th;
+ thread_t curr;
+
+ select_err:
+ thread_pending = 0;
+ if (curr_thread == curr_thread->next) return;
+
+ next = 0;
+ curr = curr_thread; /* starting thread */
+
+ while (curr->status == THREAD_KILLED) {
+ curr = curr->prev;
+ }
+
+ FOREACH_THREAD_FROM(curr,th) {
+ if (th->status != THREAD_STOPPED && th->status != THREAD_KILLED) {
+ next = th;
+ break;
+ }
+ }
+ END_FOREACH_FROM(curr,th);
+
+ if (num_waiting_on_join) {
+ curr_thread->file = sourcefile;
+ curr_thread->line = sourceline;
+ FOREACH_THREAD_FROM(curr,th) {
+ if ((th->wait_for & WAIT_JOIN) && thread_dead(th->join)) {
+ th->join = 0;
+ th->wait_for &= ~WAIT_JOIN;
+ th->status = THREAD_RUNNABLE;
+ num_waiting_on_join--;
+ if (!next) next = th;
+ }
+ }
+ END_FOREACH_FROM(curr,th);
+ }
+
+ if (num_waiting_on_fd > 0 || num_waiting_on_timer > 0) {
+ fd_set readfds;
+ struct timeval delay_tv, *delay_ptr;
+ double delay, now;
+
+ int n, max;
+
+ do {
+ max = 0;
+ FD_ZERO(&readfds);
+ if (num_waiting_on_fd > 0) {
+ FOREACH_THREAD_FROM(curr,th) {
+ if (th->wait_for & WAIT_FD) {
+ FD_SET(th->fd, &readfds);
+ if (th->fd > max) max = th->fd;
+ }
+ }
+ END_FOREACH_FROM(curr,th);
+ }
+
+ delay = DELAY_INFTY;
+ if (num_waiting_on_timer > 0) {
+ now = timeofday();
+ FOREACH_THREAD_FROM(curr,th) {
+ if (th->wait_for & WAIT_TIME) {
+ if (th->delay <= now) {
+ th->delay = 0.0;
+ th->wait_for &= ~WAIT_TIME;
+ th->status = THREAD_RUNNABLE;
+ num_waiting_on_timer--;
+ next = th;
+ } else if (th->delay < delay) {
+ delay = th->delay;
+ }
+ }
+ }
+ END_FOREACH_FROM(curr,th);
+ }
+ /* Do the select if needed */
+ if (num_waiting_on_fd > 0 || !next) {
+ /* Convert delay to a timeval */
+ /* If a thread is runnable, just poll */
+ if (next) {
+ delay_tv.tv_sec = 0;
+ delay_tv.tv_usec = 0;
+ delay_ptr = &delay_tv;
+ }
+ else if (delay == DELAY_INFTY) {
+ delay_ptr = 0;
+ }
+ else {
+ delay -= now;
+ delay_tv.tv_sec = (unsigned int)delay;
+ delay_tv.tv_usec = (delay - (double)delay_tv.tv_sec) * 1e6;
+ delay_ptr = &delay_tv;
+ }
+
+ n = select(max+1, &readfds, 0, 0, delay_ptr);
+ if (n < 0) {
+ if (trap_pending) rb_trap_exec();
+ goto select_err;
+ }
+ if (n > 0) {
+ /* Some descriptors are ready.
+ Make the corresponding threads runnable. */
+ FOREACH_THREAD_FROM(curr,th) {
+ if ((th->wait_for&WAIT_FD)
+ && FD_ISSET(th->fd, &readfds)) {
+ /* Wake up only one thread per fd. */
+ FD_CLR(th->fd, &readfds);
+ th->status = THREAD_RUNNABLE;
+ th->fd = 0;
+ th->wait_for &= ~WAIT_FD;
+ num_waiting_on_fd--;
+ if (!next) next = th; /* Found one. */
+ }
+ }
+ END_FOREACH_FROM(curr,th);
+ }
+ }
+ /* The delays for some of the threads should have expired.
+ Go through the loop once more, to check the delays. */
+ } while (!next && delay != DELAY_INFTY);
+ }
+
+ if (!next) {
+ FOREACH_THREAD_FROM(curr,th) {
+ fprintf(stderr, "%s:%d:deadlock 0x%x: %d:%d %s\n",
+ th->file, th->line, th->thread, th->status,
+ th->wait_for, th==main_thread?"(main)":"");
+ }
+ END_FOREACH_FROM(curr,th);
+ /* raise fatal error to main thread */
+ thread_deadlock();
+ }
+ if (next == curr_thread) {
+ return;
+ }
+
+ /* context switch */
+ if (curr == curr_thread) {
+ thread_save_context(curr);
+ if (setjmp(curr->context)) {
+ return;
+ }
+ }
+
+ curr_thread = next;
+ if (next->status == THREAD_TO_KILL) {
+ /* execute ensure-clause if any */
+ thread_restore_context(next, 1);
+ }
+ thread_restore_context(next, 0);
+}
+
+void
+thread_wait_fd(fd)
+ int fd;
+{
+ if (curr_thread == curr_thread->next) return;
+
+ curr_thread->status = THREAD_STOPPED;
+ curr_thread->fd = fd;
+ num_waiting_on_fd++;
+ curr_thread->wait_for |= WAIT_FD;
+ thread_schedule();
+}
+
+void
+thread_fd_writable(fd)
+ int fd;
+{
+ struct timeval zero;
+ fd_set fds;
+
+ if (curr_thread == curr_thread->next) return;
+
+ zero.tv_sec = zero.tv_usec = 0;
+ for (;;) {
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ if (select(fd+1, 0, &fds, 0, &zero) == 1) break;
+ thread_schedule();
+ }
+}
+
+void
+thread_wait_for(time)
+ struct timeval time;
+{
+ double date;
+
+ if (curr_thread == curr_thread->next) {
+ int n;
+#ifndef linux
+ double d, limit;
+ limit = timeofday()+(double)time.tv_sec+(double)time.tv_usec*1e-6;
+#endif
+ for (;;) {
+ TRAP_BEG;
+ n = select(0, 0, 0, 0, &time);
+ TRAP_END;
+ if (n == 0) return;
+
+#ifndef linux
+ d = limit - timeofday();
+
+ time.tv_sec = (int)d;
+ time.tv_usec = (int)((d - (int)d)*1e6);
+ if (time.tv_usec < 0) {
+ time.tv_usec += 1e6;
+ time.tv_sec -= 1;
+ }
+ if (time.tv_sec < 0) return;
+#endif
+ }
+ }
+
+ date = timeofday() + (double)time.tv_sec + (double)time.tv_usec*1e-6;
+ curr_thread->status = THREAD_STOPPED;
+ curr_thread->delay = date;
+ num_waiting_on_timer++;
+ curr_thread->wait_for |= WAIT_TIME;
+ thread_schedule();
+}
+
+void thread_sleep_forever();
+
+int
+thread_alone()
+{
+ return curr_thread == curr_thread->next;
+}
+
+int
+thread_select(max, read, write, except, timeout)
+ int max;
+ fd_set *read, *write, *except;
+ struct timeval *timeout;
+{
+ double limit;
+ struct timeval zero;
+ fd_set r, *rp, w, *wp, x, *xp;
+ int n;
+
+ if (!read && !write && !except) {
+ if (!timeout) {
+ thread_sleep_forever();
+ return 0;
+ }
+ thread_wait_for(*timeout);
+ return 0;
+ }
+
+ if (timeout) {
+ limit = timeofday()+
+ (double)timeout->tv_sec+(double)timeout->tv_usec*1e-6;
+ }
+
+ if (curr_thread == curr_thread->next) { /* no other thread */
+#ifndef linux
+ struct timeval tv, *tvp = timeout;
+
+ if (timeout) {
+ tv = *timeout;
+ tvp = &tv;
+ }
+ for (;;) {
+ TRAP_BEG;
+ n = select(max, read, write, except, tvp);
+ TRAP_END;
+ if (n < 0 && errno == EINTR) {
+ if (timeout) {
+ double d = timeofday() - limit;
+
+ tv.tv_sec = (unsigned int)d;
+ tv.tv_usec = (d - (double)tv.tv_sec) * 1e6;
+ }
+ continue;
+ }
+ return n;
+ }
+#else
+ for (;;) {
+ TRAP_BEG;
+ n = select(max, read, write, except, timeout);
+ TRAP_END;
+ if (n < 0 && errno == EINTR) {
+ continue;
+ }
+ return n;
+ }
+#endif
+
+ }
+
+ for (;;) {
+ zero.tv_sec = zero.tv_usec = 0;
+ if (read) {rp = &r; r = *read;} else {rp = 0;}
+ if (write) {wp = &w; w = *write;} else {wp = 0;}
+ if (except) {xp = &x; x = *except;} else {xp = 0;}
+ n = select(max, rp, wp, xp, &zero);
+ if (n > 0) {
+ /* write back fds */
+ if (read) {*read = r;}
+ if (write) {*write = w;}
+ if (except) {*except = x;}
+ return n;
+ }
+ if (n < 0 && errno != EINTR) {
+ return n;
+ }
+ if (timeout) {
+ if (timeout->tv_sec == 0 && timeout->tv_usec == 0) return 0;
+ if (limit <= timeofday()) return 0;
+ }
+
+ thread_schedule();
+ CHECK_INTS;
+ }
+}
+
+static VALUE
+thread_join(dmy, thread)
+ VALUE dmy;
+ VALUE thread;
+{
+ thread_t th = thread_check(thread);
+
+ if (thread_dead(th)) return thread;
+ if ((th->wait_for & WAIT_JOIN) && th->join == curr_thread)
+ Raise(eThreadError, "Thread.join: deadlock");
+ curr_thread->status = THREAD_STOPPED;
+ curr_thread->join = th;
+ num_waiting_on_join++;
+ curr_thread->wait_for |= WAIT_JOIN;
+ thread_schedule();
+
+ return thread;
+}
+
+static VALUE
+thread_current()
+{
+ return curr_thread->thread;
+}
+
+static VALUE
+thread_main()
+{
+ return main_thread->thread;
+}
+
+static VALUE
+thread_wakeup(thread)
+ VALUE thread;
+{
+ thread_t th = thread_check(thread);
+
+ if (th->status == THREAD_KILLED) Raise(eThreadError, "killed thread");
+ thread_ready(th);
+
+ return thread;
+}
+
+static VALUE
+thread_run(thread)
+ VALUE thread;
+{
+ thread_wakeup(thread);
+ if (!thread_critical) thread_schedule();
+
+ return thread;
+}
+
+static VALUE
+thread_kill(thread)
+ VALUE thread;
+{
+ thread_t th = thread_check(thread);
+
+ if (th->status == THREAD_TO_KILL || th->status == THREAD_KILLED)
+ return thread;
+ if (th == th->next || th == main_thread) rb_exit(0);
+
+ thread_ready(th);
+ th->status = THREAD_TO_KILL;
+ thread_schedule();
+ return Qnil; /* not reached */
+}
+
+static VALUE
+thread_s_kill(obj, th)
+ VALUE obj, th;
+{
+ return thread_kill(th);
+}
+
+static VALUE
+thread_exit()
+{
+ return thread_kill(curr_thread->thread);
+}
+
+static VALUE
+thread_pass()
+{
+ thread_schedule();
+ return Qnil;
+}
+
+static VALUE
+thread_stop_method(thread)
+ VALUE thread;
+{
+ thread_t th = thread_check(thread);
+
+ thread_critical = 0;
+ th->status = THREAD_STOPPED;
+ thread_schedule();
+
+ return thread;
+}
+
+static VALUE
+thread_stop()
+{
+ thread_stop_method(curr_thread->thread);
+ return Qnil;
+}
+
+void
+thread_sleep(sec)
+ int sec;
+{
+ if (curr_thread == curr_thread->next) {
+ TRAP_BEG;
+ sleep(sec);
+ TRAP_END;
+ return;
+ }
+ thread_wait_for(time_timeval(INT2FIX(sec)));
+}
+
+void
+thread_sleep_forever()
+{
+ if (curr_thread == curr_thread->next) {
+ TRAP_BEG;
+ sleep((32767<<16)+32767);
+ TRAP_END;
+ return;
+ }
+
+ num_waiting_on_timer++;
+ curr_thread->delay = DELAY_INFTY;
+ curr_thread->wait_for |= WAIT_TIME;
+ curr_thread->status = THREAD_STOPPED;
+ thread_schedule();
+}
+
+static int thread_abort;
+
+static VALUE
+thread_s_abort_exc()
+{
+ return thread_abort?TRUE:FALSE;
+}
+
+static VALUE
+thread_s_abort_exc_set(self, val)
+ VALUE self, val;
+{
+ thread_abort = RTEST(val);
+ return val;
+}
+
+static VALUE
+thread_abort_exc(thread)
+ VALUE thread;
+{
+ thread_t th = thread_check(thread);
+
+ return th->abort?TRUE:FALSE;
+}
+
+static VALUE
+thread_abort_exc_set(thread, val)
+ VALUE thread, val;
+{
+ thread_t th = thread_check(thread);
+
+ th->abort = RTEST(val);
+ return val;
+}
+
+static thread_t
+thread_alloc()
+{
+ thread_t th;
+
+ th = ALLOC(struct thread);
+ th->status = THREAD_RUNNABLE;
+
+ th->status = 0;
+ th->result = 0;
+ th->errinfo = Qnil;
+ th->errat = Qnil;
+
+ th->stk_ptr = 0;
+ th->stk_len = 0;
+ th->stk_max = 0;
+ th->wait_for = 0;
+ th->fd = 0;
+ th->delay = 0.0;
+ th->join = 0;
+
+ th->frame = 0;
+ th->scope = 0;
+ th->class = 0;
+ th->dyna_vars = 0;
+ th->block = 0;
+ th->iter = 0;
+ th->tag = 0;
+ th->errat = 0;
+ th->errinfo = 0;
+ th->last_status = 0;
+ th->last_line = 0;
+ th->last_match = 0;
+ th->abort = 0;
+
+ th->thread = data_object_alloc(cThread, th, 0, thread_free);
+
+ if (curr_thread) {
+ th->prev = curr_thread;
+ curr_thread->next->prev = th;
+ th->next = curr_thread->next;
+ curr_thread->next = th;
+ }
+ else {
+ curr_thread = th->prev = th->next = th;
+ th->status = THREAD_RUNNABLE;
+ }
+
+ return th;
+}
+
+#if defined(HAVE_SETITIMER) && !defined(__BOW__)
+static void
+catch_timer(sig)
+ int sig;
+{
+#if !defined(POSIX_SIGNAL) && !defined(BSD_SIGNAL)
+ signal(sig, catch_timer);
+#endif
+ if (!thread_critical) {
+ if (trap_immediate) {
+ trap_immediate = 0;
+ thread_schedule();
+ }
+ else thread_pending = 1;
+ }
+}
+#else
+int thread_tick = THREAD_TICK;
+#endif
+
+VALUE
+thread_create(fn, arg)
+ VALUE (*fn)();
+ void *arg;
+{
+ thread_t th = thread_alloc();
+ NODE *state;
+
+#if defined(HAVE_SETITIMER) && !defined(__BOW__)
+ static init = 0;
+
+ if (!init) {
+ struct itimerval tval;
+
+#ifdef POSIX_SIGNAL
+ posix_signal(SIGVTALRM, catch_timer);
+#else
+ signal(SIGVTALRM, catch_timer);
+#endif
+
+ tval.it_interval.tv_sec = 0;
+ tval.it_interval.tv_usec = 100000;
+ tval.it_value = tval.it_interval;
+ setitimer(ITIMER_VIRTUAL, &tval, NULL);
+
+ init = 1;
+ }
+#endif
+
+ thread_save_context(curr_thread);
+ if (setjmp(curr_thread->context)) {
+ return th->thread;
+ }
+
+ PUSH_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ thread_save_context(th);
+ if (setjmp(th->context) == 0) {
+ curr_thread = th;
+ th->result = (*fn)(arg, th);
+ }
+ }
+ POP_TAG();
+ if (state) {
+ if (state->nd_tag == TAG_THROW) {
+ char *mesg;
+ char *tag = rb_id2name(state->nd_tlev);
+
+ mesg = ALLOCA_N(char, strlen(tag) + 64);
+
+ sprintf(mesg, "uncaught throw `%s' in thread 0x%x\n",
+ tag, th->thread);
+ curr_thread->errinfo = exc_new2(eThreadError, mesg);
+ curr_thread->errat = make_backtrace();
+ }
+ else if (th->status != THREAD_TO_KILL && !NIL_P(errinfo)) {
+ if (state->nd_tag == TAG_FATAL ||
+ obj_is_kind_of(errinfo, eSystemExit)) {
+ /* fatal error or global exit within this thread */
+ /* need to stop whole script */
+ main_thread->errat = errat;
+ main_thread->errinfo = errinfo;
+ thread_cleanup();
+ }
+ else if (thread_abort || curr_thread->abort) {
+ f_abort();
+ }
+ else {
+ curr_thread->errat = errat;
+ curr_thread->errinfo = errinfo;
+ }
+ }
+ }
+ thread_remove();
+ return 0;
+}
+
+static void
+thread_yield(arg, th)
+ int arg;
+ thread_t th;
+{
+ scope_dup(the_block->scope);
+ rb_yield(th->thread);
+}
+
+static VALUE
+thread_start()
+{
+ if (!iterator_p()) {
+ Raise(eThreadError, "must be called as iterator");
+ }
+ return thread_create(thread_yield, 0);
+}
+
+static VALUE
+thread_value(thread)
+ VALUE thread;
+{
+ thread_t th = thread_check(thread);
+
+ thread_join(0, thread);
+ if (!NIL_P(th->errinfo)) {
+ errat = make_backtrace();
+ ary_unshift(errat, ary_entry(th->errat, 0));
+ sourcefile = 0; /* kludge to print errat */
+ rb_raise(th->errinfo);
+ }
+
+ return th->result;
+}
+
+static VALUE
+thread_status(thread)
+ VALUE thread;
+{
+ thread_t th = thread_check(thread);
+
+ if (thread_dead(th)) {
+ if (NIL_P(th->errinfo)) return FALSE;
+ return Qnil;
+ }
+
+ return TRUE;
+}
+
+static VALUE
+thread_stopped(thread)
+ VALUE thread;
+{
+ thread_t th = thread_check(thread);
+
+ if (thread_dead(th)) return TRUE;
+ if (th->status == THREAD_STOPPED) return TRUE;
+ return FALSE;
+}
+
+static void
+thread_wait_other_threads()
+{
+ /* wait other threads to terminate */
+ while (curr_thread != curr_thread->next) {
+ thread_schedule();
+ }
+}
+
+static void
+thread_cleanup()
+{
+ thread_t th;
+
+ if (curr_thread != curr_thread->next->prev) {
+ curr_thread = curr_thread->prev;
+ }
+
+ FOREACH_THREAD(th) {
+ if (th != curr_thread && th->status != THREAD_KILLED) {
+ th->status = THREAD_TO_KILL;
+ th->wait_for = 0;
+ }
+ }
+ END_FOREACH(th);
+}
+
+int thread_critical;
+
+static VALUE
+thread_get_critical()
+{
+ return thread_critical?TRUE:FALSE;
+}
+
+static VALUE
+thread_set_critical(obj, val)
+ VALUE obj, val;
+{
+ thread_critical = RTEST(val);
+ return val;
+}
+
+void
+thread_interrupt()
+{
+ thread_critical = 0;
+ thread_ready(main_thread);
+ if (curr_thread == main_thread) {
+ rb_interrupt();
+ }
+ thread_save_context(curr_thread);
+ if (setjmp(curr_thread->context)) {
+ return;
+ }
+ curr_thread = main_thread;
+ thread_restore_context(curr_thread, 2);
+}
+
+static VALUE
+thread_raise(argc, argv, thread)
+ int argc;
+ VALUE *argv;
+ VALUE thread;
+{
+ thread_t th = thread_check(thread);
+
+ if (thread_dead(th)) return thread;
+ if (curr_thread == th) {
+ f_raise(argc, argv);
+ }
+
+ thread_save_context(curr_thread);
+ if (setjmp(curr_thread->context)) {
+ return thread;
+ }
+
+ rb_scan_args(argc, argv, "11", &th_raise_argv[0], &th_raise_argv[1]);
+ thread_ready(th);
+ curr_thread = th;
+
+ th_raise_argc = argc;
+ th_raise_file = sourcefile;
+ th_raise_line = sourceline;
+ thread_restore_context(curr_thread, 3);
+ return Qnil; /* not reached */
+}
+
+static thread_t loading_thread;
+static int loading_nest;
+
+static int
+thread_loading(feature)
+ char *feature;
+{
+ if (curr_thread != curr_thread->next && loading_thread) {
+ while (loading_thread != curr_thread) {
+ thread_schedule();
+ CHECK_INTS;
+ }
+ if (rb_provided(feature)) return TRUE; /* no need to load */
+ }
+
+ loading_thread = curr_thread;
+ loading_nest++;
+
+ return FALSE;
+}
+
+static void
+thread_loading_done()
+{
+ if (--loading_nest == 0) {
+ loading_thread = 0;
+ }
+}
+
+void
+Init_Thread()
+{
+ eThreadError = rb_define_class("ThreadError", eException);
+ cThread = rb_define_class("Thread", cObject);
+
+ rb_define_singleton_method(cThread, "new", thread_start, 0);
+ rb_define_singleton_method(cThread, "start", thread_start, 0);
+ rb_define_singleton_method(cThread, "fork", thread_start, 0);
+
+ rb_define_singleton_method(cThread, "stop", thread_stop, 0);
+ rb_define_singleton_method(cThread, "kill", thread_s_kill, 1);
+ rb_define_singleton_method(cThread, "exit", thread_exit, 0);
+ rb_define_singleton_method(cThread, "pass", thread_pass, 0);
+ rb_define_singleton_method(cThread, "join", thread_join, 1);
+ rb_define_singleton_method(cThread, "current", thread_current, 0);
+ rb_define_singleton_method(cThread, "main", thread_main, 0);
+
+ rb_define_singleton_method(cThread, "critical", thread_get_critical, 0);
+ rb_define_singleton_method(cThread, "critical=", thread_set_critical, 1);
+
+ rb_define_singleton_method(cThread, "abort_on_exception", thread_s_abort_exc, 0);
+ rb_define_singleton_method(cThread, "abort_on_exception=", thread_s_abort_exc_set, 1);
+
+ rb_define_method(cThread, "run", thread_run, 0);
+ rb_define_method(cThread, "wakeup", thread_wakeup, 0);
+ rb_define_method(cThread, "stop", thread_stop_method, 0);
+ rb_define_method(cThread, "exit", thread_kill, 0);
+ rb_define_method(cThread, "value", thread_value, 0);
+ rb_define_method(cThread, "status", thread_status, 0);
+ rb_define_method(cThread, "alive?", thread_status, 0);
+ rb_define_method(cThread, "stop?", thread_stopped, 0);
+ rb_define_method(cThread, "raise", thread_raise, -1);
+
+ rb_define_method(cThread, "abort_on_exception", thread_abort_exc, 0);
+ rb_define_method(cThread, "abort_on_exception=", thread_abort_exc_set, 1);
+
+ /* allocate main thread */
+ main_thread = thread_alloc();
+}
+#endif