summaryrefslogtreecommitdiff
path: root/trunk/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/eval.c')
-rw-r--r--trunk/eval.c1262
1 files changed, 1262 insertions, 0 deletions
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]
+ *
+ * <em>produces:</em>
+ *
+ * ["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 <code>$!</code> or raises
+ * a <code>RuntimeError</code> if <code>$!</code> 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 <code>begin...end</code> 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 <code>true</code> if <code>yield</code> would execute a
+ * block in the current context. The <code>iterator?</code> form
+ * is mildly deprecated.
+ *
+ * def try
+ * if block_given?
+ * yield
+ * else
+ * "no block"
+ * end
+ * end
+ * try #=> "no block"
+ * try { "hello" } #=> "hello"
+ * try do "hello" end #=> "hello"
+ */
+
+
+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("<ifunc>");
+ }
+ 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
+ * <code>append_features</code> 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 <code>Module#include</code>.
+ */
+
+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 <code>Module.append_features</code> 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 <code>Object#extend</code>.
+ *
+ * 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
+ *
+ * <em>produces:</em>
+ *
+ * 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) #=> #<Klass:0x401b3bc8>
+ * 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 <code>Module.append_features</code>
+ * 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 <code>nil</code>.
+ *
+ */
+
+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; i<iseq->local_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;
+}
+
+