summaryrefslogtreecommitdiff
path: root/eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'eval.c')
-rw-r--r--eval.c6990
1 files changed, 1751 insertions, 5239 deletions
diff --git a/eval.c b/eval.c
index 42617c2b27..b6fedf11f3 100644
--- a/eval.c
+++ b/eval.c
@@ -1,5788 +1,2300 @@
-/************************************************
+/**********************************************************************
eval.c -
$Author$
- $Date$
created at: Thu Jun 10 14:22:17 JST 1993
- Copyright (C) 1993-1997 Yukihiro Matsumoto
+ Copyright (C) 1993-2007 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
-************************************************/
+**********************************************************************/
-#include "ruby.h"
-#include "node.h"
-#include "env.h"
-#include "sig.h"
+#include "ruby/internal/config.h"
-#include <stdio.h>
-#include <setjmp.h>
-#include "st.h"
-#include "dln.h"
-
-#ifndef HAVE_STRING_H
-char *strrchr();
-#endif
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#ifndef setjmp
-#ifdef HAVE__SETJMP
-#define setjmp(env) _setjmp(env)
-#define longjmp(env,val) _longjmp(env,val)
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
#endif
-#endif
-
-extern VALUE cData;
-
-VALUE cProc;
-static VALUE proc_call _((VALUE,VALUE));
-static VALUE f_binding _((VALUE));
-static void f_END _((void));
-
-#define SCOPE_PRIVATE FL_USER4
-#define CACHE_SIZE 0x200
-#define CACHE_MASK 0x1ff
-#define EXPR1(c,m) ((((int)(c)>>3)^(m))&CACHE_MASK)
+#include "eval_intern.h"
+#include "internal.h"
+#include "internal/class.h"
+#include "internal/cont.h"
+#include "internal/error.h"
+#include "internal/eval.h"
+#include "internal/gc.h"
+#include "internal/hash.h"
+#include "internal/inits.h"
+#include "internal/io.h"
+#include "internal/object.h"
+#include "internal/thread.h"
+#include "internal/variable.h"
+#include "ruby/fiber/scheduler.h"
+#include "iseq.h"
+#include "probes.h"
+#include "probes_helper.h"
+#include "ruby/vm.h"
+#include "vm_core.h"
+#include "ractor_core.h"
+#include "zjit.h"
+
+NORETURN(static void rb_raise_jump(VALUE, VALUE));
+void rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec);
+void rb_ec_clear_all_trace_func(const rb_execution_context_t *ec);
+
+static int rb_ec_cleanup(rb_execution_context_t *ec, enum ruby_tag_type ex);
+static int rb_ec_exec_node(rb_execution_context_t *ec, void *n);
+
+VALUE rb_eLocalJumpError;
+VALUE rb_eSysStackError;
+
+ID ruby_static_id_signo, ruby_static_id_status;
+extern ID ruby_static_id_cause;
+#define id_cause ruby_static_id_cause
+
+#define exception_error GET_VM()->special_exceptions[ruby_error_reenter]
+
+#include "eval_error.c"
+#include "eval_jump.c"
+
+#define CLASS_OR_MODULE_P(obj) \
+ (!SPECIAL_CONST_P(obj) && \
+ (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE))
-struct cache_entry { /* method hash table. */
- ID mid; /* method's id */
- ID mid0; /* method's original id */
- VALUE class; /* receiver's class */
- VALUE origin; /* where method defined */
- NODE *method;
- int noex;
-};
-
-static struct cache_entry cache[CACHE_SIZE];
-
-void
-rb_clear_cache()
+int
+ruby_setup(void)
{
- struct cache_entry *ent, *end;
+ enum ruby_tag_type state;
- ent = cache; end = ent + CACHE_SIZE;
- while (ent < end) {
- ent->mid = 0;
- ent++;
- }
-}
+ if (GET_VM())
+ return 0;
-static void
-rb_clear_cache_by_id(id)
- ID id;
-{
- struct cache_entry *ent, *end;
-
- ent = cache; end = ent + CACHE_SIZE;
- while (ent < end) {
- if (ent->mid == id) {
- ent->mid = 0;
- }
- ent++;
- }
-}
+ /*
+ * Disable THP early before mallocs happen because we want this to
+ * affect as many future pages as possible for CoW-friendliness
+ */
+#if defined(__linux__) && defined(PR_SET_THP_DISABLE)
+ prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0);
+#endif
+ Init_BareVM();
+ rb_vm_encoded_insn_data_table_init();
+ Init_enable_box();
+ Init_vm_objects();
+ Init_master_box();
+ Init_fstring_table();
-void
-rb_add_method(class, mid, node, noex)
- VALUE class;
- ID mid;
- NODE *node;
- int noex;
-{
- NODE *body;
+ EC_PUSH_TAG(GET_EC());
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ rb_call_inits();
+ ruby_prog_init();
+ GET_VM()->running = 1;
+ }
+ EC_POP_TAG();
- if (NIL_P(class)) class = cObject;
- body = NEW_METHOD(node, noex);
- st_insert(RCLASS(class)->m_tbl, mid, body);
+ return state;
}
void
-rb_remove_method(class, mid)
- VALUE class;
- ID mid;
+ruby_init(void)
{
- NODE *body;
-
- if (!st_delete(RCLASS(class)->m_tbl, &mid, &body)) {
- NameError("method `%s' not defined in %s",
- rb_id2name(mid), rb_class2name(class));
+ int state = ruby_setup();
+ if (state) {
+ if (RTEST(ruby_debug)) {
+ rb_execution_context_t *ec = GET_EC();
+ rb_ec_error_print(ec, ec->errinfo);
+ }
+ exit(EXIT_FAILURE);
}
- rb_clear_cache_by_id(mid);
}
-static NODE*
-search_method(class, id, origin)
- VALUE class, *origin;
- ID id;
+void *
+ruby_options(int argc, char **argv)
{
- NODE *body;
-
- while (!st_lookup(RCLASS(class)->m_tbl, id, &body)) {
- class = (VALUE)RCLASS(class)->super;
- if (!class) return 0;
- }
-
- if (origin) *origin = class;
- return body;
-}
+ rb_execution_context_t *ec = GET_EC();
+ enum ruby_tag_type state;
+ void *volatile iseq = 0;
-static NODE*
-rb_get_method_body(classp, idp, noexp)
- VALUE *classp;
- ID *idp;
- int *noexp;
-{
- ID id = *idp;
- VALUE class = *classp;
- VALUE origin;
- NODE *body;
- 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 = body->nd_orig;
- ent->origin = body->nd_orig;
- *idp = ent->mid0 = body->nd_mid;
- body = ent->method = body->nd_head;
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ iseq = ruby_process_options(argc, argv);
}
else {
- *classp = (VALUE)origin;
- ent->origin = origin;
- ent->mid = ent->mid0 = id;
- ent->method = body;
+ rb_ec_clear_current_thread_trace_func(ec);
+ int exitcode = error_handle(ec, ec->errinfo, state);
+ ec->errinfo = Qnil; /* just been handled */
+ iseq = (void *)INT2FIX(exitcode);
}
-
- if (noexp) *noexp = ent->noex;
- return body;
-}
-
-void
-rb_alias(class, name, def)
- VALUE class;
- ID name, def;
-{
- VALUE 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 = body->nd_orig;
- }
-
- st_insert(RCLASS(class)->m_tbl, name,
- NEW_METHOD(NEW_FBODY(body, def, origin), orig->nd_noex));
+ EC_POP_TAG();
+ return iseq;
}
static void
-rb_export_method(class, name, noex)
- VALUE class;
- ID name;
- int noex;
+rb_ec_fiber_scheduler_finalize(rb_execution_context_t *ec)
{
- NODE *body;
- struct RClass *origin;
+ enum ruby_tag_type state;
- body = search_method(class, name, &origin);
- if (!body && TYPE(class) == T_MODULE) {
- body = search_method(cObject, name, &origin);
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ rb_fiber_scheduler_set(Qnil);
}
- if (!body) {
- NameError("undefined method `%s' for `%s'",
- rb_id2name(name), rb_class2name(class));
- }
- if (body->nd_noex != noex) {
- if (class == (VALUE)origin) {
- body->nd_noex = noex;
- }
- else {
- rb_clear_cache_by_id(name);
- rb_add_method(class, name, NEW_ZSUPER(), noex);
- }
+ else {
+ state = error_handle(ec, ec->errinfo, state);
}
+ EC_POP_TAG();
}
-static VALUE
-method_boundp(class, id, ex)
- VALUE class;
- ID id;
- int ex;
+static void
+rb_ec_teardown(rb_execution_context_t *ec)
{
- int noex;
+ // If the user code defined a scheduler for the top level thread, run it:
+ rb_ec_fiber_scheduler_finalize(ec);
- if (rb_get_method_body(&class, &id, &noex)) {
- if (ex && noex == NOEX_PRIVATE)
- return FALSE;
- return TRUE;
+ EC_PUSH_TAG(ec);
+ if (EC_EXEC_TAG() == TAG_NONE) {
+ rb_vm_trap_exit(rb_ec_vm_ptr(ec));
}
- return FALSE;
+ EC_POP_TAG();
+ rb_ec_exec_end_proc(ec);
+ rb_ec_clear_all_trace_func(ec);
}
-int
-rb_method_boundp(class, id, ex)
- VALUE class;
- ID id;
- int ex;
+static void
+rb_ec_finalize(rb_execution_context_t *ec)
{
- if (method_boundp(class, id, ex))
- return TRUE;
- return FALSE;
-}
-
-static ID init, eqq, each, aref, aset, match;
-VALUE errinfo = Qnil, errat = Qnil;
-extern NODE *eval_tree0;
-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 = ALLOCA_N(struct FRAME,1);\
- _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;
- VALUE class;
- struct tag *tag;
- 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 = ALLOCA_N(struct BLOCK,1);\
- _block->tag = 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 = ALLOCA_N(struct BLOCK,1);\
- *_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; \
+ ruby_sig_finalize();
+ ec->errinfo = Qnil;
+ rb_objspace_call_finalizer();
}
-VALUE
-dyna_var_defined(id)
- ID id;
+void
+ruby_finalize(void)
{
- struct RVarmap *vars = the_dyna_vars;
-
- while (vars) {
- if (vars->id == id) return TRUE;
- vars = vars->next;
- }
- return FALSE;
+ rb_execution_context_t *ec = GET_EC();
+ rb_ec_teardown(ec);
+ rb_ec_finalize(ec);
}
-VALUE
-dyna_var_ref(id)
- ID id;
+int
+ruby_cleanup(int ex)
{
- struct RVarmap *vars = the_dyna_vars;
-
- while (vars) {
- if (vars->id == id) {
- return vars->val;
- }
- vars = vars->next;
- }
- return Qnil;
+ return rb_ec_cleanup(GET_EC(), (enum ruby_tag_type)ex);
}
-VALUE
-dyna_var_asgn(id, value)
- ID id;
- VALUE value;
+static int
+rb_ec_cleanup(rb_execution_context_t *ec, enum ruby_tag_type ex)
{
- 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; \
-}
-
-#ifdef C_ALLOCA
-/* need to protect retval in struct tag from GC. */
-#define tag_retval_dcl VALUE *dd_retval
-#define tag_retval_init VALUE _tag_retval = Qnil;\
- _tag->dd_retval = &_tag_retval;
-#define tag_retval dd_retval[0]
-#else
-#define tag_retval_dcl VALUE retval
-#define tag_retval_init _tag->retval = Qnil
-#define tag_retval retval
-#endif
-
-static struct tag {
- jmp_buf buf;
- struct FRAME *frame;
- struct iter *iter;
- ID tag;
- tag_retval_dcl;
- ID dst;
- struct tag *prev;
-} *prot_tag;
-
-#define PUSH_TAG(ptag) { \
- struct tag *_tag = ALLOCA_N(struct tag,1);\
- tag_retval_init; \
- _tag->frame = the_frame; \
- _tag->iter = the_iter; \
- _tag->prev = prot_tag; \
- _tag->tag_retval = Qnil; \
- _tag->tag = ptag; \
- _tag->dst = 0; \
- prot_tag = _tag;
-
-#define PROT_NONE 0
-#define PROT_FUNC -1
-#define PROT_THREAD -2
-
-#define EXEC_TAG() setjmp(prot_tag->buf)
-
-#define JUMP_TAG(st) { \
- the_frame = prot_tag->frame; \
- the_iter = prot_tag->iter; \
- longjmp(prot_tag->buf,(st)); \
-}
+ int state;
+ volatile VALUE save_error = Qundef;
+ volatile int sysex = EXIT_SUCCESS;
+ volatile int signaled = 0;
+ rb_thread_t *th = rb_ec_thread_ptr(ec);
+ rb_thread_t *const volatile th0 = th;
+ volatile int step = 0;
+ volatile VALUE message = Qnil;
+ VALUE buf;
+
+ rb_threadptr_interrupt(th);
+ rb_threadptr_check_signal(th);
+
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ RUBY_VM_CHECK_INTS(ec);
+
+ step_0: step++;
+ save_error = ec->errinfo;
+ if (THROW_DATA_P(ec->errinfo)) ec->errinfo = Qnil;
+
+ /* exits with failure but silently when an exception raised
+ * here */
+ rb_ec_teardown(ec);
+
+ step_1: step++;
+ VALUE err = ec->errinfo;
+ volatile int mode0 = 0, mode1 = 0;
+ if (err != save_error && !NIL_P(err)) {
+ mode0 = exiting_split(err, &sysex, &signaled);
+ }
-#define POP_TAG() \
- prot_tag = _tag->prev; \
-}
+ /* exceptions after here will be ignored */
-#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 TAG_MASK 0xf
-
-VALUE the_class;
-
-#define PUSH_CLASS() { \
- VALUE _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;\
-}
+ /* build error message including causes */
+ err = ATOMIC_VALUE_EXCHANGE(save_error, Qnil);
-static VALUE rb_eval();
-static VALUE eval();
-static NODE *compile();
+ if (!NIL_P(err) && !THROW_DATA_P(err)) {
+ mode1 = exiting_split(err, (mode0 & EXITING_WITH_STATUS) ? NULL : &sysex, &signaled);
+ if (mode1 & EXITING_WITH_MESSAGE) {
+ buf = rb_str_new(NULL, 0);
+ rb_ec_error_print_detailed(ec, err, buf, Qundef);
+ message = buf;
+ }
+ }
-static VALUE rb_call();
-VALUE rb_apply();
-VALUE rb_funcall2();
+ step_2: step++;
+ /* protect from Thread#raise */
+ th->status = THREAD_KILLED;
-static VALUE module_setup();
+ rb_ractor_terminate_all();
-static VALUE massign();
-static void assign();
+ step_3: step++;
+ if (!NIL_P(buf = message)) {
+ warn_print_str(buf);
+ }
+ else if (!NIL_OR_UNDEF_P(err = save_error) ||
+ (ex != TAG_NONE && !((mode0|mode1) & EXITING_WITH_STATUS))) {
+ sysex = error_handle(ec, err, ex);
+ }
+ }
+ else {
+ th = th0;
+ switch (step) {
+ case 0: goto step_0;
+ case 1: goto step_1;
+ case 2: goto step_2;
+ case 3: goto step_3;
+ }
+ }
-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
-*/
+ rb_ec_finalize(ec);
-int
-rb_safe_level()
-{
- return safe_level;
-}
+ /* unlock again if finalizer took mutexes. */
+ rb_threadptr_unlock_all_locking_mutexes(th);
+ th = th0;
+ EC_POP_TAG();
+ th = th0;
+ rb_thread_stop_timer_thread();
+ ruby_vm_destruct(th->vm);
+ // For YJIT, call this after ruby_vm_destruct() frees jit_cont for the root fiber.
+ rb_jit_cont_finish();
-void
-rb_set_safe_level(level)
- int level;
-{
- if (level > safe_level) {
- safe_level = level;
- }
-}
+ if (signaled) ruby_default_signal(signaled);
-static VALUE
-safe_getter()
-{
- return INT2FIX(safe_level);
+ return sysex;
}
-static void
-safe_setter(val)
- VALUE val;
+static int
+rb_ec_exec_node(rb_execution_context_t *ec, void *n)
{
- int level = NUM2INT(val);
+ volatile int state;
+ rb_iseq_t *iseq = (rb_iseq_t *)n;
+ if (!n) return 0;
- if (level < safe_level) {
- Raise(eSecurityError, "tried to downgrade safe level from %d to %d",
- safe_level, level);
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ rb_iseq_eval_main(iseq);
}
- safe_level = level;
+ EC_POP_TAG();
+ return state;
}
void
-rb_check_safe_str(x)
- VALUE x;
+ruby_stop(int ex)
{
- 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));
- }
+ exit(ruby_cleanup(ex));
}
-void
-rb_secure(level)
- int level;
+int
+ruby_executable_node(void *n, int *status)
{
- if (level <= safe_level) {
- Raise(eSecurityError, "Insecure operation `%s' for level %d",
- rb_id2name(the_frame->last_func), level);
+ VALUE v = (VALUE)n;
+ int s;
+
+ switch (v) {
+ case Qtrue: s = EXIT_SUCCESS; break;
+ case Qfalse: s = EXIT_FAILURE; break;
+ default:
+ if (!FIXNUM_P(v)) return TRUE;
+ s = FIX2INT(v);
}
+ if (status) *status = s;
+ return FALSE;
}
-extern int sourceline;
-extern char *sourcefile;
-
-static VALUE trace_func = 0;
-static void call_trace_func();
-
-static void
-error_pos()
+int
+ruby_run_node(void *n)
{
- 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);
- }
+ rb_execution_context_t *ec = GET_EC();
+ int status;
+ if (!ruby_executable_node(n, &status)) {
+ rb_ec_cleanup(ec, (NIL_P(ec->errinfo) ? TAG_NONE : TAG_RAISE));
+ return status;
}
+ return rb_ec_cleanup(ec, rb_ec_exec_node(ec, n));
}
-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);
- }
+int
+ruby_exec_node(void *n)
+{
+ return rb_ec_exec_node(GET_EC(), n);
+}
+
+/*
+ * call-seq:
+ * Module.nesting -> array
+ *
+ * Returns nested module as an array of Module objects:
+ *
+ * module M0
+ * def self.speak = Module.nesting
+ * module M1
+ * def self.speak = Module.nesting
+ * module M2
+ * def self.speak = Module.nesting
+ * end
+ * end
+ * end
+ * M0.speak # => [M0]
+ * M0.speak.first.class # => Module
+ * M0::M1.speak # => [M0::M1, M0]
+ * M0::M1::M2.speak # => [M0::M1::M2, M0::M1, M0]
+ *
+ */
+
+static VALUE
+rb_mod_nesting(VALUE _)
+{
+ VALUE ary = rb_ary_new();
+ const rb_cref_t *cref = rb_vm_cref();
+
+ while (cref && CREF_NEXT(cref)) {
+ VALUE klass = CREF_CLASS(cref);
+ if (!CREF_PUSHED_BY_EVAL(cref) &&
+ !NIL_P(klass)) {
+ rb_ary_push(ary, klass);
+ }
+ cref = CREF_NEXT(cref);
}
+ return ary;
+}
- eclass = CLASS_OF(errinfo);
- if (eclass == eRuntimeError && RSTRING(errinfo)->len == 0) {
- fprintf(stderr, ": unhandled exception\n");
- }
- else {
- VALUE epath;
-
- epath = rb_class_path(eclass);
- if (RSTRING(errinfo)->len == 0) {
- fprintf(stderr, ": ");
- fwrite(RSTRING(epath)->ptr, 1, RSTRING(epath)->len, stderr);
- putc('\n', stderr);
- }
- else {
- unsigned char *tail = 0;
- int len = RSTRING(errinfo)->len;
-
- if (RSTRING(epath)->ptr[0] == '#') epath = 0;
- if (tail = strchr(RSTRING(errinfo)->ptr, '\n')) {
- len = tail - RSTRING(errinfo)->ptr;
- tail++; /* skip newline */
- }
- fprintf(stderr, ": ");
- fwrite(RSTRING(errinfo)->ptr, 1, len, stderr);
- if (epath) {
- fprintf(stderr, " (");
- fwrite(RSTRING(epath)->ptr, 1, RSTRING(epath)->len, stderr);
- fprintf(stderr, ")\n");
- }
- if (tail) {
- fwrite(tail, 1, RSTRING(errinfo)->len-len-1, stderr);
- putc('\n', stderr);
- }
- }
+/*
+ * call-seq:
+ * Module.constants -> array
+ * Module.constants(inherited) -> array
+ *
+ * In the first form, returns an array of the names of all
+ * constants accessible from the point of call.
+ * This list includes the names of all modules and classes
+ * defined in the global scope.
+ *
+ * Module.constants.first(4)
+ * # => [:ARGF, :ARGV, :ArgumentError, :Array]
+ *
+ * Module.constants.include?(:SEEK_SET) # => false
+ *
+ * class IO
+ * Module.constants.include?(:SEEK_SET) # => true
+ * end
+ *
+ * The second form calls the instance method +constants+.
+ */
+
+static VALUE
+rb_mod_s_constants(int argc, VALUE *argv, VALUE mod)
+{
+ const rb_cref_t *cref = rb_vm_cref();
+ VALUE klass;
+ VALUE cbase = 0;
+ void *data = 0;
+
+ if (argc > 0 || mod != rb_cModule) {
+ return rb_mod_constants(argc, argv, mod);
+ }
+
+ while (cref) {
+ klass = CREF_CLASS(cref);
+ if (!CREF_PUSHED_BY_EVAL(cref) &&
+ !NIL_P(klass)) {
+ data = rb_mod_const_at(CREF_CLASS(cref), data);
+ if (!cbase) {
+ cbase = klass;
+ }
+ }
+ cref = CREF_NEXT(cref);
}
- 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
-
- ep = RARRAY(errat);
- 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;
- }
- }
+ if (cbase) {
+ data = rb_mod_const_of(cbase, data);
}
+ return rb_const_list(data);
}
-#ifndef NT
-extern char **environ;
-#endif
-char **origenviron;
-
-void rb_call_inits _((void));
-void init_stack _((void));
-void init_heap _((void));
-void Init_ext _((void));
-void gc_call_finalizer_at_exit _((void));
-
+/**
+ * Asserts that `klass` is not a frozen class.
+ * @param[in] klass a `Module` object
+ * @exception RuntimeError if `klass` is not a class or frozen.
+ * @ingroup class
+ */
void
-ruby_init()
+rb_class_modify_check(VALUE klass)
{
- static struct FRAME frame;
- static struct iter iter;
- int 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;
- /* default visibility is private at toplevel */
- FL_SET(top_scope, SCOPE_PRIVATE);
-
- PUSH_TAG(PROT_NONE)
- if ((state = EXEC_TAG()) == 0) {
- rb_call_inits();
- the_class = cObject;
- the_frame->cbase = (VALUE)node_newnode(NODE_CREF,cObject,0,0);
- rb_define_global_const("TOPLEVEL_BINDING", f_binding(TopSelf));
- ruby_prog_init();
+ if (SPECIAL_CONST_P(klass)) {
+ Check_Type(klass, T_CLASS);
}
- 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;
-{
- int state;
-
- PUSH_TAG(PROT_NONE)
- if ((state = EXEC_TAG()) == 0) {
- NODE *save;
-
- ruby_process_options(argc, argv);
- save = eval_tree;
- eval_tree = 0;
- Init_ext();
- ext_init = 1;
- rb_require_modules();
- eval_tree = save;
+ if (RB_TYPE_P(klass, T_MODULE)) {
+ // TODO: shouldn't this only happen in a few places?
+ rb_class_set_initialized(klass);
}
- POP_TAG();
- if (state) {
- error_print();
- exit(1);
+ if (OBJ_FROZEN(klass)) {
+ if (RCLASS_SINGLETON_P(klass)) {
+ klass = RCLASS_ATTACHED_OBJECT(klass);
+ }
+ rb_error_frozen_object(klass);
}
}
+NORETURN(static void rb_longjmp(rb_execution_context_t *, enum ruby_tag_type, volatile VALUE, VALUE));
+static VALUE get_errinfo(void);
+#define get_ec_errinfo(ec) rb_ec_get_errinfo(ec)
+
static VALUE
-eval_node(self)
- VALUE self;
+exc_setup_cause(VALUE exc, VALUE cause)
{
- VALUE result = Qnil;
- NODE *tree;
+#if OPT_SUPPORT_JOKE
+ if (NIL_P(cause)) {
+ ID id_true_cause;
+ CONST_ID(id_true_cause, "true_cause");
- if (eval_tree0) {
- tree = eval_tree0;
- eval_tree0 = 0;
- rb_eval(self, tree);
+ cause = rb_attr_get(rb_eFatal, id_true_cause);
+ if (NIL_P(cause)) {
+ cause = rb_exc_new_cstr(rb_eFatal, "because using such Ruby");
+ rb_ivar_set(cause, id_cause, INT2FIX(42)); /* the answer */
+ OBJ_FREEZE(cause);
+ rb_ivar_set(rb_eFatal, id_true_cause, cause);
+ }
}
-
- 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
+ if (!NIL_P(cause) && cause != exc) {
+ rb_ivar_set(exc, id_cause, cause);
+ if (!rb_ivar_defined(cause, id_cause)) {
+ rb_ivar_set(cause, id_cause, Qnil);
+ }
+ }
+ return exc;
+}
-static int exit_status;
-
-static void exec_end_proc();
-
-void
-ruby_run()
+static inline VALUE
+exc_setup_message(const rb_execution_context_t *ec, VALUE mesg, VALUE *cause)
{
- int state;
- static int ex;
-
- if (nerrs > 0) exit(nerrs);
-
- init_stack();
- errat = Qnil; /* clear for execution */
+ int nocause = 0;
+ int nocircular = 0;
- PUSH_TAG(PROT_NONE);
- PUSH_ITER(ITER_NOT);
- if ((state = EXEC_TAG()) == 0) {
- if (!ext_init) Init_ext();
- eval_node(TopSelf);
+ if (NIL_P(mesg)) {
+ mesg = ec->errinfo;
+ if (INTERNAL_EXCEPTION_P(mesg)) EC_JUMP_TAG(ec, TAG_FATAL);
+ nocause = 1;
}
- POP_ITER();
- POP_TAG();
-
- if (state && !ex) ex = state;
- PUSH_TAG(PROT_NONE);
- PUSH_ITER(ITER_NOT);
- if ((state = EXEC_TAG()) == 0) {
- rb_trap_exit();
-#ifdef THREAD
- thread_cleanup();
- thread_wait_other_threads();
-#endif
- exec_end_proc();
- gc_call_finalizer_at_exit();
+ if (NIL_P(mesg)) {
+ mesg = rb_exc_new(rb_eRuntimeError, 0, 0);
+ nocause = 0;
+ nocircular = 1;
}
- else {
- ex = state;
+ if (UNDEF_P(*cause)) {
+ if (nocause) {
+ *cause = Qnil;
+ nocircular = 1;
+ }
+ else if (!rb_ivar_defined(mesg, id_cause)) {
+ *cause = get_ec_errinfo(ec);
+ }
+ else {
+ nocircular = 1;
+ }
+ }
+ else if (!NIL_P(*cause) && !rb_obj_is_kind_of(*cause, rb_eException)) {
+ rb_raise(rb_eTypeError, "exception object expected");
}
- POP_ITER();
- POP_TAG();
- switch (ex & 0xf) {
- case 0:
- exit(0);
-
- 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 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;
- default:
- Bug("Unknown longjmp status %d", ex);
- break;
+ if (!nocircular && !NIL_P(*cause) && !UNDEF_P(*cause) && *cause != mesg) {
+#if 0 /* maybe critical for some cases */
+ rb_exc_check_circular_cause(*cause);
+#else
+ VALUE c = *cause;
+ while (!NIL_P(c)) {
+ if (c == mesg) {
+ rb_raise(rb_eArgError, "circular causes");
+ }
+ if (THROW_DATA_P(c)) {
+ break;
+ }
+ c = rb_attr_get(c, id_cause);
+ }
+#endif
}
+ return mesg;
}
static void
-compile_error(at)
- char *at;
+setup_exception(rb_execution_context_t *ec, enum ruby_tag_type tag, volatile VALUE mesg, VALUE cause)
{
- VALUE mesg;
-
- mesg = errinfo;
- nerrs = 0;
- errinfo = exc_new2(eSyntaxError, "compile error");
- if (at) {
- str_cat(errinfo, " in ", 4);
- str_cat(errinfo, at, strlen(at));
+ VALUE e;
+ int line;
+ const char *file = rb_source_location_cstr(&line);
+ const char *const volatile file0 = file;
+
+ if ((file && !NIL_P(mesg)) || !UNDEF_P(cause)) {
+ volatile int state = 0;
+
+ EC_PUSH_TAG(ec);
+ if (EC_EXEC_TAG() == TAG_NONE && !(state = rb_ec_set_raised(ec))) {
+ VALUE bt = rb_get_backtrace(mesg);
+ if (!NIL_P(bt) || UNDEF_P(cause)) {
+ if (OBJ_FROZEN(mesg)) {
+ mesg = rb_obj_dup(mesg);
+ }
+ }
+ if (!UNDEF_P(cause) && !THROW_DATA_P(cause)) {
+ exc_setup_cause(mesg, cause);
+ }
+ if (NIL_P(bt)) {
+ VALUE at = rb_ec_backtrace_object(ec);
+ rb_ivar_set(mesg, idBt_locations, at);
+ set_backtrace(mesg, at);
+ }
+ rb_ec_reset_raised(ec);
+ }
+ EC_POP_TAG();
+ file = file0;
+ if (state) goto fatal;
}
- str_cat(errinfo, "\n", 1);
- 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;
-{
- int state;
- struct SCOPE *saved_scope;
- volatile int safe = rb_safe_level();
-
- if (TYPE(cmd) != T_STRING) {
- Check_Type(arg, T_ARRAY);
- rb_funcall2(cmd, rb_intern("call"),
- RARRAY(arg)->len, RARRAY(arg)->ptr);
- return;
+ if (!NIL_P(mesg)) {
+ ec->errinfo = mesg;
+ }
+
+ if (RTEST(ruby_debug) && !NIL_P(e = ec->errinfo) &&
+ !rb_obj_is_kind_of(e, rb_eSystemExit)) {
+ enum ruby_tag_type state;
+
+ mesg = e;
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ ec->errinfo = Qnil;
+ e = rb_obj_as_string(mesg);
+ ec->errinfo = mesg;
+ if (file && line) {
+ e = rb_sprintf("Exception '%"PRIsVALUE"' at %s:%d - %"PRIsVALUE"\n",
+ rb_obj_class(mesg), file, line, e);
+ }
+ else if (file) {
+ e = rb_sprintf("Exception '%"PRIsVALUE"' at %s - %"PRIsVALUE"\n",
+ rb_obj_class(mesg), file, e);
+ }
+ else {
+ e = rb_sprintf("Exception '%"PRIsVALUE"' - %"PRIsVALUE"\n",
+ rb_obj_class(mesg), e);
+ }
+ warn_print_str(e);
+ }
+ EC_POP_TAG();
+ if (state == TAG_FATAL && ec->errinfo == exception_error) {
+ ec->errinfo = mesg;
+ }
+ else if (state) {
+ rb_ec_reset_raised(ec);
+ EC_JUMP_TAG(ec, state);
+ }
}
- PUSH_CLASS();
- PUSH_TAG(PROT_NONE);
- saved_scope = the_scope;
- the_scope = top_scope;
-
- the_class = cObject;
- if (str_tainted(cmd)) {
- safe_level = 5;
+ if (rb_ec_set_raised(ec)) {
+ goto fatal;
}
- if ((state = EXEC_TAG()) == 0) {
- eval(TopSelf, cmd, Qnil);
+ if (tag != TAG_FATAL) {
+ RUBY_DTRACE_HOOK(RAISE, rb_obj_classname(ec->errinfo));
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_RAISE, ec->cfp->self, 0, 0, 0, mesg);
}
+ return;
- the_scope = saved_scope;
- safe_level = safe;
- POP_TAG();
- POP_CLASS();
-
- switch (state) {
- case 0:
- break;
- 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;
- }
+ fatal:
+ ec->errinfo = exception_error;
+ rb_ec_reset_raised(ec);
+ EC_JUMP_TAG(ec, TAG_FATAL);
}
+/*! \private */
void
-rb_trap_eval(cmd, sig)
- VALUE cmd;
- int sig;
+rb_ec_setup_exception(const rb_execution_context_t *ec, VALUE mesg, VALUE cause)
{
- int state;
-
- PUSH_TAG(PROT_NONE);
- if ((state = EXEC_TAG()) == 0) {
- rb_eval_cmd(cmd, ary_new3(1, INT2FIX(sig)));
- }
- POP_TAG();
- if (state) {
- trap_immediate = 0;
- JUMP_TAG(state);
+ if (UNDEF_P(cause)) {
+ cause = get_ec_errinfo(ec);
}
-}
-
-static VALUE
-superclass(self, node)
- VALUE self;
- NODE *node;
-{
- VALUE val = 0; /* OK */
- int state;
+ if (cause != mesg) {
+ if (THROW_DATA_P(cause)) {
+ cause = Qnil;
+ }
- PUSH_TAG(PROT_NONE);
- if ((state = EXEC_TAG()) == 0) {
- val = rb_eval(self, node);
- }
- POP_TAG();
- if (state == 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");
+ rb_ivar_set(mesg, id_cause, cause);
}
-
- return val;
}
-static VALUE
-ev_const_defined(cref, id)
- NODE *cref;
- ID id;
+static void
+rb_longjmp(rb_execution_context_t *ec, enum ruby_tag_type tag, volatile VALUE mesg, VALUE cause)
{
- 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);
+ mesg = exc_setup_message(ec, mesg, &cause);
+ setup_exception(ec, tag, mesg, cause);
+ rb_ec_raised_clear(ec);
+ EC_JUMP_TAG(ec, tag);
}
-static VALUE
-ev_const_get(cref, id)
- NODE *cref;
- ID id;
-{
- NODE *cbase = cref;
- VALUE result;
+static VALUE make_exception(int argc, const VALUE *argv, int isstr);
- 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);
-}
+NORETURN(static void rb_exc_exception(VALUE mesg, enum ruby_tag_type tag, VALUE cause));
-static VALUE
-mod_nesting()
+static void
+rb_exc_exception(VALUE mesg, enum ruby_tag_type tag, VALUE cause)
{
- NODE *cbase = (NODE*)the_frame->cbase;
- VALUE ary = ary_new();
-
- while (cbase && cbase->nd_clss != cObject) {
- ary_push(ary, cbase->nd_clss);
- cbase = cbase->nd_next;
+ if (!NIL_P(mesg)) {
+ mesg = make_exception(1, &mesg, FALSE);
}
- return ary;
+ rb_longjmp(GET_EC(), tag, mesg, cause);
}
-static VALUE
-mod_s_constants()
+/**
+ * Raises an exception in the current thread.
+ * @param[in] mesg an Exception class or an `Exception` object.
+ * @exception always raises an instance of the given exception class or
+ * the given `Exception` object.
+ * @ingroup exception
+ */
+void
+rb_exc_raise(VALUE mesg)
{
- NODE *cbase = (NODE*)the_frame->cbase;
- VALUE ary = ary_new();
-
- while (cbase && cbase->nd_clss != cObject) {
- mod_const_at(cbase->nd_clss, ary);
- cbase = cbase->nd_next;
- }
-
- mod_const_of(((NODE*)the_frame->cbase)->nd_clss, ary);
- return ary;
+ rb_exc_exception(mesg, TAG_RAISE, Qundef);
}
-static VALUE
-mod_remove_method(mod, name)
- VALUE mod, name;
+/*!
+ * Raises a fatal error in the current thread.
+ *
+ * Same as rb_exc_raise() but raises a fatal error, which Ruby codes
+ * cannot rescue.
+ * \ingroup exception
+ */
+void
+rb_exc_fatal(VALUE mesg)
{
- rb_remove_method(mod, rb_to_id(name));
- return mod;
+ rb_exc_exception(mesg, TAG_FATAL, Qnil);
}
-static VALUE
-mod_undef_method(mod, name)
- VALUE mod, name;
+void
+rb_interrupt(void)
{
- ID id = rb_to_id(name);
-
- rb_add_method(mod, id, 0, NOEX_PUBLIC);
- rb_clear_cache_by_id(id);
- return mod;
+ rb_exc_raise(rb_exc_new(rb_eInterrupt, 0, 0));
}
-static VALUE
-mod_alias_method(mod, new, old)
- VALUE mod, new, old;
+static int
+extract_raise_options(int argc, VALUE *argv, VALUE *cause)
{
- ID id = rb_to_id(new);
-
- rb_alias(mod, id, rb_to_id(old));
- rb_clear_cache_by_id(id);
- return mod;
-}
-
-#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) {\
- char *file = sourcefile;\
- int line = sourceline;\
- int i;\
- 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 = file;\
- sourceline = line;\
- }\
- else {\
- argc = 0;\
- argv = 0;\
- }\
- }\
- else {\
- VALUE args = rb_eval(self,n);\
- char *file = sourcefile;\
- int line = sourceline;\
- if (TYPE(args) != T_ARRAY)\
- args = rb_Array(args);\
- argc = RARRAY(args)->len;\
- argv = ALLOCA_N(VALUE, argc);\
- MEMCPY(argv, RARRAY(args)->ptr, VALUE, argc);\
- sourcefile = file;\
- sourceline = line;\
- }\
-}
+ // Keyword arguments:
+ static ID keywords[1] = {0};
+ if (!keywords[0]) {
+ CONST_ID(keywords[0], "cause");
+ }
-#define MATCH_DATA the_scope->local_vars[node->nd_cnt]
+ if (argc > 0) {
+ VALUE options;
+ argc = rb_scan_args(argc, argv, "*:", NULL, &options);
-static char* is_defined _((VALUE, NODE*, char*));
+ if (!NIL_P(options)) {
+ if (!RHASH_EMPTY_P(options)) {
+ // Extract optional cause keyword argument, leaving any other options alone:
+ rb_get_kwargs(options, keywords, 0, -2, cause);
-static char*
-arg_defined(self, node, buf, type)
- VALUE self;
- NODE *node;
- char *buf;
- char *type;
-{
- int argc;
- int i;
-
- if (!node) return type; /* no args */
- if (nd_type(node) == NODE_ARRAY) {
- argc=node->nd_alen;
- if (argc > 0) {
- for (i=0;i<argc;i++) {
- if (!is_defined(self, node->nd_head, buf))
- return 0;
- node = node->nd_next;
- }
+ // If there were any other options, add them back to the arguments:
+ if (!RHASH_EMPTY_P(options)) argv[argc++] = options;
+ }
}
}
- else if (!is_defined(self, node, buf)) {
- return 0;
- }
- return type;
-}
-
-static char*
-is_defined(self, node, buf)
- VALUE self;
- NODE *node; /* OK */
- char *buf;
-{
- VALUE val; /* OK */
- int state;
- switch (nd_type(node)) {
- case NODE_SUPER:
- case NODE_ZSUPER:
- if (the_frame->last_func == 0) return 0;
- else if (method_boundp(RCLASS(the_frame->last_class)->super,
- the_frame->last_func, 1)) {
- if (nd_type(node) == NODE_SUPER) {
- return arg_defined(self, node->nd_args, buf, "super");
- }
- return "super";
- }
- break;
-
- case NODE_FCALL:
- case NODE_VCALL:
- val = CLASS_OF(self);
- goto check_bound;
-
- case NODE_CALL:
- if (!is_defined(self, node->nd_recv, buf)) return 0;
- PUSH_TAG(PROT_NONE);
- 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 arg_defined(self, node->nd_args, buf, "method");
- }
- break;
-
- case NODE_MATCH2:
- case NODE_MATCH3:
- return "method";
-
- case NODE_YIELD:
- if (iterator_p()) {
- return "yield";
- }
- break;
-
- case NODE_SELF:
- return "self";
-
- case NODE_NIL:
- return "nil";
-
- case NODE_TRUE:
- return "true";
-
- case NODE_FALSE:
- return "false";
-
- 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(PROT_NONE);
- 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(PROT_NONE);
- if ((state = EXEC_TAG()) == 0) {
- rb_eval(self, node);
- }
- POP_TAG();
- if (!state) {
- return "expression";
- }
- break;
- }
- return 0;
+ return argc;
}
-static int handle_rescue();
-VALUE rb_yield_0();
-
-static void blk_free();
-
-static VALUE
-set_trace_func(obj, trace)
- VALUE obj, trace;
-{
- if (NIL_P(trace)) {
- trace_func = 0;
- return Qnil;
- }
- if (TYPE(trace) != T_DATA || RDATA(trace)->dfree != blk_free) {
- TypeError("trace_func needs to be Proc");
- }
- return trace_func = trace;
-}
-
-static void
-call_trace_func(event, file, line, self, id)
- char *event;
- char *file;
- int line;
- VALUE self;
- ID id;
+/**
+ * Complete exception setup for cross-context raises (Thread#raise, Fiber#raise).
+ * Handles keyword extraction, validation, exception creation, and cause assignment.
+ *
+ * @param[in] argc Number of arguments
+ * @param[in] argv Argument array (will be modified for keyword extraction)
+ * @return Prepared exception object with cause applied
+ */
+VALUE
+rb_exception_setup(int argc, VALUE *argv)
{
- int state;
- volatile VALUE trace;
- struct FRAME *prev;
+ rb_execution_context_t *ec = GET_EC();
- if (!trace_func) return;
-
- trace = trace_func;
- trace_func = 0;
-#ifdef THREAD
- thread_critical++;
-#endif
+ // Extract cause keyword argument:
+ VALUE cause = Qundef;
+ argc = extract_raise_options(argc, argv, &cause);
- 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(PROT_NONE);
- 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));
+ // Validate cause-only case:
+ if (argc == 0 && !UNDEF_P(cause)) {
+ rb_raise(rb_eArgError, "only cause is given with no arguments");
}
- POP_TAG();
- POP_FRAME();
-#ifdef THREAD
- thread_critical--;
-#endif
- if (!trace_func) trace_func = trace;
- if (state) JUMP_TAG(state);
-}
-
-static void return_value _((VALUE val));
-static VALUE
-rb_eval(self, node)
- VALUE self;
- NODE * volatile node;
-{
- int state;
- volatile VALUE result = Qnil;
+ // Create exception:
+ VALUE exception;
+ if (argc == 0) {
+ exception = rb_exc_new(rb_eRuntimeError, 0, 0);
+ }
+ else {
+ exception = rb_make_exception(argc, argv);
+ }
-#define RETURN(v) { result = (v); goto finish; }
-
- again:
- if (!node) RETURN(Qnil);
-
- switch (nd_type(node)) {
- case NODE_BLOCK:
- while (node) {
- result = rb_eval(self, node->nd_head);
- node = node->nd_next;
- }
- break;
-
- case NODE_POSTEXE:
- f_END();
- nd_set_type(node, NODE_NIL); /* exec just once */
- result = Qnil;
- 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(literal match) */
- case NODE_MATCH2:
- result = reg_match(rb_eval(self,node->nd_recv),
- rb_eval(self,node->nd_value));
- break;
-
- /* nodes for speed-up(literal match) */
- case NODE_MATCH3:
- {
- VALUE r = rb_eval(self,node->nd_recv);
- VALUE l = rb_eval(self,node->nd_value);
- if (TYPE(r) == T_STRING) {
- result = reg_match(l, r);
- }
- else {
- result = rb_funcall(r, match, 1, l);
- }
- }
- 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_TRUE:
- RETURN(TRUE);
-
- case NODE_FALSE:
- RETURN(FALSE);
-
- case NODE_IF:
- sourceline = nd_line(node);
- 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->nd_file, nd_line(tag),
- self, the_frame->last_func);
- }
- sourceline = nd_line(tag);
- 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(PROT_NONE);
- switch (state = EXEC_TAG()) {
- case 0:
- sourceline = nd_line(node);
- 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)));
- break;
-
- 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(PROT_NONE);
- switch (state = EXEC_TAG()) {
- case 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)));
- break;
-
- 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:
- {
- iter_retry:
- PUSH_BLOCK(node->nd_var, node->nd_body);
- PUSH_TAG(PROT_FUNC);
-
- 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;
- char *file = sourcefile;
- int line = sourceline;
-
- recv = rb_eval(self, node->nd_iter);
- PUSH_ITER(ITER_PRE);
- sourcefile = file;
- sourceline = line;
- result = rb_call(CLASS_OF(recv),recv,each,0,0,0);
- POP_ITER();
- }
- }
- else if (the_block->tag->dst == state) {
- state &= TAG_MASK;
- if (state == TAG_RETURN) {
- result = prot_tag->tag_retval;
- }
- }
- POP_TAG();
- POP_BLOCK();
- switch (state) {
- case 0:
- break;
-
- case TAG_RETRY:
- goto iter_retry;
-
- case TAG_BREAK:
- result = Qnil;
- break;
- case TAG_RETURN:
- return_value(result);
- /* fall through */
- default:
- JUMP_TAG(state);
- }
- }
- break;
-
- case NODE_BREAK:
- JUMP_TAG(TAG_BREAK);
- break;
-
- case NODE_NEXT:
- JUMP_TAG(TAG_NEXT);
- break;
-
- case NODE_REDO:
- JUMP_TAG(TAG_REDO);
- break;
-
- case NODE_RETRY:
- JUMP_TAG(TAG_RETRY);
- 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(PROT_NONE);
- if ((state = EXEC_TAG()) == 0) {
- result = rb_eval(self, node->nd_head);
- }
- POP_TAG();
- if (state == TAG_RAISE) {
- NODE * volatile resq = node->nd_resq;
- while (resq) {
- if (handle_rescue(self, resq)) {
- state = 0;
- PUSH_TAG(PROT_NONE);
- 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 == TAG_RETRY) {
- state = 0;
- goto retry_entry;
- }
- break;
- }
- resq = resq->nd_head; /* next rescue */
- }
- }
- if (state) {
- JUMP_TAG(state);
- }
- }
- break;
+ VALUE resolved_cause = Qnil;
- case NODE_ENSURE:
- PUSH_TAG(PROT_NONE);
- 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:
- if (node->nd_stts) {
- return_value(rb_eval(self, node->nd_stts));
- }
- JUMP_TAG(TAG_RETURN);
- 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(RCLASS(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(PROT_NONE);
- 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 (RTEST(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++;
- eval_tree = 0;
- 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("discarding old %s", rb_id2name(node->nd_mid));
- }
- rb_clear_cache_by_id(node->nd_mid);
- }
-
- if (FL_TEST(the_scope,SCOPE_PRIVATE)) {
- noex = NOEX_PRIVATE;
- }
- else {
- noex = NOEX_PUBLIC;
- }
- rb_add_method(the_class, node->nd_mid, node->nd_defn, noex);
- if (FL_TEST(the_class, FL_SINGLETON)) {
- VALUE recv = rb_iv_get(the_class, "__attached__");
- rb_funcall(recv, rb_intern("singleton_method_added"),
- 1, INT2FIX(node->nd_mid));
- }
- else {
- rb_funcall(the_class, rb_intern("method_added"),
- 1, INT2FIX(node->nd_mid));
- }
- 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_by_id(node->nd_mid);
- rb_add_method(class, node->nd_mid, node->nd_defn, NOEX_PUBLIC);
- rb_funcall(recv, rb_intern("singleton_method_added"),
- 1, INT2FIX(node->nd_mid));
- result = Qnil;
- }
- break;
-
- case NODE_UNDEF:
- {
- VALUE 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_by_id(node->nd_mid);
- 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);
- rb_funcall(the_class, rb_intern("method_added"),
- 1, INT2FIX(node->nd_mid));
- result = Qnil;
- break;
-
- case NODE_VALIAS:
- rb_alias_variable(node->nd_new, node->nd_old);
- result = Qnil;
- break;
-
- case NODE_CLASS:
- {
- VALUE super, class, 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 != super) {
- TypeError("superclass mismatch for %s",
- rb_id2name(node->nd_cname));
- }
- }
- if (safe_level >= 4) {
- Raise(eSecurityError, "extending class prohibited");
- }
- rb_clear_cache();
- }
- 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));
- }
-
- return 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");
- }
- }
- 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->nd_head, buf);
-
- if (desc) result = str_new2(desc);
- else result = FALSE;
- }
- break;
-
- case NODE_NEWLINE:
- sourcefile = node->nd_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;
+ // Resolve cause with validation:
+ if (UNDEF_P(cause)) {
+ // No explicit cause - use automatic cause chaining from calling context:
+ resolved_cause = rb_ec_get_errinfo(ec);
- default:
- Bug("unknown node type %d", nd_type(node));
+ // Prevent self-referential cause (e.g. `raise $!`):
+ if (resolved_cause == exception) {
+ resolved_cause = Qnil;
+ }
}
- finish:
- CHECK_INTS;
- return result;
-}
-
-static VALUE
-module_setup(module, node)
- VALUE module;
- NODE * volatile node;
-{
- int state;
- VALUE save = the_frame->cbase;
- VALUE result; /* OK */
- char *file = sourcefile;
- int line = sourceline;
-
- /* fill c-ref */
- node->nd_clss = module;
- node = node->nd_body;
-
- PUSH_CLASS();
- the_class = 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 if (NIL_P(cause)) {
+ // Explicit nil cause - prevent chaining:
+ resolved_cause = Qnil;
}
else {
- the_scope->local_vars = 0;
- the_scope->local_tbl = 0;
- }
+ // Explicit cause - validate and assign:
+ if (!rb_obj_is_kind_of(cause, rb_eException)) {
+ rb_raise(rb_eTypeError, "exception object expected");
+ }
- PUSH_TAG(PROT_NONE);
- if ((state = EXEC_TAG()) == 0) {
- if (trace_func) {
- call_trace_func("class", file, line,
- the_class, the_frame->last_func);
- }
- result = rb_eval((VALUE)the_class, node->nd_body);
+ if (cause == exception) {
+ // Prevent self-referential cause (e.g. `raise error, cause: error`) - although I'm not sure this is good behaviour, it's inherited from `Kernel#raise`.
+ resolved_cause = Qnil;
+ }
+ else {
+ // Check for circular causes:
+ VALUE current_cause = cause;
+ while (!NIL_P(current_cause)) {
+ // We guarantee that the cause chain is always terminated. Then, creating an exception with an existing cause is not circular as long as exception is not an existing cause of any other exception.
+ if (current_cause == exception) {
+ rb_raise(rb_eArgError, "circular causes");
+ }
+ if (THROW_DATA_P(current_cause)) {
+ break;
+ }
+ current_cause = rb_attr_get(current_cause, id_cause);
+ }
+ resolved_cause = cause;
+ }
}
- POP_TAG();
- POP_SCOPE();
- POP_CLASS();
- the_frame->cbase = save;
- if (trace_func) {
- call_trace_func("end", file, line, 0, the_frame->last_func);
+ // Apply cause to exception object (duplicate if frozen):
+ if (!UNDEF_P(resolved_cause)) {
+ if (OBJ_FROZEN(exception)) {
+ exception = rb_obj_dup(exception);
+ }
+ rb_ivar_set(exception, id_cause, resolved_cause);
}
- if (state) JUMP_TAG(state);
- return result;
+ return exception;
}
-int
-rb_respond_to(obj, id)
- VALUE obj;
- ID id;
+VALUE
+rb_f_raise(int argc, VALUE *argv)
{
- if (rb_method_boundp(CLASS_OF(obj), id, 0)) {
- return TRUE;
- }
- return FALSE;
-}
+ VALUE cause = Qundef;
+ argc = extract_raise_options(argc, argv, &cause);
-static VALUE
-obj_respond_to(argc, argv, obj)
- int argc;
- VALUE *argv;
- VALUE obj;
-{
- VALUE mid, priv;
- ID id;
+ VALUE exception;
- 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;
-}
+ // Bare re-raise case:
+ if (argc == 0) {
+ // Cause was extracted, but no arguments were provided:
+ if (!UNDEF_P(cause)) {
+ rb_raise(rb_eArgError, "only cause is given with no arguments");
+ }
-static VALUE
-mod_method_defined(mod, mid)
- VALUE mod, mid;
-{
- if (rb_method_boundp(mod, rb_to_id(mid), 1)) {
- return TRUE;
+ // Otherwise, re-raise the current exception:
+ exception = get_errinfo();
+ if (!NIL_P(exception)) {
+ argc = 1;
+ argv = &exception;
+ }
}
- return FALSE;
-}
-void
-rb_exit(status)
- int status;
-{
- if (prot_tag) {
- exit_status = status;
- rb_raise(exc_new(eSystemExit, 0, 0));
- }
- exit(status);
-}
+ rb_raise_jump(rb_make_exception(argc, argv), cause);
+
+ UNREACHABLE_RETURN(Qnil);
+}
+
+/*
+ * call-seq:
+ * raise(exception, message = exception.to_s, backtrace = nil, cause: $!)
+ * raise(message = nil, cause: $!)
+ *
+ * Raises an exception;
+ * see {Exceptions}[rdoc-ref:exceptions.md].
+ *
+ * Argument +exception+ sets the class of the new exception;
+ * it should be class Exception or one of its subclasses
+ * (most commonly, RuntimeError or StandardError),
+ * or an instance of one of those classes:
+ *
+ * begin
+ * raise(StandardError)
+ * rescue => x
+ * p x.class
+ * end
+ * # => StandardError
+ *
+ * Argument +message+ sets the stored message in the new exception,
+ * which may be retrieved by method Exception#message;
+ * the message must be
+ * a {string-convertible object}[rdoc-ref:implicit_conversion.rdoc@String-Convertible+Objects]
+ * or +nil+:
+ *
+ * begin
+ * raise(StandardError, 'Boom')
+ * rescue => x
+ * p x.message
+ * end
+ * # => "Boom"
+ *
+ * If argument +message+ is not given,
+ * the message is the exception class name.
+ *
+ * See {Messages}[rdoc-ref:exceptions.md@Messages].
+ *
+ * Argument +backtrace+ might be used to modify the backtrace of the new exception,
+ * as reported by Exception#backtrace and Exception#backtrace_locations;
+ * the backtrace must be an array of Thread::Backtrace::Location, an array of
+ * strings, a single string, or +nil+.
+ *
+ * Using the array of Thread::Backtrace::Location instances is the most consistent option
+ * and should be preferred when possible. The necessary value might be obtained
+ * from #caller_locations, or copied from Exception#backtrace_locations of another
+ * error:
+ *
+ * begin
+ * do_some_work()
+ * rescue ZeroDivisionError => ex
+ * raise(LogicalError, "You have an error in your math", ex.backtrace_locations)
+ * end
+ *
+ * The ways, both Exception#backtrace and Exception#backtrace_locations of the
+ * raised error are set to the same backtrace.
+ *
+ * When the desired stack of locations is not available and should
+ * be constructed from scratch, an array of strings or a singular
+ * string can be used. In this case, only Exception#backtrace is set:
+ *
+ * begin
+ * raise(StandardError, 'Boom', %w[dsl.rb:3 framework.rb:1])
+ * rescue => ex
+ * p ex.backtrace
+ * # => ["dsl.rb:3", "framework.rb:1"]
+ * p ex.backtrace_locations
+ * # => nil
+ * end
+ *
+ * If argument +backtrace+ is not given,
+ * the backtrace is set according to an array of Thread::Backtrace::Location objects,
+ * as derived from the call stack.
+ *
+ * See {Backtraces}[rdoc-ref:exceptions.md@Backtraces].
+ *
+ * Keyword argument +cause+ sets the stored cause in the new exception,
+ * which may be retrieved by method Exception#cause;
+ * the cause must be an exception object (Exception or one of its subclasses),
+ * or +nil+:
+ *
+ * begin
+ * raise(StandardError, cause: RuntimeError.new)
+ * rescue => x
+ * p x.cause
+ * end
+ * # => #<RuntimeError: RuntimeError>
+ *
+ * If keyword argument +cause+ is not given,
+ * the cause is the value of <tt>$!</tt>.
+ *
+ * See {Cause}[rdoc-ref:exceptions.md@Cause].
+ *
+ * In the alternate calling sequence,
+ * where argument +exception+ _not_ given,
+ * raises a new exception of the class given by <tt>$!</tt>,
+ * or of class RuntimeError if <tt>$!</tt> is +nil+:
+ *
+ * begin
+ * raise
+ * rescue => x
+ * p x
+ * end
+ * # => RuntimeError
+ *
+ * With argument +exception+ not given,
+ * argument +message+ and keyword argument +cause+ may be given,
+ * but argument +backtrace+ may not be given.
+ *
+ * +cause+ can not be given as an only argument.
+ *
+ */
+
+static VALUE
+f_raise(int c, VALUE *v, VALUE _)
+{
+ return rb_f_raise(c, v);
+}
+
+static VALUE
+make_exception(int argc, const VALUE *argv, int isstr)
+{
+ VALUE mesg, exc;
-static VALUE
-f_exit(argc, argv, obj)
- int argc;
- VALUE *argv;
- VALUE obj;
-{
- VALUE status;
+ mesg = Qnil;
+ switch (argc) {
+ case 0:
+ return Qnil;
+ case 1:
+ exc = argv[0];
+ if (isstr &&! NIL_P(exc)) {
+ mesg = rb_check_string_type(exc);
+ if (!NIL_P(mesg)) {
+ return rb_exc_new3(rb_eRuntimeError, mesg);
+ }
+ }
- rb_secure(2);
- if (rb_scan_args(argc, argv, "01", &status) == 1) {
- status = NUM2INT(status);
+ case 2:
+ case 3:
+ break;
+ default:
+ rb_error_arity(argc, 0, 3);
}
- else {
- status = 0;
+ if (NIL_P(mesg)) {
+ mesg = rb_check_funcall(argv[0], idException, argc != 1, &argv[1]);
}
- rb_exit(status);
- /* not reached */
-}
-
-static VALUE
-f_abort()
-{
- rb_secure(2);
- if (errinfo) {
- error_print();
+ if (UNDEF_P(mesg)) {
+ rb_raise(rb_eTypeError, "exception class/object expected");
+ }
+ if (!rb_obj_is_kind_of(mesg, rb_eException)) {
+ rb_raise(rb_eTypeError, "exception object expected");
+ }
+ if (argc == 3) {
+ set_backtrace(mesg, argv[2]);
}
- rb_exit(1);
- /* not reached */
-}
-void
-rb_iter_break()
-{
- JUMP_TAG(TAG_BREAK);
+ return mesg;
}
-#ifdef __GNUC__
-static volatile voidfn rb_longjmp;
-#endif
-
-static VALUE make_backtrace();
-
-static VALUE
-check_errat(val)
- VALUE val;
+VALUE
+rb_make_exception(int argc, const VALUE *argv)
{
- int i;
- static char *err = "value of $@ must be Array of String";
-
- if (!NIL_P(val)) {
- int t = TYPE(val);
-
- if (t == T_STRING) return ary_new3(1, val);
- if (t != T_ARRAY) {
- TypeError(err);
- }
- for (i=0;i<RARRAY(val)->len;i++) {
- if (TYPE(RARRAY(val)->ptr[i]) != T_STRING) {
- TypeError(err);
- }
- }
- }
- return val;
+ return make_exception(argc, argv, TRUE);
}
+/*! \private
+ */
static void
-rb_longjmp(tag, mesg, at)
- int tag;
- VALUE mesg, at;
+rb_raise_jump(VALUE mesg, VALUE cause)
{
- if (NIL_P(errinfo) && NIL_P(mesg)) {
- errinfo = exc_new(eRuntimeError, 0, 0);
- }
+ rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *cfp = ec->cfp;
+ const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
+ VALUE klass = me->owner;
+ VALUE self = cfp->self;
+ ID mid = me->called_id;
- if (!NIL_P(at)) {
- errat = check_errat(at);
- }
- else 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);
- }
+ rb_vm_pop_frame(ec);
+ EXEC_EVENT_HOOK(ec, RUBY_EVENT_C_RETURN, self, me->def->original_id, mid, klass, Qnil);
- JUMP_TAG(tag);
-}
-
-void
-rb_raise(mesg)
- VALUE mesg;
-{
- rb_longjmp(TAG_RAISE, mesg, Qnil);
+ rb_longjmp(ec, TAG_RAISE, mesg, cause);
}
void
-rb_fatal(mesg)
- VALUE mesg;
+rb_jump_tag(int tag)
{
- rb_longjmp(TAG_FATAL, mesg, Qnil);
-}
-
-void
-rb_interrupt()
-{
- Raise(eInterrupt, "");
-}
-
-static VALUE
-f_raise(argc, argv)
- int argc;
- VALUE *argv;
-{
- VALUE arg1, arg2, arg3;
- VALUE etype, mesg;
- int n;
-
- etype = eRuntimeError;
- mesg = Qnil;
- switch (n = rb_scan_args(argc, argv, "03", &arg1, &arg2, &arg3)) {
- case 1:
- mesg = arg1;
- break;
- case 2:
- case 3:
- 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);
- }
+ if (UNLIKELY(tag < TAG_RETURN || tag > TAG_FATAL)) {
+ unknown_longjmp_status(tag);
}
-
- PUSH_FRAME(); /* fake frame */
- *the_frame = *_frame->prev->prev;
- rb_longjmp(TAG_RAISE, mesg, arg3);
- POP_FRAME();
+ EC_JUMP_TAG(GET_EC(), tag);
}
int
-iterator_p()
+rb_block_given_p(void)
{
- 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;
- volatile VALUE result = Qnil;
- struct BLOCK *block;
- struct SCOPE *old_scope;
- struct FRAME frame;
- int state;
- static USHORT serial = 1;
-
- 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(PROT_NONE);
- if ((state = EXEC_TAG()) == 0) {
- redo:
- if (!node) {
- result = Qnil;
- }
- else if (nd_type(node) == NODE_CFUNC) {
- result = (*node->nd_cfnc)(val, node->nd_tval, self);
- }
- else {
- result = rb_eval(self, node);
- }
+ if (rb_vm_frame_block_handler(GET_EC()->cfp) == VM_BLOCK_HANDLER_NONE) {
+ return FALSE;
}
else {
- switch (state) {
- case TAG_REDO:
- state = 0;
- goto redo;
- case TAG_NEXT:
- state = 0;
- result = Qnil;
- break;
- case TAG_BREAK:
- case TAG_RETURN:
- state |= (serial++ << 8);
- state |= 0x10;
- block->tag->dst = state;
- break;
- default:
- break;
- }
+ return TRUE;
}
- 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);
-}
+int rb_vm_cframe_keyword_p(const rb_control_frame_t *cfp);
-static VALUE
-f_loop()
+int
+rb_keyword_given_p(void)
{
- for (;;) { rb_yield(Qnil); }
+ return rb_vm_cframe_keyword_p(GET_EC()->cfp);
}
-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_Array(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;
-}
+VALUE rb_eThreadError;
-static void
-assign(self, lhs, val)
- VALUE self;
- NODE *lhs;
- VALUE val;
+void
+rb_need_block(void)
{
- 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;
+ if (!rb_block_given_p()) {
+ rb_vm_localjump_error("no block given", Qnil, 0);
}
}
VALUE
-rb_iterate(it_proc, data1, bl_proc, data2)
- VALUE (*it_proc)(), (*bl_proc)();
- VALUE data1, data2;
+rb_rescue2(VALUE (* b_proc) (VALUE), VALUE data1,
+ VALUE (* r_proc) (VALUE, VALUE), VALUE data2, ...)
{
- int state;
- volatile VALUE retval = Qnil;
- NODE *node = NEW_CFUNC(bl_proc, data2);
- VALUE self = TopSelf;
-
- iter_retry:
- PUSH_ITER(ITER_PRE);
- PUSH_BLOCK(0, node);
- PUSH_TAG(PROT_NONE);
-
- state = EXEC_TAG();
- if (state == 0) {
- retval = (*it_proc)(data1);
- }
- if (the_block->tag->dst == state) {
- state &= TAG_MASK;
- if (state == TAG_RETURN) {
- retval = prot_tag->tag_retval;
- }
- }
- POP_TAG();
- POP_BLOCK();
- POP_ITER();
-
- switch (state) {
- case 0:
- break;
-
- case TAG_RETRY:
- goto iter_retry;
-
- case TAG_BREAK:
- retval = Qnil;
- break;
-
- case TAG_RETURN:
- return_value(retval);
- /* 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;
+ va_list ap;
+ va_start(ap, data2);
+ VALUE ret = rb_vrescue2(b_proc, data1, r_proc, data2, ap);
+ va_end(ap);
+ return ret;
}
VALUE
-rb_rescue(b_proc, data1, r_proc, data2)
- VALUE (*b_proc)(), (*r_proc)();
- VALUE data1, data2;
-{
- int state;
- volatile VALUE result;
-
- PUSH_TAG(PROT_NONE);
- if ((state = EXEC_TAG()) == 0) {
+rb_vrescue2(VALUE (* b_proc) (VALUE), VALUE data1,
+ VALUE (* r_proc) (VALUE, VALUE), VALUE data2,
+ va_list args)
+{
+ enum ruby_tag_type state;
+ rb_execution_context_t * volatile ec = GET_EC();
+ rb_control_frame_t *volatile cfp = ec->cfp;
+ volatile VALUE result = Qfalse;
+ volatile VALUE e_info = ec->errinfo;
+
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
retry_entry:
- result = (*b_proc)(data1);
+ result = (*b_proc) (data1);
+ }
+ else if (result) {
+ /* escape from r_proc */
+ if (state == TAG_RETRY) {
+ state = TAG_NONE;
+ ec->errinfo = Qnil;
+ result = Qfalse;
+ goto retry_entry;
+ }
}
- else if (state == TAG_RAISE && obj_is_kind_of(errinfo, eException)) {
- if (r_proc) {
- PUSH_TAG(PROT_NONE);
- if ((state = EXEC_TAG()) == 0) {
- result = (*r_proc)(data2, errinfo);
- }
- POP_TAG();
- if (state == TAG_RETRY) {
- state = 0;
- goto retry_entry;
- }
- }
- else {
- result = Qnil;
- state = 0;
- }
- if (state == 0) {
- errat = Qnil;
- }
+ else {
+ rb_vm_rewind_cfp(ec, cfp);
+
+ if (state == TAG_RAISE) {
+ int handle = FALSE;
+ VALUE eclass;
+ va_list ap;
+
+ result = Qnil;
+ /* reuses args when raised again after retrying in r_proc */
+ va_copy(ap, args);
+ while ((eclass = va_arg(ap, VALUE)) != 0) {
+ if (rb_obj_is_kind_of(ec->errinfo, eclass)) {
+ handle = TRUE;
+ break;
+ }
+ }
+ va_end(ap);
+
+ if (handle) {
+ state = TAG_NONE;
+ if (r_proc) {
+ result = (*r_proc) (data2, ec->errinfo);
+ }
+ ec->errinfo = e_info;
+ }
+ }
}
- POP_TAG();
- if (state) JUMP_TAG(state);
+ EC_POP_TAG();
+ if (state)
+ EC_JUMP_TAG(ec, state);
return result;
}
VALUE
-rb_ensure(b_proc, data1, e_proc, data2)
- VALUE (*b_proc)();
- void (*e_proc)();
- VALUE data1, data2;
-{
- int state;
- volatile VALUE result = Qnil;
-
- PUSH_TAG(PROT_NONE);
- 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;
+rb_rescue(VALUE (* b_proc)(VALUE), VALUE data1,
+ VALUE (* r_proc)(VALUE, VALUE), VALUE data2)
{
- 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";
- }
- else if (iterator_p()) {
- format = "undefined iterator `%s' for %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";
- }
- }
- if (!format) {
- format = "undefined method `%s' for %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:"");
- POP_FRAME();
-
- return Qnil; /* not reached */
+ return rb_rescue2(b_proc, data1, r_proc, data2, rb_eStandardError,
+ (VALUE)0);
}
-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)
- VALUE class, recv;
- ID mid;
- int argc; /* OK */
- VALUE *argv; /* OK */
- int scope;
+VALUE
+rb_protect(VALUE (* proc) (VALUE), VALUE data, int *pstate)
{
- 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);
+ volatile enum ruby_tag_type state;
+ rb_execution_context_t * volatile ec = GET_EC();
+ rb_control_frame_t *volatile cfp = ec->cfp;
- switch (the_iter->iter) {
- case ITER_PRE:
- itr = ITER_CUR;
- break;
- case ITER_CUR:
- default:
- itr = ITER_NOT;
- break;
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ result = (*proc)(data);
}
-
- type = nd_type(body);
- if (type == NODE_ZSUPER) {
- /* for re-scoped/renamed method */
- mid = id;
- if (scope == 0) scope = 1;
- if (RCLASS(class)->super == 0) {
- /* origin is the Module, so need to scan superclass hierarchy. */
- struct RClass *cl = RCLASS(class);
-
- class = RBASIC(recv)->class;
- while (class) {
- if (RCLASS(class)->m_tbl == cl->m_tbl)
- break;
- class = RCLASS(class)->super;
- }
- }
- else {
- class = RCLASS(class)->super;
- }
- goto again;
+ else {
+ rb_vm_rewind_cfp(ec, cfp);
}
+ EC_POP_TAG();
- 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:
- {
- int state;
- VALUE *local_vars; /* OK */
-
- 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(PROT_FUNC);
- 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->nd_file, nd_line(b2),
- recv, the_frame->last_func);
- }
- result = rb_eval(recv, body);
- }
- else if (state == TAG_RETURN) {
- result = prot_tag->tag_retval;
- state = 0;
- }
- 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);
- }
- switch (state) {
- case 0:
- 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:
- if (!iterator_p()) {
- Raise(eLocalJumpError, "retry outside of rescue clause");
- }
- default:
- JUMP_TAG(state);
- }
- }
- }
- POP_FRAME();
- POP_ITER();
+ if (pstate != NULL) *pstate = state;
return result;
}
VALUE
-rb_apply(recv, mid, args)
- VALUE recv;
- ID mid;
- VALUE args;
-{
- 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;
+rb_ec_ensure(rb_execution_context_t *ec, VALUE (*b_proc)(VALUE), VALUE data1, VALUE (*e_proc)(VALUE), VALUE data2)
{
- 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);
+ enum ruby_tag_type state;
+ volatile VALUE result = Qnil;
+ VALUE errinfo;
+ EC_PUSH_TAG(ec);
+ if ((state = EC_EXEC_TAG()) == TAG_NONE) {
+ result = (*b_proc) (data1);
}
- else {
- mid = NUM2INT(vid);
+ EC_POP_TAG();
+ errinfo = ec->errinfo;
+ if (!NIL_P(errinfo) && !RB_TYPE_P(errinfo, T_OBJECT)) {
+ ec->errinfo = Qnil;
}
- PUSH_ITER(iterator_p()?ITER_PRE:ITER_NOT);
- vid = rb_call(CLASS_OF(recv), recv, mid, argc, argv, 1);
- POP_ITER();
-
- return vid;
+ (*e_proc)(data2);
+ ec->errinfo = errinfo;
+ if (state)
+ EC_JUMP_TAG(ec, state);
+ return result;
}
-#include <varargs.h>
-
VALUE
-rb_funcall(recv, mid, n, va_alist)
- VALUE recv;
- ID mid;
- int n;
- va_dcl
+rb_ensure(VALUE (*b_proc)(VALUE), VALUE data1, VALUE (*e_proc)(VALUE), VALUE data2)
{
- 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);
+ return rb_ec_ensure(GET_EC(), b_proc, data1, e_proc, data2);
}
-VALUE
-rb_funcall2(recv, mid, argc, argv)
- VALUE recv;
- ID mid;
- int argc;
- VALUE *argv;
+static ID
+frame_func_id(const rb_control_frame_t *cfp)
{
- return rb_call(CLASS_OF(recv), recv, mid, argc, argv, 1);
-}
+ const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
-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));
+ if (me) {
+ return me->def->original_id;
}
else {
- while (lev-- > 0) {
- frame = frame->prev;
- if (!frame) return Qnil;
- }
+ return 0;
}
- 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()
+static ID
+frame_called_id(rb_control_frame_t *cfp)
{
- int i, lev;
- VALUE ary;
+ const rb_callable_method_entry_t *me = rb_vm_frame_method_entry(cfp);
- 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)
- VALUE src;
-{
- NODE *node;
-
- Check_Type(src, T_STRING);
-
- node = compile_string(sourcefile, RSTRING(src)->ptr, RSTRING(src)->len);
-
- if (nerrs == 0) return node;
- return 0;
-}
-
-static VALUE
-eval(self, src, scope)
- VALUE self, src, scope;
-{
- struct BLOCK *data;
- volatile VALUE result = Qnil;
- struct SCOPE * volatile old_scope;
- struct BLOCK * volatile old_block;
- struct RVarmap * volatile old_d_vars;
- struct FRAME frame;
- char *file = sourcefile;
- int line = sourceline;
- volatile int iter = the_frame->iter;
- int state;
-
- if (!NIL_P(scope)) {
- if (TYPE(scope) != T_DATA || RDATA(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 = the_scope;
- the_scope = data->scope;
- old_block = the_block;
- the_block = data->prev;
- old_d_vars = the_dyna_vars;
- the_dyna_vars = data->d_vars;
-
- self = data->self;
- the_frame->iter = data->iter;
+ if (me) {
+ return me->called_id;
}
else {
- if (the_frame->prev) {
- the_frame->iter = the_frame->prev->iter;
- }
- }
- PUSH_CLASS();
- the_class = ((NODE*)the_frame->cbase)->nd_clss;
-
- rb_in_eval++;
- if (TYPE(the_class) == T_ICLASS) {
- the_class = RBASIC(the_class)->class;
- }
- PUSH_TAG(PROT_NONE);
- if ((state = EXEC_TAG()) == 0) {
- eval_tree = 0;
- compile(src);
- if (nerrs > 0) {
- compile_error(0);
- }
- result = eval_node(self);
+ return 0;
}
- POP_TAG();
- POP_CLASS();
- rb_in_eval--;
- if (!NIL_P(scope)) {
- the_frame = the_frame->prev;
- the_scope = old_scope;
- the_block = old_block;
- the_dyna_vars = old_d_vars;
- }
- else {
- the_frame->iter = iter;
- }
- if (state) {
- VALUE err;
-
- if (state == TAG_RAISE) {
- sourcefile = file;
- sourceline = line;
- if (strcmp(sourcefile, "(eval)") == 0) {
- err = str_dup(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;
+ID
+rb_frame_this_func(void)
{
- VALUE src, scope;
-
- rb_scan_args(argc, argv, "11", &src, &scope);
-
- Check_SafeStr(src);
- return eval(self, src, scope);
+ return frame_func_id(GET_EC()->cfp);
}
-static VALUE
-eval_under(under, self, src)
- VALUE under, self, src;
+ID
+rb_frame_callee(void)
{
- VALUE val; /* OK */
- int state;
- VALUE cbase = the_frame->cbase;
-
- PUSH_CLASS();
- the_class = under;
- PUSH_FRAME();
- the_frame->last_func = _frame->last_func;
- the_frame->last_class = _frame->last_class;
- the_frame->argc = 1;
- the_frame->argv = &src;
- the_frame->cbase = (VALUE)node_newnode(NODE_CREF,under,0,cbase);
- PUSH_TAG(PROT_NONE);
- if ((state = EXEC_TAG()) == 0) {
- val = eval(self, src, Qnil);
- }
- POP_TAG();
- POP_FRAME();
- POP_CLASS();
- if (state) JUMP_TAG(state);
-
- return val;
+ return frame_called_id(GET_EC()->cfp);
}
-static VALUE
-obj_instance_eval(self, src)
- VALUE self, src;
+static rb_control_frame_t *
+previous_frame(const rb_execution_context_t *ec)
{
- return eval_under(CLASS_OF(self), self, src);
+ rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(ec->cfp);
+ /* check if prev_cfp can be accessible */
+ if ((void *)(ec->vm_stack + ec->vm_stack_size) == (void *)(prev_cfp)) {
+ return 0;
+ }
+ return prev_cfp;
}
-static VALUE
-mod_module_eval(mod, src)
- VALUE mod, src;
+static ID
+prev_frame_callee(void)
{
- return eval_under(mod, mod, src);
+ rb_control_frame_t *prev_cfp = previous_frame(GET_EC());
+ if (!prev_cfp) return 0;
+ return frame_called_id(prev_cfp);
}
-VALUE rb_load_path;
-
-char *dln_find_file();
-
-static char*
-find_file(file)
- char *file;
+static ID
+prev_frame_func(void)
{
- 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);
+ rb_control_frame_t *prev_cfp = previous_frame(GET_EC());
+ if (!prev_cfp) return 0;
+ return frame_func_id(prev_cfp);
}
-VALUE
-f_load(obj, fname)
- VALUE obj, fname;
+/*!
+ * \private
+ * Returns the ID of the last method in the call stack.
+ * \sa rb_frame_this_func
+ * \ingroup defmethod
+ */
+ID
+rb_frame_last_func(void)
{
- int state;
- char *file;
- volatile ID last_func;
-
- Check_SafeStr(fname);
- if (RSTRING(fname)->ptr[0] == '~') {
- fname = file_s_expand_path(0, fname);
- }
- file = find_file(RSTRING(fname)->ptr);
- if (!file) LoadError("No such file to load -- %s", RSTRING(fname)->ptr);
-
- PUSH_TAG(PROT_NONE);
- PUSH_CLASS();
- the_class = 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;
- }
- /* default visibility is private at loading toplevel */
- FL_SET(the_scope, SCOPE_PRIVATE);
-
- 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);
+ const rb_execution_context_t *ec = GET_EC();
+ const rb_control_frame_t *cfp = ec->cfp;
+ ID mid;
- return TRUE;
+ while (!(mid = frame_func_id(cfp)) &&
+ (cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp),
+ !RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(ec, cfp)));
+ return mid;
}
-static VALUE rb_features;
+/*
+ * 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 int
-rb_provided(feature)
- char *feature;
+static VALUE
+rb_mod_append_features(VALUE module, VALUE include)
{
- 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++;
+ if (!CLASS_OR_MODULE_P(include)) {
+ Check_Type(include, T_CLASS);
}
- return FALSE;
-}
-
-#ifdef THREAD
-static int thread_loading();
-static void thread_loading_done();
-#endif
+ rb_include_module(include, module);
-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));
- }
+ return module;
}
-VALUE
-f_require(obj, fname)
- VALUE obj, fname;
-{
- char *ext, *file, *feature, *buf; /* OK */
- VALUE load;
-
- Check_SafeStr(fname);
- if (rb_provided(RSTRING(fname)->ptr))
- return FALSE;
-
- ext = strrchr(RSTRING(fname)->ptr, '.');
- if (ext) {
- if (strcmp(".rb", ext) == 0) {
- feature = file = RSTRING(fname)->ptr;
- file = find_file(file);
- if (file) goto rb_load;
- }
- else if (strcmp(".o", ext) == 0) {
- file = feature = RSTRING(fname)->ptr;
- if (strcmp(".o", DLEXT) != 0) {
- buf = ALLOCA_N(char, strlen(file)+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 = RSTRING(fname)->ptr;
- file = find_file(feature);
- if (file) goto dyna_load;
- }
- }
- buf = ALLOCA_N(char, strlen(RSTRING(fname)->ptr) + 5);
- sprintf(buf, "%s.rb", RSTRING(fname)->ptr);
- file = find_file(buf);
- if (file) {
- fname = str_new2(file);
- feature = buf;
- goto rb_load;
- }
- sprintf(buf, "%s%s", RSTRING(fname)->ptr, DLEXT);
- file = find_file(buf);
- if (file) {
- feature = buf;
- goto dyna_load;
- }
- LoadError("No such file to load -- %s", RSTRING(fname)->ptr);
-
- dyna_load:
-#ifdef THREAD
- if (thread_loading(feature)) return FALSE;
- else {
- int state;
- PUSH_TAG(PROT_NONE);
- 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;
+static VALUE refinement_import_methods(int argc, VALUE *argv, VALUE refinement);
- rb_load:
-#ifdef THREAD
- if (thread_loading(feature)) return FALSE;
- else {
- int state;
- PUSH_TAG(PROT_NONE);
- 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;
-}
+/*
+ * call-seq:
+ * include(module, ...) -> self
+ *
+ * Invokes Module.append_features on each parameter in reverse order.
+ */
-static void
-set_method_visibility(self, argc, argv, ex)
- VALUE self;
- int argc;
- VALUE *argv;
- int ex;
+static VALUE
+rb_mod_include(int argc, VALUE *argv, VALUE module)
{
int i;
+ ID id_append_features, id_included;
- for (i=0; i<argc; i++) {
- rb_export_method(self, rb_to_id(argv[i]), ex);
- }
-}
+ CONST_ID(id_append_features, "append_features");
+ CONST_ID(id_included, "included");
-static VALUE
-mod_public(argc, argv, module)
- int argc;
- VALUE *argv;
- VALUE module;
-{
- if (argc == 0) {
- FL_UNSET(the_scope, SCOPE_PRIVATE);
+ if (BUILTIN_TYPE(module) == T_MODULE && FL_TEST(module, RMODULE_IS_REFINEMENT)) {
+ rb_raise(rb_eTypeError, "Refinement#include has been removed");
}
- else {
- set_method_visibility(module, argc, argv, NOEX_PUBLIC);
- }
- return module;
-}
-static VALUE
-mod_private(argc, argv, module)
- int argc;
- VALUE *argv;
- VALUE module;
-{
- if (argc == 0) {
- FL_SET(the_scope, SCOPE_PRIVATE);
+ rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
+ for (i = 0; i < argc; i++) {
+ Check_Type(argv[i], T_MODULE);
+ if (FL_TEST(argv[i], RMODULE_IS_REFINEMENT)) {
+ rb_raise(rb_eTypeError, "Cannot include refinement");
+ }
}
- else {
- set_method_visibility(module, argc, argv, NOEX_PRIVATE);
+ while (argc--) {
+ rb_funcall(argv[argc], id_append_features, 1, module);
+ rb_funcall(argv[argc], id_included, 1, module);
}
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;
-}
+/*
+ * call-seq:
+ * prepend_features(mod) -> mod
+ *
+ * When this module is prepended in another, Ruby calls
+ * #prepend_features in this module, passing it the receiving module
+ * in _mod_. Ruby's default implementation is to overlay 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#prepend.
+ */
static VALUE
-mod_private_method(argc, argv, obj)
- int argc;
- VALUE *argv;
- VALUE obj;
+rb_mod_prepend_features(VALUE module, VALUE prepend)
{
- set_method_visibility(CLASS_OF(obj), argc, argv, NOEX_PRIVATE);
- return obj;
-}
+ if (!CLASS_OR_MODULE_P(prepend)) {
+ Check_Type(prepend, T_CLASS);
+ }
+ rb_prepend_module(prepend, module);
-static VALUE
-top_public(argc, argv)
- int argc;
- VALUE *argv;
-{
- return mod_public(argc, argv, cObject);
+ return module;
}
-static VALUE
-top_private(argc, argv)
- int argc;
- VALUE *argv;
-{
- return mod_private(argc, argv, cObject);
-}
+/*
+ * call-seq:
+ * prepend(module, ...) -> self
+ *
+ * Invokes Module.prepend_features on each parameter in reverse order.
+ */
static VALUE
-mod_modfunc(argc, argv, module)
- int argc;
- VALUE *argv;
- VALUE module;
+rb_mod_prepend(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;
-}
+ ID id_prepend_features, id_prepended;
-static VALUE
-mod_append_features(module, include)
- VALUE module, include;
-{
- switch (TYPE(include)) {
- case T_CLASS:
- case T_MODULE:
- break;
- default:
- Check_Type(include, T_CLASS);
- break;
+ if (BUILTIN_TYPE(module) == T_MODULE && FL_TEST(module, RMODULE_IS_REFINEMENT)) {
+ rb_raise(rb_eTypeError, "Refinement#prepend has been removed");
}
- rb_include_module(include, module);
- return module;
-}
-
-static VALUE
-mod_include(argc, argv, module)
- int argc;
- VALUE *argv;
- VALUE module;
-{
- int i;
+ CONST_ID(id_prepend_features, "prepend_features");
+ CONST_ID(id_prepended, "prepended");
- for (i=0; i<argc; i++) {
- Check_Type(argv[i], T_MODULE);
- rb_funcall(argv[i], rb_intern("append_features"), 1, module);
+ rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
+ for (i = 0; i < argc; i++) {
+ Check_Type(argv[i], T_MODULE);
+ if (FL_TEST(argv[i], RMODULE_IS_REFINEMENT)) {
+ rb_raise(rb_eTypeError, "Cannot prepend refinement");
+ }
+ }
+ while (argc--) {
+ rb_funcall(argv[argc], id_prepend_features, 1, module);
+ rb_funcall(argv[argc], id_prepended, 1, module);
}
return module;
}
-VALUE
-class_new_instance(argc, argv, class)
- int argc;
- VALUE *argv;
- VALUE class;
+static void
+ensure_class_or_module(VALUE obj)
{
- VALUE obj;
-
- if (FL_TEST(class, FL_SINGLETON)) {
- TypeError("can't create instance of virtual class");
+ if (!RB_TYPE_P(obj, T_CLASS) && !RB_TYPE_P(obj, T_MODULE)) {
+ rb_raise(rb_eTypeError,
+ "wrong argument type %"PRIsVALUE" (expected Class or Module)",
+ rb_obj_class(obj));
}
- 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;
+hidden_identity_hash_new(void)
{
- rb_secure(4);
- return mod_include(argc, argv, cObject);
-}
+ VALUE hash = rb_ident_hash_new();
-void
-rb_extend_object(obj, module)
- VALUE obj, module;
-{
- rb_include_module(rb_singleton_class(obj), module);
+ RBASIC_CLEAR_CLASS(hash); /* hide from ObjectSpace */
+ return hash;
}
static VALUE
-mod_extend_object(mod, obj)
- VALUE mod, obj;
+refinement_superclass(VALUE superclass)
{
- 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);
+ if (RB_TYPE_P(superclass, T_MODULE)) {
+ /* FIXME: Should ancestors of superclass be used here? */
+ return rb_include_class_new(RCLASS_ORIGIN(superclass), rb_cBasicObject);
+ }
+ else {
+ return superclass;
}
- return obj;
}
-VALUE f_trace_var();
-VALUE f_untrace_var();
-
-extern void rb_str_setter();
-
+/*!
+ * \private
+ */
static void
-errat_setter(val, id, var)
- VALUE val;
- ID id;
- VALUE *var;
+rb_using_refinement(rb_cref_t *cref, VALUE klass, VALUE module)
{
- *var = check_errat(val);
-}
+ VALUE iclass, c, superclass = klass;
-VALUE f_global_variables();
-static VALUE f_catch();
-static VALUE f_throw();
+ ensure_class_or_module(klass);
+ Check_Type(module, T_MODULE);
+ if (NIL_P(CREF_REFINEMENTS(cref))) {
+ CREF_REFINEMENTS_SET(cref, hidden_identity_hash_new());
+ }
+ else {
+ if (CREF_OMOD_SHARED(cref)) {
+ CREF_REFINEMENTS_SET(cref, rb_hash_dup(CREF_REFINEMENTS(cref)));
+ CREF_OMOD_SHARED_UNSET(cref);
+ }
+ if (!NIL_P(c = rb_hash_lookup(CREF_REFINEMENTS(cref), klass))) {
+ superclass = c;
+ while (c && RB_TYPE_P(c, T_ICLASS)) {
+ if (RBASIC(c)->klass == module) {
+ /* already used refinement */
+ return;
+ }
+ c = RCLASS_SUPER(c);
+ }
+ }
+ }
+ superclass = refinement_superclass(superclass);
+ c = iclass = rb_include_class_new(module, superclass);
+ RCLASS_SET_REFINED_CLASS(c, klass);
-struct end_proc_data {
- void (*func)();
- VALUE data;
- struct end_proc_data *next;
-} *end_proc_data;
+ RCLASS_WRITE_M_TBL(c, RCLASS_M_TBL(module));
-void
-rb_set_end_proc(func, data)
- void (*func)();
- VALUE data;
-{
- struct end_proc_data *link = ALLOC(struct end_proc_data);
+ rb_class_subclass_add(klass, iclass);
- link->next = end_proc_data;
- link->func = func;
- link->data = data;
- rb_global_variable(&link->data);
- end_proc_data = link;
+ rb_hash_aset(CREF_REFINEMENTS(cref), klass, iclass);
}
-static void
-call_end_proc(data)
- VALUE data;
+static int
+using_refinement(VALUE klass, VALUE module, VALUE arg)
{
- proc_call(data, Qnil);
-}
+ rb_cref_t *cref = (rb_cref_t *) arg;
-static void
-f_END()
-{
- PUSH_FRAME();
- rb_set_end_proc(call_end_proc, f_lambda());
- POP_FRAME();
+ rb_using_refinement(cref, klass, module);
+ return ST_CONTINUE;
}
-static VALUE
-f_at_exit()
+static void
+using_module_recursive(const rb_cref_t *cref, VALUE klass)
{
- VALUE proc;
-
- proc = f_lambda();
+ ID id_refinements;
+ VALUE super, module, refinements;
- rb_set_end_proc(call_end_proc, proc);
- return proc;
-}
+ super = RCLASS_SUPER(klass);
+ if (super) {
+ using_module_recursive(cref, super);
+ }
+ switch (BUILTIN_TYPE(klass)) {
+ case T_MODULE:
+ module = klass;
+ break;
-static void
-exec_end_proc()
-{
- struct end_proc_data *link = end_proc_data;
+ case T_ICLASS:
+ module = RBASIC(klass)->klass;
+ break;
- while (link) {
- (*link->func)(link->data);
- link = link->next;
+ default:
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Module)",
+ rb_obj_classname(klass));
+ break;
}
+ CONST_ID(id_refinements, "__refinements__");
+ refinements = rb_attr_get(module, id_refinements);
+ if (NIL_P(refinements)) return;
+ rb_hash_foreach(refinements, using_refinement, (VALUE) cref);
}
-void
-Init_eval()
+/*!
+ * \private
+ */
+static void
+rb_using_module(const rb_cref_t *cref, VALUE module)
{
- init = rb_intern("initialize");
- eqq = rb_intern("===");
- each = rb_intern("each");
-
- aref = rb_intern("[]");
- aset = rb_intern("[]=");
- match = rb_intern("=~");
-
- rb_global_variable((VALUE*)&top_scope);
- rb_global_variable((VALUE*)&eval_tree0);
- rb_global_variable((VALUE*)&eval_tree);
- rb_global_variable((VALUE*)&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("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("at_exit", f_at_exit, 0);
-
- rb_define_global_function("catch", f_catch, 1);
- rb_define_global_function("throw", f_throw, -1);
- rb_define_global_function("global_variables", f_global_variables, 0);
-
- rb_define_method(mKernel, "send", f_send, -1);
- rb_define_method(mKernel, "instance_eval", obj_instance_eval, 1);
-
- rb_define_private_method(cModule, "append_features", mod_append_features, 1);
- rb_define_private_method(cModule, "extend_object", mod_extend_object, 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, "public_class_method", mod_public_method, -1);
- rb_define_method(cModule, "private_class_method", mod_private_method, -1);
- rb_define_method(cModule, "module_eval", mod_module_eval, 1);
-
- rb_define_method(cModule, "remove_method", mod_remove_method, -1);
- rb_define_method(cModule, "undef_method", mod_undef_method, 1);
- rb_define_method(cModule, "alias_method", mod_alias_method, 2);
-
- rb_define_singleton_method(cModule, "nesting", mod_nesting, 0);
- rb_define_singleton_method(cModule, "constants", mod_s_constants, 0);
-
- rb_define_singleton_method(TopSelf, "include", top_include, -1);
- rb_define_singleton_method(TopSelf, "public", top_public, -1);
- rb_define_singleton_method(TopSelf, "private", top_private, -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);
+ Check_Type(module, T_MODULE);
+ using_module_recursive(cref, module);
+ rb_clear_all_refinement_method_cache();
}
-VALUE f_autoload();
-
void
-Init_load()
+rb_vm_using_module(VALUE module)
+{
+ rb_using_module(rb_vm_cref_replace_with_duplicated_cref(), module);
+}
+
+/*
+ * call-seq:
+ * target -> class_or_module
+ *
+ * Return the class or module refined by the receiver.
+ *
+ * module M
+ * refine String do
+ * end
+ * end
+ *
+ * M.refinements[0].target # => String
+ */
+VALUE
+rb_refinement_module_get_refined_class(VALUE module)
{
- 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);
+ ID id_refined_class;
- rb_define_global_function("load", f_load, 1);
- rb_define_global_function("require", f_require, 1);
- rb_define_global_function("autoload", f_autoload, 2);
+ CONST_ID(id_refined_class, "__refined_class__");
+ return rb_attr_get(module, id_refined_class);
}
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;
+add_activated_refinement(VALUE activated_refinements,
+ VALUE klass, VALUE refinement)
+{
+ VALUE iclass, c, superclass = klass;
+
+ if (!NIL_P(c = rb_hash_lookup(activated_refinements, klass))) {
+ superclass = c;
+ while (c && RB_TYPE_P(c, T_ICLASS)) {
+ if (RBASIC(c)->klass == refinement) {
+ /* already used refinement */
+ return;
+ }
+ c = RCLASS_SUPER(c);
+ }
}
- else {
- scope->flag = SCOPE_NOSTACK;
+ superclass = refinement_superclass(superclass);
+ c = iclass = rb_include_class_new(refinement, superclass);
+ RCLASS_SET_REFINED_CLASS(c, klass);
+ rb_class_subclass_add(klass, iclass);
+ refinement = RCLASS_SUPER(refinement);
+ while (refinement && refinement != klass) {
+ c = rb_class_set_super(c, rb_include_class_new(refinement, RCLASS_SUPER(c)));
+ RCLASS_SET_REFINED_CLASS(c, klass);
+ rb_class_subclass_add(klass, c);
+ refinement = RCLASS_SUPER(refinement);
}
+ rb_hash_aset(activated_refinements, klass, iclass);
}
-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;
+void
+rb_refinement_setup(struct rb_refinements_data *data, VALUE module, VALUE klass)
{
- volatile VALUE proc;
- struct BLOCK *data;
+ VALUE refinement;
+ ID id_refinements, id_activated_refinements,
+ id_refined_class, id_defined_at;
+ VALUE refinements, activated_refinements;
- if (!iterator_p() && !f_iterator_p()) {
- ArgError("tryed to create Procedure-Object out of iterator");
+ CONST_ID(id_refinements, "__refinements__");
+ refinements = rb_attr_get(module, id_refinements);
+ if (NIL_P(refinements)) {
+ refinements = hidden_identity_hash_new();
+ rb_ivar_set(module, id_refinements, refinements);
}
-
- 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;
- }
+ CONST_ID(id_activated_refinements, "__activated_refinements__");
+ activated_refinements = rb_attr_get(module, id_activated_refinements);
+ if (NIL_P(activated_refinements)) {
+ activated_refinements = hidden_identity_hash_new();
+ rb_ivar_set(module, id_activated_refinements,
+ activated_refinements);
+ }
+ refinement = rb_hash_lookup(refinements, klass);
+ if (NIL_P(refinement)) {
+ VALUE superclass = refinement_superclass(klass);
+ refinement = rb_refinement_new();
+ rb_class_set_super(refinement, superclass);
+ RUBY_ASSERT(BUILTIN_TYPE(refinement) == T_MODULE);
+ FL_SET(refinement, RMODULE_IS_REFINEMENT);
+ CONST_ID(id_refined_class, "__refined_class__");
+ rb_ivar_set(refinement, id_refined_class, klass);
+ CONST_ID(id_defined_at, "__defined_at__");
+ rb_ivar_set(refinement, id_defined_at, module);
+ rb_hash_aset(refinements, klass, refinement);
+ add_activated_refinement(activated_refinements, klass, refinement);
}
- return proc;
+ data->refinement = refinement;
+ data->refinements = activated_refinements;
}
-VALUE
-f_lambda()
-{
- return proc_s_new(cProc);
-}
+/*
+ * call-seq:
+ * refine(mod) { block } -> module
+ *
+ * Refine <i>mod</i> in the receiver.
+ *
+ * Returns a module, where refined methods are defined.
+ */
static VALUE
-proc_call(proc, args)
- VALUE proc, args; /* OK */
+rb_mod_refine(VALUE module, VALUE klass)
{
- struct BLOCK *data;
- volatile VALUE result = Qnil;
- int state;
- volatile int orphan;
- volatile int safe = safe_level;
-
- if (TYPE(args) == T_ARRAY) {
- switch (RARRAY(args)->len) {
- case 0:
- args = Qnil;
- 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;
- }
- }
+ /* module is the receiver of #refine, klass is a module to be refined (`mod` in the doc) */
+ rb_thread_t *th = GET_THREAD();
+ VALUE block_handler = rb_vm_frame_block_handler(th->ec->cfp);
+ struct rb_refinements_data data;
- /* 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;
- }
+ if (block_handler == VM_BLOCK_HANDLER_NONE) {
+ rb_raise(rb_eArgError, "no block given");
}
-
- PUSH_TAG(PROT_NONE);
- state = EXEC_TAG();
- if (state == 0) {
- result = rb_yield(args);
- }
- POP_TAG();
-
- POP_ITER();
- if (the_block->tag->dst == state) {
- state &= TAG_MASK;
- }
- POP_BLOCK();
- safe_level = safe;
-
- if (state) {
- if (orphan) {/* orphan procedure */
- switch (state) {
- case TAG_BREAK:
- Raise(eLocalJumpError, "break from proc-closure");
- break;
- case TAG_RETRY:
- Raise(eLocalJumpError, "retry from proc-closure");
- break;
- case TAG_RETURN:
- Raise(eLocalJumpError, "return from proc-closure");
- break;
- }
- }
- JUMP_TAG(state);
+ if (vm_block_handler_type(block_handler) != block_handler_type_iseq) {
+ rb_raise(rb_eArgError, "can't pass a Proc as a block to Module#refine");
}
- 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;
+ ensure_class_or_module(klass);
-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 RVarmap *dyna_vars;
- struct BLOCK *block;
- struct iter *iter;
- struct tag *tag;
- VALUE class;
-
- 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;
+ rb_refinement_setup(&data, module, klass);
-#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;
+ rb_yield_refine_block(data.refinement, data.refinements);
+ return data.refinement;
}
-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;
+ignored_block(VALUE module, const char *klass)
{
- 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;
+ const char *anon = "";
+ Check_Type(module, T_MODULE);
+ if (!RTEST(rb_search_class_path(module))) {
+ anon = ", maybe for Module.new";
}
+ rb_warn("%s""using doesn't call the given block""%s.", klass, anon);
}
-void
-gc_mark_threads()
-{
- thread_t th;
-
- FOREACH_THREAD(th) {
- thread_mark(th);
- } END_FOREACH(th);
-}
+/*
+ * call-seq:
+ * using(module) -> self
+ *
+ * Import class refinements from <i>module</i> into the current class or
+ * module definition.
+ */
-static void
-thread_free(th)
- thread_t th;
+static VALUE
+mod_using(VALUE self, VALUE module)
{
- if (th->stk_ptr) free(th->stk_ptr);
- th->stk_ptr = 0;
-}
+ rb_control_frame_t *prev_cfp = previous_frame(GET_EC());
-static thread_t
-thread_check(data)
- VALUE data;
-{
- if (TYPE(data) != T_DATA || RDATA(data)->dfree != thread_free) {
- TypeError("wrong argument type %s (expected Thread)",
- rb_class2name(CLASS_OF(data)));
+ if (prev_frame_func()) {
+ rb_raise(rb_eRuntimeError,
+ "Module#using is not permitted in methods");
}
- return (thread_t)RDATA(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);
+ if (prev_cfp && prev_cfp->self != self) {
+ rb_raise(rb_eRuntimeError, "Module#using is not called on self");
}
- 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);
+ if (rb_block_given_p()) {
+ ignored_block(module, "Module#");
+ }
+ rb_using_module(rb_vm_cref_replace_with_duplicated_cref(), module);
+ return self;
}
-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;
+/*
+ * call-seq:
+ * refinements -> array
+ *
+ * Returns an array of +Refinement+ defined within the receiver.
+ *
+ * module A
+ * refine Integer do
+ * end
+ *
+ * refine String do
+ * end
+ * end
+ *
+ * p A.refinements
+ *
+ * <em>produces:</em>
+ *
+ * [#<refinement:Integer@A>, #<refinement:String@A>]
+ */
+static VALUE
+mod_refinements(VALUE self)
{
- VALUE v;
- static thread_t tmp;
- static int ex;
-
- if (!th->stk_ptr) Bug("unsaved context");
+ ID id_refinements;
+ VALUE refinements;
- 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_TAG(TAG_FATAL);
- 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);
+ CONST_ID(id_refinements, "__refinements__");
+ refinements = rb_attr_get(self, id_refinements);
+ if (NIL_P(refinements)) {
+ return rb_ary_new();
}
+ return rb_hash_values(refinements);
}
-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--;
+static int
+used_modules_i(VALUE _, VALUE mod, VALUE ary)
+{
+ ID id_defined_at;
+ CONST_ID(id_defined_at, "__defined_at__");
+ while (BUILTIN_TYPE(rb_class_of(mod)) == T_MODULE && FL_TEST(rb_class_of(mod), RMODULE_IS_REFINEMENT)) {
+ rb_ary_push(ary, rb_attr_get(rb_class_of(mod), id_defined_at));
+ mod = RCLASS_SUPER(mod);
+ }
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * used_modules -> array
+ *
+ * Returns an array of all modules used in the current scope. The ordering
+ * of modules in the resulting array is not defined.
+ *
+ * module A
+ * refine Object do
+ * end
+ * end
+ *
+ * module B
+ * refine Object do
+ * end
+ * end
+ *
+ * using A
+ * using B
+ * p Module.used_modules
+ *
+ * <em>produces:</em>
+ *
+ * [B, A]
+ */
+static VALUE
+rb_mod_s_used_modules(VALUE _)
+{
+ const rb_cref_t *cref = rb_vm_cref();
+ VALUE ary = rb_ary_new();
+
+ while (cref) {
+ if (!NIL_P(CREF_REFINEMENTS(cref))) {
+ rb_hash_foreach(CREF_REFINEMENTS(cref), used_modules_i, ary);
+ }
+ cref = CREF_NEXT(cref);
}
- 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();
+ return rb_funcall(ary, rb_intern("uniq"), 0);
}
static int
-thread_dead(th)
- thread_t th;
-{
- return th->status == THREAD_KILLED;
-}
+used_refinements_i(VALUE _, VALUE mod, VALUE ary)
+{
+ while (BUILTIN_TYPE(rb_class_of(mod)) == T_MODULE && FL_TEST(rb_class_of(mod), RMODULE_IS_REFINEMENT)) {
+ rb_ary_push(ary, rb_class_of(mod));
+ mod = RCLASS_SUPER(mod);
+ }
+ return ST_CONTINUE;
+}
+
+/*
+ * call-seq:
+ * used_refinements -> array
+ *
+ * Returns an array of all modules used in the current scope. The ordering
+ * of modules in the resulting array is not defined.
+ *
+ * module A
+ * refine Object do
+ * end
+ * end
+ *
+ * module B
+ * refine Object do
+ * end
+ * end
+ *
+ * using A
+ * using B
+ * p Module.used_refinements
+ *
+ * <em>produces:</em>
+ *
+ * [#<refinement:Object@B>, #<refinement:Object@A>]
+ */
+static VALUE
+rb_mod_s_used_refinements(VALUE _)
+{
+ const rb_cref_t *cref = rb_vm_cref();
+ VALUE ary = rb_ary_new();
+
+ while (cref) {
+ if (!NIL_P(CREF_REFINEMENTS(cref))) {
+ rb_hash_foreach(CREF_REFINEMENTS(cref), used_refinements_i, ary);
+ }
+ cref = CREF_NEXT(cref);
+ }
-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();
+ return ary;
}
-void
-thread_schedule()
-{
- thread_t next; /* OK */
- thread_t th;
- thread_t curr;
-
- select_err:
- thread_pending = 0;
- if (curr_thread == curr_thread->next) return;
+struct refinement_import_methods_arg {
+ rb_cref_t *cref;
+ VALUE refinement;
+ VALUE module;
+};
- next = 0;
- curr = curr_thread; /* starting thread */
+/* vm.c */
+rb_cref_t *rb_vm_cref_dup_without_refinements(const rb_cref_t *cref);
- while (curr->status == THREAD_KILLED) {
- curr = curr->prev;
- }
+static enum rb_id_table_iterator_result
+refinement_import_methods_i(ID key, VALUE value, void *data)
+{
+ const rb_method_entry_t *me = (const rb_method_entry_t *)value;
+ struct refinement_import_methods_arg *arg = (struct refinement_import_methods_arg *)data;
- 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) {
- 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 (me->def->type != VM_METHOD_TYPE_ISEQ) {
+ rb_raise(rb_eArgError, "Can't import method which is not defined with Ruby code: %"PRIsVALUE"#%"PRIsVALUE, rb_class_path(arg->module), rb_id2str(key));
}
+ rb_cref_t *new_cref = rb_vm_cref_dup_without_refinements(me->def->body.iseq.cref);
+ CREF_REFINEMENTS_SET(new_cref, CREF_REFINEMENTS(arg->cref));
+ rb_add_method_iseq(arg->refinement, key, me->def->body.iseq.iseqptr, new_cref, METHOD_ENTRY_VISI(me));
+ return ID_TABLE_CONTINUE;
+}
- if (num_waiting_on_fd > 0 || num_waiting_on_timer > 0) {
- fd_set readfds;
- struct timeval delay_tv, *delay_ptr;
- double delay, now; /* OK */
-
- 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);
- }
+/*
+ * Note: docs for the method are in class.c
+ */
- if (!next) {
- curr_thread->file = sourcefile;
- curr_thread->line = sourceline;
- 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;
- }
+static VALUE
+refinement_import_methods(int argc, VALUE *argv, VALUE refinement)
+{
+ int i;
+ struct refinement_import_methods_arg arg;
- /* context switch */
- if (curr == curr_thread) {
- thread_save_context(curr);
- if (setjmp(curr->context)) {
- return;
- }
+ rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
+ for (i = 0; i < argc; i++) {
+ Check_Type(argv[i], T_MODULE);
+ if (RCLASS_SUPER(argv[i])) {
+ rb_warn("%"PRIsVALUE" has ancestors, but Refinement#import_methods doesn't import their methods", rb_class_path(argv[i]));
+ }
}
-
- curr_thread = next;
- if (next->status == THREAD_TO_KILL) {
- /* execute ensure-clause if any */
- thread_restore_context(next, 1);
+ arg.cref = rb_vm_cref_replace_with_duplicated_cref();
+ arg.refinement = refinement;
+ for (i = 0; i < argc; i++) {
+ arg.module = argv[i];
+ struct rb_id_table *m_tbl = RCLASS_M_TBL(argv[i]);
+ if (!m_tbl) continue;
+ rb_id_table_foreach(m_tbl, refinement_import_methods_i, &arg);
}
- thread_restore_context(next, 0);
+ return refinement;
}
void
-thread_wait_fd(fd)
- int fd;
+rb_obj_call_init(VALUE obj, int argc, const VALUE *argv)
{
- 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();
+ rb_obj_call_init_kw(obj, argc, argv, RB_NO_KEYWORDS);
}
void
-thread_fd_writable(fd)
- int fd;
+rb_obj_call_init_kw(VALUE obj, int argc, const VALUE *argv, int kw_splat)
{
- 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();
- }
+ PASS_PASSED_BLOCK_HANDLER();
+ rb_funcallv_kw(obj, idInitialize, argc, argv, kw_splat);
}
void
-thread_wait_for(time)
- struct timeval time;
+rb_extend_object(VALUE obj, VALUE module)
{
- 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();
+ rb_include_module(rb_singleton_class(obj), module);
}
-void thread_sleep_forever();
-
-int
-thread_alone()
+/*
+ * 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
+ *
+ * <em>produces:</em>
+ *
+ * Picky added to Array
+ * Can't add Picky to a String
+ */
+
+static VALUE
+rb_mod_extend_object(VALUE mod, VALUE obj)
{
- return curr_thread == curr_thread->next;
+ rb_extend_object(obj, mod);
+ return obj;
}
-int
-thread_select(max, read, write, except, timeout)
- int max;
- fd_set *read, *write, *except;
- struct timeval *timeout;
+/*
+ * 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)
{
- 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;
- }
+ int i;
+ ID id_extend_object, id_extended;
- 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
+ CONST_ID(id_extend_object, "extend_object");
+ CONST_ID(id_extended, "extended");
+ rb_check_arity(argc, 1, UNLIMITED_ARGUMENTS);
+ for (i = 0; i < argc; i++) {
+ Check_Type(argv[i], T_MODULE);
+ if (FL_TEST(argv[i], RMODULE_IS_REFINEMENT)) {
+ rb_raise(rb_eTypeError, "Cannot extend object with refinement");
+ }
}
-
- 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;
+ while (argc--) {
+ rb_funcall(argv[argc], id_extend_object, 1, obj);
+ rb_funcall(argv[argc], id_extended, 1, obj);
}
+ return obj;
}
-static VALUE
-thread_join(dmy, thread)
- VALUE dmy;
- VALUE thread;
+VALUE
+rb_top_main_class(const char *method)
{
- 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;
-}
+ VALUE klass = GET_THREAD()->top_wrapper;
-static VALUE
-thread_current()
-{
- return curr_thread->thread;
+ if (!klass) return rb_cObject;
+ rb_warning("main.%s in the wrapped load is effective only in wrapper module", method);
+ return klass;
}
-static VALUE
-thread_main()
-{
- return main_thread->thread;
-}
+/*
+ * 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
-thread_wakeup(thread)
- VALUE thread;
+top_include(int argc, VALUE *argv, VALUE self)
{
- thread_t th = thread_check(thread);
-
- if (th->status == THREAD_KILLED) Raise(eThreadError, "killed thread");
- thread_ready(th);
-
- return thread;
+ return rb_mod_include(argc, argv, rb_top_main_class("include"));
}
-static VALUE
-thread_run(thread)
- VALUE thread;
-{
- thread_wakeup(thread);
- if (!thread_critical) thread_schedule();
-
- return thread;
-}
+/*
+ * call-seq:
+ * using(module) -> self
+ *
+ * Import class refinements from <i>module</i> into the scope where
+ * #using is called.
+ */
static VALUE
-thread_kill(thread)
- VALUE thread;
+top_using(VALUE self, VALUE module)
{
- 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);
+ const rb_cref_t *cref = CREF_NEXT(rb_vm_cref());
+ rb_control_frame_t *prev_cfp = previous_frame(GET_EC());
+ rb_thread_t *th = GET_THREAD();
- thread_ready(th);
- th->status = THREAD_TO_KILL;
- thread_schedule();
- return Qnil; /* not reached */
+ if ((th->top_wrapper ? CREF_NEXT(cref) : cref) ||
+ (prev_cfp && rb_vm_frame_method_entry(prev_cfp))) {
+ rb_raise(rb_eRuntimeError, "main.using is permitted only at toplevel");
+ }
+ if (rb_block_given_p()) {
+ ignored_block(module, "main.");
+ }
+ rb_using_module(rb_vm_cref_replace_with_duplicated_cref(), module);
+ return self;
}
-static VALUE
-thread_s_kill(obj, th)
- VALUE obj, th;
+static const VALUE *
+errinfo_place(const rb_execution_context_t *ec)
{
- return thread_kill(th);
-}
+ const rb_control_frame_t *cfp = ec->cfp;
+ const rb_control_frame_t *end_cfp = RUBY_VM_END_CONTROL_FRAME(ec);
-static VALUE
-thread_exit()
-{
- return thread_kill(curr_thread->thread);
+ while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, end_cfp)) {
+ if (VM_FRAME_RUBYFRAME_P(cfp)) {
+ if (ISEQ_BODY(CFP_ISEQ(cfp))->type == ISEQ_TYPE_RESCUE) {
+ return &cfp->ep[VM_ENV_INDEX_LAST_LVAR];
+ }
+ else if (ISEQ_BODY(CFP_ISEQ(cfp))->type == ISEQ_TYPE_ENSURE &&
+ !THROW_DATA_P(cfp->ep[VM_ENV_INDEX_LAST_LVAR]) &&
+ !FIXNUM_P(cfp->ep[VM_ENV_INDEX_LAST_LVAR])) {
+ return &cfp->ep[VM_ENV_INDEX_LAST_LVAR];
+ }
+ }
+ cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
+ }
+ return 0;
}
-static VALUE
-thread_pass()
+VALUE
+rb_ec_get_errinfo(const rb_execution_context_t *ec)
{
- thread_schedule();
- return Qnil;
+ const VALUE *ptr = errinfo_place(ec);
+ if (ptr) {
+ return *ptr;
+ }
+ else {
+ return ec->errinfo;
+ }
}
static VALUE
-thread_stop_method(thread)
- VALUE thread;
+get_errinfo(void)
{
- thread_t th = thread_check(thread);
-
- thread_critical = 0;
- th->status = THREAD_STOPPED;
- thread_schedule();
-
- return thread;
+ return get_ec_errinfo(GET_EC());
}
static VALUE
-thread_stop()
+errinfo_getter(ID id, VALUE *_)
{
- thread_stop_method(curr_thread->thread);
- return Qnil;
+ return get_errinfo();
}
-struct timeval time_timeval();
-
-void
-thread_sleep(sec)
- int sec;
+VALUE
+rb_errinfo(void)
{
- if (curr_thread == curr_thread->next) {
- TRAP_BEG;
- sleep(sec);
- TRAP_END;
- return;
- }
- thread_wait_for(time_timeval(INT2FIX(sec)));
+ return GET_EC()->errinfo;
}
void
-thread_sleep_forever()
+rb_set_errinfo(VALUE err)
{
- if (curr_thread == curr_thread->next) {
- TRAP_BEG;
- sleep((32767<<16)+32767);
- TRAP_END;
- return;
+ if (!NIL_P(err) && !rb_obj_is_kind_of(err, rb_eException)) {
+ rb_raise(rb_eTypeError, "assigning non-exception to $!");
}
-
- 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;
+ GET_EC()->errinfo = err;
}
static VALUE
-thread_abort_exc_set(thread, val)
- VALUE thread, val;
+errat_getter(ID id, VALUE *_)
{
- 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;
+ VALUE err = get_errinfo();
+ if (!NIL_P(err)) {
+ return rb_get_backtrace(err);
}
else {
- curr_thread = th->prev = th->next = th;
- th->status = THREAD_RUNNABLE;
+ return Qnil;
}
-
- return th;
}
-#if defined(HAVE_SETITIMER) && !defined(__BOW__)
static void
-catch_timer(sig)
- int sig;
+errat_setter(VALUE val, ID id, VALUE *var)
{
-#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;
+ VALUE err = get_errinfo();
+ if (NIL_P(err)) {
+ rb_raise(rb_eArgError, "$! not set");
}
+ set_backtrace(err, val);
}
-#else
-int thread_tick = THREAD_TICK;
-#endif
-
-VALUE
-thread_create(fn, arg)
- VALUE (*fn)();
- void *arg;
-{
- thread_t th = thread_alloc();
- int 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(PROT_THREAD);
- 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 && th->status != THREAD_TO_KILL && !NIL_P(errinfo)) {
- if (state == 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 || RTEST(debug)) {
- f_abort();
- }
- else {
- curr_thread->errat = errat;
- curr_thread->errinfo = errinfo;
- }
- }
- thread_remove();
- return 0;
-}
+/*
+ * call-seq:
+ * __method__ -> symbol
+ *
+ * Returns the name at the definition of the current method as a
+ * Symbol.
+ * If called outside of a method, it returns <code>nil</code>.
+ *
+ */
static VALUE
-thread_yield(arg, th)
- int arg;
- thread_t th;
+rb_f_method_name(VALUE _)
{
- scope_dup(the_block->scope);
- rb_yield(th->thread);
- return th->thread;
-}
+ ID fname = prev_frame_func(); /* need *method* ID */
-static VALUE
-thread_start()
-{
- if (!iterator_p()) {
- Raise(eThreadError, "must be called as iterator");
+ if (fname) {
+ return ID2SYM(fname);
}
- 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);
+ else {
+ return Qnil;
}
-
- return th->result;
}
+/*
+ * call-seq:
+ * __callee__ -> symbol
+ *
+ * Returns the called name of the current method as a Symbol.
+ * If called outside of a method, it returns <code>nil</code>.
+ *
+ */
+
static VALUE
-thread_status(thread)
- VALUE thread;
+rb_f_callee_name(VALUE _)
{
- thread_t th = thread_check(thread);
+ ID fname = prev_frame_callee(); /* need *callee* ID */
- if (thread_dead(th)) {
- if (NIL_P(th->errinfo)) return FALSE;
- return Qnil;
+ if (fname) {
+ return ID2SYM(fname);
+ }
+ else {
+ return Qnil;
}
-
- return TRUE;
}
+/*
+ * call-seq:
+ * __dir__ -> string
+ *
+ * Returns the canonicalized absolute path of the directory of the file from
+ * which this method is called. It means symlinks in the path is resolved.
+ * If <code>__FILE__</code> is <code>nil</code>, it returns <code>nil</code>.
+ * The return value equals to <code>File.dirname(File.realpath(__FILE__))</code>.
+ *
+ */
static VALUE
-thread_stopped(thread)
- VALUE thread;
+f_current_dirname(VALUE _)
{
- 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();
+ VALUE base = rb_current_realfilepath();
+ if (NIL_P(base)) {
+ return Qnil;
}
+ base = rb_file_dirname(base);
+ return base;
}
-static void
-thread_cleanup()
-{
- thread_t th;
+/*
+ * call-seq:
+ * global_variables -> array
+ *
+ * Returns an array of the names of global variables. This includes
+ * special regexp global variables such as <tt>$~</tt> and <tt>$+</tt>,
+ * but does not include the numbered regexp global variables (<tt>$1</tt>,
+ * <tt>$2</tt>, etc.).
+ *
+ * global_variables.grep /std/ #=> [:$stdin, :$stdout, :$stderr]
+ */
- 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);
+static VALUE
+f_global_variables(VALUE _)
+{
+ return rb_f_global_variables();
}
-int thread_critical;
+/*
+ * call-seq:
+ * trace_var(symbol, cmd ) -> nil
+ * trace_var(symbol) {|val| block } -> nil
+ *
+ * Controls tracing of assignments to global variables. The parameter
+ * +symbol+ identifies the variable (as either a string name or a
+ * symbol identifier). _cmd_ (which may be a string or a
+ * +Proc+ object) or block is executed whenever the variable
+ * is assigned. The block or +Proc+ object receives the
+ * variable's new value as a parameter. Also see
+ * #untrace_var.
+ *
+ * trace_var :$_, proc {|v| puts "$_ is now '#{v}'" }
+ * $_ = "hello"
+ * $_ = ' there'
+ *
+ * <em>produces:</em>
+ *
+ * $_ is now 'hello'
+ * $_ is now ' there'
+ */
static VALUE
-thread_get_critical()
+f_trace_var(int c, const VALUE *a, VALUE _)
{
- return thread_critical?TRUE:FALSE;
+ return rb_f_trace_var(c, a);
}
+/*
+ * call-seq:
+ * untrace_var(symbol [, cmd] ) -> array or nil
+ *
+ * Removes tracing for the specified command on the given global
+ * variable and returns +nil+. If no command is specified,
+ * removes all tracing for that variable and returns an array
+ * containing the commands actually removed.
+ */
+
static VALUE
-thread_set_critical(obj, val)
- VALUE obj, val;
+f_untrace_var(int c, const VALUE *a, VALUE _)
{
- thread_critical = RTEST(val);
- return val;
+ return rb_f_untrace_var(c, a);
}
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;
+Init_eval(void)
{
- thread_t th = thread_check(thread);
+ rb_define_virtual_variable("$@", errat_getter, errat_setter);
+ rb_define_virtual_variable("$!", errinfo_getter, 0);
- if (thread_dead(th)) return thread;
- if (curr_thread == th) {
- f_raise(argc, argv);
- }
+ rb_gvar_ractor_local("$@");
+ rb_gvar_ractor_local("$!");
- thread_save_context(curr_thread);
- if (setjmp(curr_thread->context)) {
- return thread;
- }
+ rb_gvar_box_dynamic("$@");
+ rb_gvar_box_dynamic("$!");
- rb_scan_args(argc, argv, "11", &th_raise_argv[0], &th_raise_argv[1]);
- thread_ready(th);
- curr_thread = th;
+ rb_define_global_function("raise", f_raise, -1);
+ rb_define_global_function("fail", f_raise, -1);
- th_raise_argc = argc;
- th_raise_file = sourcefile;
- th_raise_line = sourceline;
- thread_restore_context(curr_thread, 3);
- return Qnil; /* not reached */
-}
+ rb_define_global_function("global_variables", f_global_variables, 0);
-static thread_t loading_thread;
-static int loading_nest;
+ rb_define_global_function("__method__", rb_f_method_name, 0);
+ rb_define_global_function("__callee__", rb_f_callee_name, 0);
+ rb_define_global_function("__dir__", f_current_dirname, 0);
+
+ rb_define_method(rb_cModule, "include", rb_mod_include, -1);
+ rb_define_method(rb_cModule, "prepend", rb_mod_prepend, -1);
+
+ 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, "prepend_features", rb_mod_prepend_features, 1);
+ rb_define_private_method(rb_cModule, "refine", rb_mod_refine, 1);
+ rb_define_private_method(rb_cModule, "using", mod_using, 1);
+ rb_define_method(rb_cModule, "refinements", mod_refinements, 0);
+ rb_define_singleton_method(rb_cModule, "used_modules",
+ rb_mod_s_used_modules, 0);
+ rb_define_singleton_method(rb_cModule, "used_refinements",
+ rb_mod_s_used_refinements, 0);
+ rb_undef_method(rb_cClass, "refine");
+ rb_define_private_method(rb_cRefinement, "import_methods", refinement_import_methods, -1);
+ rb_define_method(rb_cRefinement, "target", rb_refinement_module_get_refined_class, 0);
+ rb_undef_method(rb_cRefinement, "append_features");
+ rb_undef_method(rb_cRefinement, "prepend_features");
+ rb_undef_method(rb_cRefinement, "extend_object");
+
+ rb_undef_method(rb_cClass, "module_function");
+
+ 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_private_method(rb_singleton_class(rb_vm_top_self()),
+ "include", top_include, -1);
+ rb_define_private_method(rb_singleton_class(rb_vm_top_self()),
+ "using", top_using, 1);
+
+ rb_define_method(rb_mKernel, "extend", rb_obj_extend, -1);
-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 */
- }
+ rb_define_global_function("trace_var", f_trace_var, -1);
+ rb_define_global_function("untrace_var", f_untrace_var, -1);
- loading_thread = curr_thread;
- loading_nest++;
+ rb_vm_register_special_exception(ruby_error_reenter, rb_eFatal, "exception reentered");
+ rb_vm_register_special_exception(ruby_error_stackfatal, rb_eFatal, "machine stack overflow in critical region");
- return FALSE;
+ id_signo = rb_intern_const("signo");
+ id_status = rb_intern_const("status");
}
-static void
-thread_loading_done()
+int
+rb_errno(void)
{
- if (--loading_nest == 0) {
- loading_thread = 0;
- }
+ return *rb_orig_errno_ptr();
}
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
-
-static VALUE
-f_catch(dmy, tag)
- VALUE dmy, tag;
-{
- int state;
- ID t;
- VALUE val; /* OK */
-
- t = rb_to_id(tag);
- PUSH_TAG(t);
- if ((state = EXEC_TAG()) == 0) {
- val = rb_yield(tag);
- }
- else if (state == TAG_THROW && prot_tag->tag == prot_tag->dst) {
- val = prot_tag->tag_retval;
- state = 0;
- }
- POP_TAG();
- if (state) JUMP_TAG(state);
-
- return val;
-}
-
-static VALUE
-f_throw(argc, argv)
- int argc;
- VALUE *argv;
+rb_errno_set(int e)
{
- VALUE tag, value;
- ID t;
- struct tag *tt = prot_tag;
-
- rb_scan_args(argc, argv, "11", &tag, &value);
- t = rb_to_id(tag);
-
- while (tt) {
- if (tt->tag == t) {
- tt->dst = t;
- tt->tag_retval = value;
- break;
- }
-#ifdef THREAD
- if (tt->tag == PROT_THREAD) {
- Raise(eThreadError, "uncaught throw `%s' in thread 0x%x\n",
- rb_id2name(t),
- curr_thread);
- }
-#endif
- tt = tt->prev;
- }
-
- if (!tt) {
- NameError("uncaught throw `%s'\n", rb_id2name(t));
- }
- JUMP_TAG(TAG_THROW);
- /* not reached */
+ *rb_orig_errno_ptr() = e;
}
-static void
-return_value(val)
- VALUE val;
+int *
+rb_errno_ptr(void)
{
- struct tag *tt = prot_tag;
-
- while (tt) {
- if (tt->tag == PROT_FUNC) {
- tt->tag_retval = val;
- break;
- }
-#ifdef THREAD
- if (tt->tag == PROT_THREAD) {
- Raise(eThreadError, "return from within thread 0x%x\n",
- curr_thread);
- }
-#endif
- tt = tt->prev;
- }
+ return rb_orig_errno_ptr();
}
-