From 0dc342de848a642ecce8db697b8fecd83a63e117 Mon Sep 17 00:00:00 2001 From: yugui Date: Mon, 25 Aug 2008 15:02:05 +0000 Subject: added tag v1_9_0_4 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/tags/v1_9_0_4@18845 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- trunk/eval.c | 1262 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1262 insertions(+) create mode 100644 trunk/eval.c (limited to 'trunk/eval.c') diff --git a/trunk/eval.c b/trunk/eval.c new file mode 100644 index 0000000000..142433ac93 --- /dev/null +++ b/trunk/eval.c @@ -0,0 +1,1262 @@ +/********************************************************************** + + eval.c - + + $Author$ + created at: Thu Jun 10 14:22:17 JST 1993 + + Copyright (C) 1993-2007 Yukihiro Matsumoto + Copyright (C) 2000 Network Applied Communication Laboratory, Inc. + Copyright (C) 2000 Information-technology Promotion Agency, Japan + +**********************************************************************/ + +#include "eval_intern.h" + +VALUE proc_invoke(VALUE, VALUE, VALUE, VALUE); +VALUE rb_binding_new(void); +NORETURN(void rb_raise_jump(VALUE)); + +ID rb_frame_callee(void); +VALUE rb_eLocalJumpError; +VALUE rb_eSysStackError; + +#define exception_error GET_VM()->special_exceptions[ruby_error_reenter] + +#include "eval_error.c" +#include "eval_safe.c" +#include "eval_jump.c" + +/* initialize ruby */ + +#if defined(__APPLE__) +#define environ (*_NSGetEnviron()) +#elif !defined(_WIN32) && !defined(__MACOS__) || defined(_WIN32_WCE) +extern char **environ; +#endif +char **rb_origenviron; + +void rb_clear_trace_func(void); +void rb_thread_stop_timer_thread(void); + +void rb_call_inits(void); +void Init_heap(void); +void Init_ext(void); +void Init_BareVM(void); + +void +ruby_init(void) +{ + static int initialized = 0; + int state; + + if (initialized) + return; + initialized = 1; + +#ifdef __MACOS__ + rb_origenviron = 0; +#else + rb_origenviron = environ; +#endif + + Init_stack((void *)&state); + Init_BareVM(); + Init_heap(); + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + rb_call_inits(); + +#ifdef __MACOS__ + _macruby_init(); +#elif defined(__VMS) + _vmsruby_init(); +#endif + + ruby_prog_init(); + ALLOW_INTS; + } + POP_TAG(); + + if (state) { + error_print(); + exit(EXIT_FAILURE); + } + GET_VM()->running = 1; +} + +extern void rb_clear_trace_func(void); + +void * +ruby_options(int argc, char **argv) +{ + int state; + void *tree = 0; + + Init_stack((void *)&state); + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + SAVE_ROOT_JMPBUF(GET_THREAD(), tree = ruby_process_options(argc, argv)); + } + else { + rb_clear_trace_func(); + state = error_handle(state); + tree = (void *)INT2FIX(state); + } + POP_TAG(); + return tree; +} + +static void +ruby_finalize_0(void) +{ + PUSH_TAG(); + if (EXEC_TAG() == 0) { + rb_trap_exit(); + } + POP_TAG(); + rb_exec_end_proc(); + rb_clear_trace_func(); +} + +static void +ruby_finalize_1(void) +{ + ruby_sig_finalize(); + GET_THREAD()->errinfo = Qnil; + rb_gc_call_finalizer_at_exit(); +} + +void +ruby_finalize(void) +{ + ruby_finalize_0(); + ruby_finalize_1(); +} + +void rb_thread_stop_timer_thread(void); + +int +ruby_cleanup(int ex) +{ + int state; + volatile VALUE errs[2]; + rb_thread_t *th = GET_THREAD(); + int nerr; + + errs[1] = th->errinfo; + th->safe_level = 0; + Init_stack((void *)&state); + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + SAVE_ROOT_JMPBUF(th, ruby_finalize_0()); + } + POP_TAG(); + + errs[0] = th->errinfo; + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + SAVE_ROOT_JMPBUF(th, rb_thread_terminate_all()); + } + else if (ex == 0) { + ex = state; + } + th->errinfo = errs[1]; + ex = error_handle(ex); + ruby_finalize_1(); + POP_TAG(); + rb_thread_stop_timer_thread(); + + for (nerr = 0; nerr < sizeof(errs) / sizeof(errs[0]); ++nerr) { + VALUE err = errs[nerr]; + + if (!RTEST(err)) continue; + + /* th->errinfo contains a NODE while break'ing */ + if (TYPE(err) == T_NODE) continue; + + if (rb_obj_is_kind_of(err, rb_eSystemExit)) { + return sysexit_status(err); + } + else if (rb_obj_is_kind_of(err, rb_eSignal)) { + VALUE sig = rb_iv_get(err, "signo"); + ruby_default_signal(NUM2INT(sig)); + } + else if (ex == 0) { + ex = 1; + } + } + +#if EXIT_SUCCESS != 0 || EXIT_FAILURE != 1 + switch (ex) { +#if EXIT_SUCCESS != 0 + case 0: return EXIT_SUCCESS; +#endif +#if EXIT_FAILURE != 1 + case 1: return EXIT_FAILURE; +#endif + } +#endif + + return ex; +} + +int +ruby_exec_node(void *n, const char *file) +{ + int state; + VALUE iseq = (VALUE)n; + rb_thread_t *th = GET_THREAD(); + + if (!n) return 0; + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + SAVE_ROOT_JMPBUF(th, { + th->base_block = 0; + rb_iseq_eval(iseq); + }); + } + POP_TAG(); + return state; +} + +void +ruby_stop(int ex) +{ + exit(ruby_cleanup(ex)); +} + +int +ruby_run_node(void *n) +{ + VALUE v = (VALUE)n; + + switch (v) { + case Qtrue: return EXIT_SUCCESS; + case Qfalse: return EXIT_FAILURE; + } + if (FIXNUM_P(v)) { + return FIX2INT(v); + } + Init_stack((void *)&n); + return ruby_cleanup(ruby_exec_node(n, 0)); +} + +/* + * call-seq: + * Module.nesting => array + * + * Returns the list of +Modules+ nested at the point of call. + * + * module M1 + * module M2 + * $a = Module.nesting + * end + * end + * $a #=> [M1::M2, M1] + * $a[0].name #=> "M1::M2" + */ + +static VALUE +rb_mod_nesting(void) +{ + VALUE ary = rb_ary_new(); + const NODE *cref = vm_cref(); + + while (cref && cref->nd_next) { + VALUE klass = cref->nd_clss; + if (!NIL_P(klass)) { + rb_ary_push(ary, klass); + } + cref = cref->nd_next; + } + return ary; +} + +/* + * call-seq: + * Module.constants => array + * + * Returns an array of the names of all constants defined in the + * system. This list includes the names of all modules and classes. + * + * p Module.constants.sort[1..5] + * + * produces: + * + * ["ARGV", "ArgumentError", "Array", "Bignum", "Binding"] + */ + +static VALUE +rb_mod_s_constants(int argc, VALUE *argv, VALUE mod) +{ + const NODE *cref = vm_cref(); + VALUE klass; + VALUE cbase = 0; + void *data = 0; + + if (argc > 0) { + return rb_mod_constants(argc, argv, rb_cModule); + } + + while (cref) { + klass = cref->nd_clss; + if (!NIL_P(klass)) { + data = rb_mod_const_at(cref->nd_clss, data); + if (!cbase) { + cbase = klass; + } + } + cref = cref->nd_next; + } + + if (cbase) { + data = rb_mod_const_of(cbase, data); + } + return rb_const_list(data); +} + +void +rb_frozen_class_p(VALUE klass) +{ + const char *desc = "something(?!)"; + + if (OBJ_FROZEN(klass)) { + if (FL_TEST(klass, FL_SINGLETON)) + desc = "object"; + else { + switch (TYPE(klass)) { + case T_MODULE: + case T_ICLASS: + desc = "module"; + break; + case T_CLASS: + desc = "class"; + break; + } + } + rb_error_frozen(desc); + } +} + +NORETURN(static void rb_longjmp(int, VALUE)); +VALUE rb_make_backtrace(void); + +static void +rb_longjmp(int tag, VALUE mesg) +{ + VALUE at; + VALUE e; + rb_thread_t *th = GET_THREAD(); + const char *file; + int line = 0; + + if (rb_thread_set_raised(th)) { + th->errinfo = exception_error; + JUMP_TAG(TAG_FATAL); + } + + if (NIL_P(mesg)) + mesg = th->errinfo; + if (NIL_P(mesg)) { + mesg = rb_exc_new(rb_eRuntimeError, 0, 0); + } + + file = rb_sourcefile(); + if (file) line = rb_sourceline(); + if (file && !NIL_P(mesg)) { + at = get_backtrace(mesg); + if (NIL_P(at)) { + at = rb_make_backtrace(); + if (OBJ_FROZEN(mesg)) { + mesg = rb_obj_dup(mesg); + } + set_backtrace(mesg, at); + } + } + if (!NIL_P(mesg)) { + th->errinfo = mesg; + } + + if (RTEST(ruby_debug) && !NIL_P(e = th->errinfo) && + !rb_obj_is_kind_of(e, rb_eSystemExit)) { + int status; + + PUSH_TAG(); + if ((status = EXEC_TAG()) == 0) { + RB_GC_GUARD(e) = rb_obj_as_string(e); + if (file) { + warn_printf("Exception `%s' at %s:%d - %s\n", + rb_obj_classname(th->errinfo), + file, line, RSTRING_PTR(e)); + } + else { + warn_printf("Exception `%s' - %s\n", + rb_obj_classname(th->errinfo), + RSTRING_PTR(e)); + } + } + POP_TAG(); + if (status == TAG_FATAL && th->errinfo == exception_error) { + th->errinfo = mesg; + } + else if (status) { + rb_thread_reset_raised(th); + JUMP_TAG(status); + } + } + + rb_trap_restore_mask(); + + if (tag != TAG_FATAL) { + EXEC_EVENT_HOOK(th, RUBY_EVENT_RAISE, th->cfp->self, + 0 /* TODO: id */, 0 /* TODO: klass */); + } + + rb_thread_raised_clear(th); + JUMP_TAG(tag); +} + +void +rb_exc_raise(VALUE mesg) +{ + rb_longjmp(TAG_RAISE, mesg); +} + +void +rb_exc_fatal(VALUE mesg) +{ + rb_longjmp(TAG_FATAL, mesg); +} + +void +rb_interrupt(void) +{ + rb_raise(rb_eInterrupt, "%s", ""); +} + +static VALUE get_errinfo(void); + +/* + * call-seq: + * raise + * raise(string) + * raise(exception [, string [, array]]) + * fail + * fail(string) + * fail(exception [, string [, array]]) + * + * With no arguments, raises the exception in $! or raises + * a RuntimeError if $! is +nil+. + * With a single +String+ argument, raises a + * +RuntimeError+ with the string as a message. Otherwise, + * the first parameter should be the name of an +Exception+ + * class (or an object that returns an +Exception+ object when sent + * an +exception+ message). The optional second parameter sets the + * message associated with the exception, and the third parameter is an + * array of callback information. Exceptions are caught by the + * +rescue+ clause of begin...end blocks. + * + * raise "Failed to create socket" + * raise ArgumentError, "No parameters", caller + */ + +static VALUE +rb_f_raise(int argc, VALUE *argv) +{ + VALUE err; + if (argc == 0) { + err = get_errinfo(); + if (!NIL_P(err)) { + argc = 1; + argv = &err; + } + } + rb_raise_jump(rb_make_exception(argc, argv)); + return Qnil; /* not reached */ +} + +VALUE +rb_make_exception(int argc, VALUE *argv) +{ + VALUE mesg; + ID exception; + int n; + + mesg = Qnil; + switch (argc) { + case 0: + mesg = Qnil; + break; + case 1: + if (NIL_P(argv[0])) + break; + if (TYPE(argv[0]) == T_STRING) { + mesg = rb_exc_new3(rb_eRuntimeError, argv[0]); + break; + } + n = 0; + goto exception_call; + + case 2: + case 3: + n = 1; + exception_call: + CONST_ID(exception, "exception"); + if (!rb_respond_to(argv[0], exception)) { + rb_raise(rb_eTypeError, "exception class/object expected"); + } + mesg = rb_funcall(argv[0], exception, n, argv[1]); + break; + default: + rb_raise(rb_eArgError, "wrong number of arguments"); + break; + } + if (argc > 0) { + if (!rb_obj_is_kind_of(mesg, rb_eException)) + rb_raise(rb_eTypeError, "exception object expected"); + if (argc > 2) + set_backtrace(mesg, argv[2]); + } + + return mesg; +} + +void +rb_raise_jump(VALUE mesg) +{ + rb_thread_t *th = GET_THREAD(); + th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp); + /* TODO: fix me */ + rb_longjmp(TAG_RAISE, mesg); +} + +void +rb_jump_tag(int tag) +{ + JUMP_TAG(tag); +} + +int +rb_block_given_p(void) +{ + rb_thread_t *th = GET_THREAD(); + + if ((th->cfp->lfp[0] & 0x02) == 0 && + GC_GUARDED_PTR_REF(th->cfp->lfp[0])) { + return Qtrue; + } + else { + return Qfalse; + } +} + +int +rb_iterator_p() +{ + return rb_block_given_p(); +} + +/* + * call-seq: + * block_given? => true or false + * iterator? => true or false + * + * Returns true if yield would execute a + * block in the current context. The iterator? form + * is mildly deprecated. + * + * def try + * if block_given? + * yield + * else + * "no block" + * end + * end + * try #=> "no block" + * try { "hello" } #=> "hello" + * try do "hello" end #=> "hello" + */ + + +VALUE +rb_f_block_given_p(void) +{ + rb_thread_t *th = GET_THREAD(); + rb_control_frame_t *cfp = th->cfp; + cfp = vm_get_ruby_level_caller_cfp(th, RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)); + + if (cfp != 0 && + (cfp->lfp[0] & 0x02) == 0 && + GC_GUARDED_PTR_REF(cfp->lfp[0])) { + return Qtrue; + } + else { + return Qfalse; + } +} + +VALUE rb_eThreadError; + +void +rb_need_block() +{ + if (!rb_block_given_p()) { + vm_localjump_error("no block given", Qnil, 0); + } +} + +VALUE +rb_rescue2(VALUE (* b_proc) (ANYARGS), VALUE data1, + VALUE (* r_proc) (ANYARGS), VALUE data2, ...) +{ + int state; + rb_thread_t *th = GET_THREAD(); + rb_control_frame_t *cfp = th->cfp; + volatile VALUE result; + volatile VALUE e_info = th->errinfo; + va_list args; + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + retry_entry: + result = (*b_proc) (data1); + } + else { + th->cfp = cfp; /* restore */ + + if (state == TAG_RAISE) { + int handle = Qfalse; + VALUE eclass; + + va_init_list(args, data2); + while ((eclass = va_arg(args, VALUE)) != 0) { + if (rb_obj_is_kind_of(th->errinfo, eclass)) { + handle = Qtrue; + break; + } + } + va_end(args); + + if (handle) { + if (r_proc) { + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + result = (*r_proc) (data2, th->errinfo); + } + POP_TAG(); + if (state == TAG_RETRY) { + state = 0; + th->errinfo = Qnil; + goto retry_entry; + } + } + else { + result = Qnil; + state = 0; + } + if (state == 0) { + th->errinfo = e_info; + } + } + } + } + POP_TAG(); + if (state) + JUMP_TAG(state); + + return result; +} + +VALUE +rb_rescue(VALUE (* b_proc)(ANYARGS), VALUE data1, + VALUE (* r_proc)(ANYARGS), VALUE data2) +{ + return rb_rescue2(b_proc, data1, r_proc, data2, rb_eStandardError, + (VALUE)0); +} + +VALUE +rb_protect(VALUE (* proc) (VALUE), VALUE data, int * state) +{ + VALUE result = Qnil; /* OK */ + int status; + rb_thread_t *th = GET_THREAD(); + rb_control_frame_t *cfp = th->cfp; + struct rb_vm_trap_tag trap_tag; + rb_jmpbuf_t org_jmpbuf; + + trap_tag.prev = th->trap_tag; + + PUSH_TAG(); + th->trap_tag = &trap_tag; + MEMCPY(&org_jmpbuf, &(th)->root_jmpbuf, rb_jmpbuf_t, 1); + if ((status = EXEC_TAG()) == 0) { + SAVE_ROOT_JMPBUF(th, result = (*proc) (data)); + } + MEMCPY(&(th)->root_jmpbuf, &org_jmpbuf, rb_jmpbuf_t, 1); + th->trap_tag = trap_tag.prev; + POP_TAG(); + + if (state) { + *state = status; + } + if (status != 0) { + th->cfp = cfp; + return Qnil; + } + + return result; +} + +VALUE +rb_ensure(VALUE (*b_proc)(ANYARGS), VALUE data1, VALUE (*e_proc)(ANYARGS), VALUE data2) +{ + int state; + volatile VALUE result = Qnil; + + PUSH_TAG(); + if ((state = EXEC_TAG()) == 0) { + result = (*b_proc) (data1); + } + POP_TAG(); + /* TODO: fix me */ + /* retval = prot_tag ? prot_tag->retval : Qnil; */ /* save retval */ + (*e_proc) (data2); + if (state) + JUMP_TAG(state); + return result; +} + +VALUE +rb_with_disable_interrupt(VALUE (*proc)(ANYARGS), VALUE data) +{ + VALUE result = Qnil; /* OK */ + int status; + + DEFER_INTS; + { + int thr_critical = rb_thread_critical; + + rb_thread_critical = Qtrue; + PUSH_TAG(); + if ((status = EXEC_TAG()) == 0) { + result = (*proc) (data); + } + POP_TAG(); + rb_thread_critical = thr_critical; + } + ENABLE_INTS; + if (status) + JUMP_TAG(status); + + return result; +} + +static ID +frame_func_id(rb_control_frame_t *cfp) +{ + rb_iseq_t *iseq = cfp->iseq; + if (!iseq) { + return cfp->method_id; + } + while (iseq) { + if (RUBY_VM_IFUNC_P(iseq)) { + return rb_intern(""); + } + if (iseq->defined_method_id) { + return iseq->defined_method_id; + } + if (iseq->local_iseq == iseq) { + break; + } + iseq = iseq->parent_iseq; + } + return 0; +} + +ID +rb_frame_this_func(void) +{ + return frame_func_id(GET_THREAD()->cfp); +} + +ID +rb_frame_callee(void) +{ + rb_thread_t *th = GET_THREAD(); + rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp); + /* check if prev_cfp can be accessible */ + if ((void *)(th->stack + th->stack_size) == (void *)(prev_cfp)) { + return 0; + } + return frame_func_id(prev_cfp); +} + +void +rb_frame_pop(void) +{ + rb_thread_t *th = GET_THREAD(); + th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp); +} + +/* + * call-seq: + * append_features(mod) => mod + * + * When this module is included in another, Ruby calls + * append_features in this module, passing it the + * receiving module in _mod_. Ruby's default implementation is + * to add the constants, methods, and module variables of this module + * to _mod_ if this module has not already been added to + * _mod_ or one of its ancestors. See also Module#include. + */ + +static VALUE +rb_mod_append_features(VALUE module, VALUE include) +{ + switch (TYPE(include)) { + case T_CLASS: + case T_MODULE: + break; + default: + Check_Type(include, T_CLASS); + break; + } + rb_include_module(include, module); + + return module; +} + +/* + * call-seq: + * include(module, ...) => self + * + * Invokes Module.append_features on each parameter in turn. + */ + +static VALUE +rb_mod_include(int argc, VALUE *argv, VALUE module) +{ + int i; + + for (i = 0; i < argc; i++) + Check_Type(argv[i], T_MODULE); + while (argc--) { + rb_funcall(argv[argc], rb_intern("append_features"), 1, module); + rb_funcall(argv[argc], rb_intern("included"), 1, module); + } + return module; +} + +void +rb_obj_call_init(VALUE obj, int argc, VALUE *argv) +{ + PASS_PASSED_BLOCK(); + rb_funcall2(obj, idInitialize, argc, argv); +} + +void +rb_extend_object(VALUE obj, VALUE module) +{ + rb_include_module(rb_singleton_class(obj), module); +} + +/* + * call-seq: + * extend_object(obj) => obj + * + * Extends the specified object by adding this module's constants and + * methods (which are added as singleton methods). This is the callback + * method used by Object#extend. + * + * module Picky + * def Picky.extend_object(o) + * if String === o + * puts "Can't add Picky to a String" + * else + * puts "Picky added to #{o.class}" + * super + * end + * end + * end + * (s = Array.new).extend Picky # Call Object.extend + * (s = "quick brown fox").extend Picky + * + * produces: + * + * Picky added to Array + * Can't add Picky to a String + */ + +static VALUE +rb_mod_extend_object(VALUE mod, VALUE obj) +{ + rb_extend_object(obj, mod); + return obj; +} + +/* + * call-seq: + * obj.extend(module, ...) => obj + * + * Adds to _obj_ the instance methods from each module given as a + * parameter. + * + * module Mod + * def hello + * "Hello from Mod.\n" + * end + * end + * + * class Klass + * def hello + * "Hello from Klass.\n" + * end + * end + * + * k = Klass.new + * k.hello #=> "Hello from Klass.\n" + * k.extend(Mod) #=> # + * k.hello #=> "Hello from Mod.\n" + */ + +static VALUE +rb_obj_extend(int argc, VALUE *argv, VALUE obj) +{ + int i; + + if (argc == 0) { + rb_raise(rb_eArgError, "wrong number of arguments (0 for 1)"); + } + for (i = 0; i < argc; i++) + Check_Type(argv[i], T_MODULE); + while (argc--) { + rb_funcall(argv[argc], rb_intern("extend_object"), 1, obj); + rb_funcall(argv[argc], rb_intern("extended"), 1, obj); + } + return obj; +} + +/* + * call-seq: + * include(module, ...) => self + * + * Invokes Module.append_features + * on each parameter in turn. Effectively adds the methods and constants + * in each module to the receiver. + */ + +static VALUE +top_include(int argc, VALUE *argv, VALUE self) +{ + rb_thread_t *th = GET_THREAD(); + + rb_secure(4); + if (th->top_wrapper) { + rb_warning + ("main#include in the wrapped load is effective only in wrapper module"); + return rb_mod_include(argc, argv, th->top_wrapper); + } + return rb_mod_include(argc, argv, rb_cObject); +} + +VALUE rb_f_trace_var(); +VALUE rb_f_untrace_var(); + +static VALUE * +errinfo_place(void) +{ + rb_thread_t *th = GET_THREAD(); + rb_control_frame_t *cfp = th->cfp; + rb_control_frame_t *end_cfp = RUBY_VM_END_CONTROL_FRAME(th); + + while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp)) { + if (RUBY_VM_NORMAL_ISEQ_P(cfp->iseq)) { + if (cfp->iseq->type == ISEQ_TYPE_RESCUE) { + return &cfp->dfp[-2]; + } + else if (cfp->iseq->type == ISEQ_TYPE_ENSURE && + TYPE(cfp->dfp[-2]) != T_NODE && + !FIXNUM_P(cfp->dfp[-2])) { + return &cfp->dfp[-2]; + } + } + cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); + } + return 0; +} + +static VALUE +get_errinfo(void) +{ + VALUE *ptr = errinfo_place(); + if (ptr) { + return *ptr; + } + else { + return Qnil; + } +} + +static VALUE +errinfo_getter(ID id) +{ + return get_errinfo(); +} + +#if 0 +static void +errinfo_setter(VALUE val, ID id, VALUE *var) +{ + if (!NIL_P(val) && !rb_obj_is_kind_of(val, rb_eException)) { + rb_raise(rb_eTypeError, "assigning non-exception to $!"); + } + else { + VALUE *ptr = errinfo_place(); + if (ptr) { + *ptr = val; + } + else { + rb_raise(rb_eRuntimeError, "errinfo_setter: not in rescue clause."); + } + } +} +#endif + +VALUE +rb_errinfo(void) +{ + rb_thread_t *th = GET_THREAD(); + return th->errinfo; +} + +void +rb_set_errinfo(VALUE err) +{ + if (!NIL_P(err) && !rb_obj_is_kind_of(err, rb_eException)) { + rb_raise(rb_eTypeError, "assigning non-exception to $!"); + } + GET_THREAD()->errinfo = err; +} + +VALUE +rb_rubylevel_errinfo(void) +{ + return get_errinfo(); +} + +static VALUE +errat_getter(ID id) +{ + VALUE err = get_errinfo(); + if (!NIL_P(err)) { + return get_backtrace(err); + } + else { + return Qnil; + } +} + +static void +errat_setter(VALUE val, ID id, VALUE *var) +{ + VALUE err = get_errinfo(); + if (NIL_P(err)) { + rb_raise(rb_eArgError, "$! not set"); + } + set_backtrace(err, val); +} + +int vm_collect_local_variables_in_heap(rb_thread_t *th, VALUE *dfp, VALUE ary); + +/* + * call-seq: + * local_variables => array + * + * Returns the names of the current local variables. + * + * fred = 1 + * for i in 1..10 + * # ... + * end + * local_variables #=> ["fred", "i"] + */ + +static VALUE +rb_f_local_variables(void) +{ + VALUE ary = rb_ary_new(); + rb_thread_t *th = GET_THREAD(); + rb_control_frame_t *cfp = + vm_get_ruby_level_caller_cfp(th, RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp)); + int i; + + while (cfp) { + if (cfp->iseq) { + for (i = 0; i < cfp->iseq->local_table_size; i++) { + ID lid = cfp->iseq->local_table[i]; + if (lid) { + const char *vname = rb_id2name(lid); + /* should skip temporary variable */ + if (vname) { + rb_ary_push(ary, ID2SYM(lid)); + } + } + } + } + if (cfp->lfp != cfp->dfp) { + /* block */ + VALUE *dfp = GC_GUARDED_PTR_REF(cfp->dfp[0]); + + if (vm_collect_local_variables_in_heap(th, dfp, ary)) { + break; + } + else { + while (cfp->dfp != dfp) { + cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); + } + } + } + else { + break; + } + } + return ary; +} + + +/* + * call-seq: + * __method__ => symbol + * __callee__ => symbol + * + * Returns the name of the current method as a Symbol. + * If called outside of a method, it returns nil. + * + */ + +static VALUE +rb_f_method_name(void) +{ + ID fname = rb_frame_callee(); + + if (fname) { + return ID2SYM(fname); + } + else { + return Qnil; + } +} + +void +Init_eval(void) +{ + /* TODO: fix position */ + GET_THREAD()->vm->mark_object_ary = rb_ary_new(); + + rb_define_virtual_variable("$@", errat_getter, errat_setter); + rb_define_virtual_variable("$!", errinfo_getter, 0); + + rb_define_global_function("eval", rb_f_eval, -1); + rb_define_global_function("iterator?", rb_f_block_given_p, 0); + rb_define_global_function("block_given?", rb_f_block_given_p, 0); + + rb_define_global_function("raise", rb_f_raise, -1); + rb_define_global_function("fail", rb_f_raise, -1); + + rb_define_global_function("global_variables", rb_f_global_variables, 0); /* in variable.c */ + rb_define_global_function("local_variables", rb_f_local_variables, 0); + + rb_define_global_function("__method__", rb_f_method_name, 0); + rb_define_global_function("__callee__", rb_f_method_name, 0); + + rb_define_private_method(rb_cModule, "append_features", rb_mod_append_features, 1); + rb_define_private_method(rb_cModule, "extend_object", rb_mod_extend_object, 1); + rb_define_private_method(rb_cModule, "include", rb_mod_include, -1); + rb_define_method(rb_cModule, "module_eval", rb_mod_module_eval, -1); + rb_define_method(rb_cModule, "class_eval", rb_mod_module_eval, -1); + + rb_undef_method(rb_cClass, "module_function"); + + { + extern void Init_vm_eval(void); + extern void Init_eval_method(void); + Init_vm_eval(); + Init_eval_method(); + } + + rb_define_singleton_method(rb_cModule, "nesting", rb_mod_nesting, 0); + rb_define_singleton_method(rb_cModule, "constants", rb_mod_s_constants, -1); + + rb_define_singleton_method(rb_vm_top_self(), "include", top_include, -1); + + rb_define_method(rb_mKernel, "extend", rb_obj_extend, -1); + + rb_define_global_function("trace_var", rb_f_trace_var, -1); /* in variable.c */ + rb_define_global_function("untrace_var", rb_f_untrace_var, -1); /* in variable.c */ + + rb_define_virtual_variable("$SAFE", safe_getter, safe_setter); + + exception_error = rb_exc_new3(rb_eFatal, + rb_obj_freeze(rb_str_new2("exception reentered"))); + rb_ivar_set(exception_error, idThrowState, INT2FIX(TAG_FATAL)); + OBJ_TAINT(exception_error); + OBJ_FREEZE(exception_error); +} + + +/* for parser */ + +int +rb_dvar_defined(ID id) +{ + rb_thread_t *th = GET_THREAD(); + rb_iseq_t *iseq; + if (th->base_block && (iseq = th->base_block->iseq)) { + while (iseq->type == ISEQ_TYPE_BLOCK || + iseq->type == ISEQ_TYPE_RESCUE || + iseq->type == ISEQ_TYPE_ENSURE || + iseq->type == ISEQ_TYPE_EVAL) { + int i; + + for (i = 0; i < iseq->local_table_size; i++) { + if (iseq->local_table[i] == id) { + return 1; + } + } + iseq = iseq->parent_iseq; + } + } + return 0; +} + +int +rb_local_defined(ID id) +{ + rb_thread_t *th = GET_THREAD(); + rb_iseq_t *iseq; + + if (th->base_block && th->base_block->iseq) { + int i; + iseq = th->base_block->iseq->local_iseq; + + for (i=0; ilocal_table_size; i++) { + if (iseq->local_table[i] == id) { + return 1; + } + } + } + return 0; +} + +int +rb_parse_in_eval(void) +{ + return GET_THREAD()->parse_in_eval != 0; +} + + -- cgit v1.2.3