1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/eval.c

5403 lines
106 KiB
C
Raw Normal View History

/************************************************
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