Ruby  2.0.0p247(2013-06-27revision41674)
iseq.c
Go to the documentation of this file.
00001 /**********************************************************************
00002 
00003   iseq.c -
00004 
00005   $Author: nagachika $
00006   created at: 2006-07-11(Tue) 09:00:03 +0900
00007 
00008   Copyright (C) 2006 Koichi Sasada
00009 
00010 **********************************************************************/
00011 
00012 #include "ruby/ruby.h"
00013 #include "internal.h"
00014 #include "eval_intern.h"
00015 
00016 /* #define RUBY_MARK_FREE_DEBUG 1 */
00017 #include "gc.h"
00018 #include "vm_core.h"
00019 #include "iseq.h"
00020 
00021 #define numberof(array) (int)(sizeof(array) / sizeof((array)[0]))
00022 
00023 #include "insns.inc"
00024 #include "insns_info.inc"
00025 
00026 #define ISEQ_MAJOR_VERSION 2
00027 #define ISEQ_MINOR_VERSION 0
00028 
00029 VALUE rb_cISeq;
00030 
00031 #define hidden_obj_p(obj) (!SPECIAL_CONST_P(obj) && !RBASIC(obj)->klass)
00032 
00033 static inline VALUE
00034 obj_resurrect(VALUE obj)
00035 {
00036     if (hidden_obj_p(obj)) {
00037         switch (BUILTIN_TYPE(obj)) {
00038           case T_STRING:
00039             obj = rb_str_resurrect(obj);
00040             break;
00041           case T_ARRAY:
00042             obj = rb_ary_resurrect(obj);
00043             break;
00044         }
00045     }
00046     return obj;
00047 }
00048 
00049 static void
00050 compile_data_free(struct iseq_compile_data *compile_data)
00051 {
00052     if (compile_data) {
00053         struct iseq_compile_data_storage *cur, *next;
00054         cur = compile_data->storage_head;
00055         while (cur) {
00056             next = cur->next;
00057             ruby_xfree(cur);
00058             cur = next;
00059         }
00060         ruby_xfree(compile_data);
00061     }
00062 }
00063 
00064 static void
00065 iseq_free(void *ptr)
00066 {
00067     rb_iseq_t *iseq;
00068     RUBY_FREE_ENTER("iseq");
00069 
00070     if (ptr) {
00071         iseq = ptr;
00072         if (!iseq->orig) {
00073             /* It's possible that strings are freed */
00074             if (0) {
00075                 RUBY_GC_INFO("%s @ %s\n", RSTRING_PTR(iseq->location.label),
00076                                           RSTRING_PTR(iseq->location.path));
00077             }
00078 
00079             if (iseq->iseq != iseq->iseq_encoded) {
00080                 RUBY_FREE_UNLESS_NULL(iseq->iseq_encoded);
00081             }
00082 
00083             RUBY_FREE_UNLESS_NULL(iseq->iseq);
00084             RUBY_FREE_UNLESS_NULL(iseq->line_info_table);
00085             RUBY_FREE_UNLESS_NULL(iseq->local_table);
00086             RUBY_FREE_UNLESS_NULL(iseq->ic_entries);
00087             RUBY_FREE_UNLESS_NULL(iseq->callinfo_entries);
00088             RUBY_FREE_UNLESS_NULL(iseq->catch_table);
00089             RUBY_FREE_UNLESS_NULL(iseq->arg_opt_table);
00090             RUBY_FREE_UNLESS_NULL(iseq->arg_keyword_table);
00091             compile_data_free(iseq->compile_data);
00092         }
00093         ruby_xfree(ptr);
00094     }
00095     RUBY_FREE_LEAVE("iseq");
00096 }
00097 
00098 static void
00099 iseq_mark(void *ptr)
00100 {
00101     RUBY_MARK_ENTER("iseq");
00102 
00103     if (ptr) {
00104         rb_iseq_t *iseq = ptr;
00105 
00106         RUBY_GC_INFO("%s @ %s\n", RSTRING_PTR(iseq->location.label), RSTRING_PTR(iseq->location.path));
00107         RUBY_MARK_UNLESS_NULL(iseq->mark_ary);
00108 
00109         RUBY_MARK_UNLESS_NULL(iseq->location.label);
00110         RUBY_MARK_UNLESS_NULL(iseq->location.base_label);
00111         RUBY_MARK_UNLESS_NULL(iseq->location.path);
00112         RUBY_MARK_UNLESS_NULL(iseq->location.absolute_path);
00113 
00114         RUBY_MARK_UNLESS_NULL((VALUE)iseq->cref_stack);
00115         RUBY_MARK_UNLESS_NULL(iseq->klass);
00116         RUBY_MARK_UNLESS_NULL(iseq->coverage);
00117 #if 0
00118         RUBY_MARK_UNLESS_NULL((VALUE)iseq->node);
00119         RUBY_MARK_UNLESS_NULL(iseq->cached_special_block);
00120 #endif
00121         RUBY_MARK_UNLESS_NULL(iseq->orig);
00122 
00123         if (iseq->compile_data != 0) {
00124             struct iseq_compile_data *const compile_data = iseq->compile_data;
00125             RUBY_MARK_UNLESS_NULL(compile_data->mark_ary);
00126             RUBY_MARK_UNLESS_NULL(compile_data->err_info);
00127             RUBY_MARK_UNLESS_NULL(compile_data->catch_table_ary);
00128         }
00129     }
00130     RUBY_MARK_LEAVE("iseq");
00131 }
00132 
00133 static size_t
00134 iseq_memsize(const void *ptr)
00135 {
00136     size_t size = sizeof(rb_iseq_t);
00137     const rb_iseq_t *iseq;
00138 
00139     if (ptr) {
00140         iseq = ptr;
00141         if (!iseq->orig) {
00142             if (iseq->iseq != iseq->iseq_encoded) {
00143                 size += iseq->iseq_size * sizeof(VALUE);
00144             }
00145 
00146             size += iseq->iseq_size * sizeof(VALUE);
00147             size += iseq->line_info_size * sizeof(struct iseq_line_info_entry);
00148             size += iseq->local_table_size * sizeof(ID);
00149             size += iseq->catch_table_size * sizeof(struct iseq_catch_table_entry);
00150             size += iseq->arg_opts * sizeof(VALUE);
00151             size += iseq->ic_size * sizeof(struct iseq_inline_cache_entry);
00152             size += iseq->callinfo_size * sizeof(rb_call_info_t);
00153 
00154             if (iseq->compile_data) {
00155                 struct iseq_compile_data_storage *cur;
00156 
00157                 cur = iseq->compile_data->storage_head;
00158                 while (cur) {
00159                     size += cur->size + sizeof(struct iseq_compile_data_storage);
00160                     cur = cur->next;
00161                 }
00162                 size += sizeof(struct iseq_compile_data);
00163             }
00164         }
00165     }
00166 
00167     return size;
00168 }
00169 
00170 static const rb_data_type_t iseq_data_type = {
00171     "iseq",
00172     {
00173         iseq_mark,
00174         iseq_free,
00175         iseq_memsize,
00176     },
00177 };
00178 
00179 static VALUE
00180 iseq_alloc(VALUE klass)
00181 {
00182     rb_iseq_t *iseq;
00183     return TypedData_Make_Struct(klass, rb_iseq_t, &iseq_data_type, iseq);
00184 }
00185 
00186 static rb_iseq_location_t *
00187 iseq_location_setup(rb_iseq_t *iseq, VALUE path, VALUE absolute_path, VALUE name, size_t first_lineno)
00188 {
00189     rb_iseq_location_t *loc = &iseq->location;
00190     loc->path = path;
00191     loc->absolute_path = absolute_path;
00192     loc->label = loc->base_label = name;
00193     loc->first_lineno = first_lineno;
00194     return loc;
00195 }
00196 
00197 static void
00198 set_relation(rb_iseq_t *iseq, const VALUE parent)
00199 {
00200     const VALUE type = iseq->type;
00201     rb_thread_t *th = GET_THREAD();
00202     rb_iseq_t *piseq;
00203 
00204     /* set class nest stack */
00205     if (type == ISEQ_TYPE_TOP) {
00206         /* toplevel is private */
00207         iseq->cref_stack = NEW_CREF(rb_cObject);
00208         iseq->cref_stack->nd_refinements = Qnil;
00209         iseq->cref_stack->nd_visi = NOEX_PRIVATE;
00210         if (th->top_wrapper) {
00211             NODE *cref = NEW_CREF(th->top_wrapper);
00212             cref->nd_refinements = Qnil;
00213             cref->nd_visi = NOEX_PRIVATE;
00214             cref->nd_next = iseq->cref_stack;
00215             iseq->cref_stack = cref;
00216         }
00217         iseq->local_iseq = iseq;
00218     }
00219     else if (type == ISEQ_TYPE_METHOD || type == ISEQ_TYPE_CLASS) {
00220         iseq->cref_stack = NEW_CREF(0); /* place holder */
00221         iseq->cref_stack->nd_refinements = Qnil;
00222         iseq->local_iseq = iseq;
00223     }
00224     else if (RTEST(parent)) {
00225         GetISeqPtr(parent, piseq);
00226         iseq->cref_stack = piseq->cref_stack;
00227         iseq->local_iseq = piseq->local_iseq;
00228     }
00229 
00230     if (RTEST(parent)) {
00231         GetISeqPtr(parent, piseq);
00232         iseq->parent_iseq = piseq;
00233     }
00234 
00235     if (type == ISEQ_TYPE_MAIN) {
00236         iseq->local_iseq = iseq;
00237     }
00238 }
00239 
00240 static VALUE
00241 prepare_iseq_build(rb_iseq_t *iseq,
00242                    VALUE name, VALUE path, VALUE absolute_path, VALUE first_lineno,
00243                    VALUE parent, enum iseq_type type, VALUE block_opt,
00244                    const rb_compile_option_t *option)
00245 {
00246     iseq->type = type;
00247     iseq->arg_rest = -1;
00248     iseq->arg_block = -1;
00249     iseq->arg_keyword = -1;
00250     iseq->klass = 0;
00251     set_relation(iseq, parent);
00252 
00253     OBJ_FREEZE(name);
00254     OBJ_FREEZE(path);
00255 
00256     iseq_location_setup(iseq, path, absolute_path, name, first_lineno);
00257     if (iseq != iseq->local_iseq) {
00258         iseq->location.base_label = iseq->local_iseq->location.label;
00259     }
00260 
00261     iseq->defined_method_id = 0;
00262     iseq->mark_ary = rb_ary_tmp_new(3);
00263     OBJ_UNTRUST(iseq->mark_ary);
00264     RBASIC(iseq->mark_ary)->klass = 0;
00265 
00266 
00267     /*
00268      * iseq->special_block_builder = GC_GUARDED_PTR_REF(block_opt);
00269      * iseq->cached_special_block_builder = 0;
00270      * iseq->cached_special_block = 0;
00271      */
00272 
00273     iseq->compile_data = ALLOC(struct iseq_compile_data);
00274     MEMZERO(iseq->compile_data, struct iseq_compile_data, 1);
00275     iseq->compile_data->err_info = Qnil;
00276     iseq->compile_data->mark_ary = rb_ary_tmp_new(3);
00277 
00278     iseq->compile_data->storage_head = iseq->compile_data->storage_current =
00279       (struct iseq_compile_data_storage *)
00280         ALLOC_N(char, INITIAL_ISEQ_COMPILE_DATA_STORAGE_BUFF_SIZE +
00281                 sizeof(struct iseq_compile_data_storage));
00282 
00283     iseq->compile_data->catch_table_ary = rb_ary_new();
00284     iseq->compile_data->storage_head->pos = 0;
00285     iseq->compile_data->storage_head->next = 0;
00286     iseq->compile_data->storage_head->size =
00287       INITIAL_ISEQ_COMPILE_DATA_STORAGE_BUFF_SIZE;
00288     iseq->compile_data->storage_head->buff =
00289       (char *)(&iseq->compile_data->storage_head->buff + 1);
00290     iseq->compile_data->option = option;
00291     iseq->compile_data->last_coverable_line = -1;
00292 
00293     iseq->coverage = Qfalse;
00294     if (!GET_THREAD()->parse_in_eval) {
00295         VALUE coverages = rb_get_coverages();
00296         if (RTEST(coverages)) {
00297             iseq->coverage = rb_hash_lookup(coverages, path);
00298             if (NIL_P(iseq->coverage)) iseq->coverage = Qfalse;
00299         }
00300     }
00301 
00302     return Qtrue;
00303 }
00304 
00305 static VALUE
00306 cleanup_iseq_build(rb_iseq_t *iseq)
00307 {
00308     struct iseq_compile_data *data = iseq->compile_data;
00309     VALUE err = data->err_info;
00310     iseq->compile_data = 0;
00311     compile_data_free(data);
00312 
00313     if (RTEST(err)) {
00314         rb_funcall2(err, rb_intern("set_backtrace"), 1, &iseq->location.path);
00315         rb_exc_raise(err);
00316     }
00317     return Qtrue;
00318 }
00319 
00320 static rb_compile_option_t COMPILE_OPTION_DEFAULT = {
00321     OPT_INLINE_CONST_CACHE, /* int inline_const_cache; */
00322     OPT_PEEPHOLE_OPTIMIZATION, /* int peephole_optimization; */
00323     OPT_TAILCALL_OPTIMIZATION, /* int tailcall_optimization */
00324     OPT_SPECIALISED_INSTRUCTION, /* int specialized_instruction; */
00325     OPT_OPERANDS_UNIFICATION, /* int operands_unification; */
00326     OPT_INSTRUCTIONS_UNIFICATION, /* int instructions_unification; */
00327     OPT_STACK_CACHING, /* int stack_caching; */
00328     OPT_TRACE_INSTRUCTION, /* int trace_instruction */
00329 };
00330 static const rb_compile_option_t COMPILE_OPTION_FALSE = {0};
00331 
00332 static void
00333 make_compile_option(rb_compile_option_t *option, VALUE opt)
00334 {
00335     if (opt == Qnil) {
00336         *option = COMPILE_OPTION_DEFAULT;
00337     }
00338     else if (opt == Qfalse) {
00339         *option = COMPILE_OPTION_FALSE;
00340     }
00341     else if (opt == Qtrue) {
00342         memset(option, 1, sizeof(rb_compile_option_t));
00343     }
00344     else if (CLASS_OF(opt) == rb_cHash) {
00345         *option = COMPILE_OPTION_DEFAULT;
00346 
00347 #define SET_COMPILE_OPTION(o, h, mem) \
00348   { VALUE flag = rb_hash_aref((h), ID2SYM(rb_intern(#mem))); \
00349       if (flag == Qtrue)  { (o)->mem = 1; } \
00350       else if (flag == Qfalse)  { (o)->mem = 0; } \
00351   }
00352 #define SET_COMPILE_OPTION_NUM(o, h, mem) \
00353   { VALUE num = rb_hash_aref(opt, ID2SYM(rb_intern(#mem))); \
00354       if (!NIL_P(num)) (o)->mem = NUM2INT(num); \
00355   }
00356         SET_COMPILE_OPTION(option, opt, inline_const_cache);
00357         SET_COMPILE_OPTION(option, opt, peephole_optimization);
00358         SET_COMPILE_OPTION(option, opt, tailcall_optimization);
00359         SET_COMPILE_OPTION(option, opt, specialized_instruction);
00360         SET_COMPILE_OPTION(option, opt, operands_unification);
00361         SET_COMPILE_OPTION(option, opt, instructions_unification);
00362         SET_COMPILE_OPTION(option, opt, stack_caching);
00363         SET_COMPILE_OPTION(option, opt, trace_instruction);
00364         SET_COMPILE_OPTION_NUM(option, opt, debug_level);
00365 #undef SET_COMPILE_OPTION
00366 #undef SET_COMPILE_OPTION_NUM
00367     }
00368     else {
00369         rb_raise(rb_eTypeError, "Compile option must be Hash/true/false/nil");
00370     }
00371 }
00372 
00373 static VALUE
00374 make_compile_option_value(rb_compile_option_t *option)
00375 {
00376     VALUE opt = rb_hash_new();
00377 #define SET_COMPILE_OPTION(o, h, mem) \
00378   rb_hash_aset((h), ID2SYM(rb_intern(#mem)), (o)->mem ? Qtrue : Qfalse)
00379 #define SET_COMPILE_OPTION_NUM(o, h, mem) \
00380   rb_hash_aset((h), ID2SYM(rb_intern(#mem)), INT2NUM((o)->mem))
00381     {
00382         SET_COMPILE_OPTION(option, opt, inline_const_cache);
00383         SET_COMPILE_OPTION(option, opt, peephole_optimization);
00384         SET_COMPILE_OPTION(option, opt, tailcall_optimization);
00385         SET_COMPILE_OPTION(option, opt, specialized_instruction);
00386         SET_COMPILE_OPTION(option, opt, operands_unification);
00387         SET_COMPILE_OPTION(option, opt, instructions_unification);
00388         SET_COMPILE_OPTION(option, opt, stack_caching);
00389         SET_COMPILE_OPTION(option, opt, trace_instruction);
00390         SET_COMPILE_OPTION_NUM(option, opt, debug_level);
00391     }
00392 #undef SET_COMPILE_OPTION
00393 #undef SET_COMPILE_OPTION_NUM
00394     return opt;
00395 }
00396 
00397 VALUE
00398 rb_iseq_new(NODE *node, VALUE name, VALUE path, VALUE absolute_path,
00399             VALUE parent, enum iseq_type type)
00400 {
00401     return rb_iseq_new_with_opt(node, name, path, absolute_path, INT2FIX(0), parent, type,
00402                                 &COMPILE_OPTION_DEFAULT);
00403 }
00404 
00405 VALUE
00406 rb_iseq_new_top(NODE *node, VALUE name, VALUE path, VALUE absolute_path, VALUE parent)
00407 {
00408     return rb_iseq_new_with_opt(node, name, path, absolute_path, INT2FIX(0), parent, ISEQ_TYPE_TOP,
00409                                 &COMPILE_OPTION_DEFAULT);
00410 }
00411 
00412 VALUE
00413 rb_iseq_new_main(NODE *node, VALUE path, VALUE absolute_path)
00414 {
00415     rb_thread_t *th = GET_THREAD();
00416     VALUE parent = th->base_block->iseq->self;
00417     return rb_iseq_new_with_opt(node, rb_str_new2("<main>"), path, absolute_path, INT2FIX(0),
00418                                 parent, ISEQ_TYPE_MAIN, &COMPILE_OPTION_DEFAULT);
00419 }
00420 
00421 static VALUE
00422 rb_iseq_new_with_bopt_and_opt(NODE *node, VALUE name, VALUE path, VALUE absolute_path, VALUE first_lineno,
00423                                 VALUE parent, enum iseq_type type, VALUE bopt,
00424                                 const rb_compile_option_t *option)
00425 {
00426     rb_iseq_t *iseq;
00427     VALUE self = iseq_alloc(rb_cISeq);
00428 
00429     GetISeqPtr(self, iseq);
00430     iseq->self = self;
00431 
00432     prepare_iseq_build(iseq, name, path, absolute_path, first_lineno, parent, type, bopt, option);
00433     rb_iseq_compile_node(self, node);
00434     cleanup_iseq_build(iseq);
00435     return self;
00436 }
00437 
00438 VALUE
00439 rb_iseq_new_with_opt(NODE *node, VALUE name, VALUE path, VALUE absolute_path, VALUE first_lineno,
00440                      VALUE parent, enum iseq_type type,
00441                      const rb_compile_option_t *option)
00442 {
00443     /* TODO: argument check */
00444     return rb_iseq_new_with_bopt_and_opt(node, name, path, absolute_path, first_lineno, parent, type,
00445                                            Qfalse, option);
00446 }
00447 
00448 VALUE
00449 rb_iseq_new_with_bopt(NODE *node, VALUE name, VALUE path, VALUE absolute_path, VALUE first_lineno,
00450                        VALUE parent, enum iseq_type type, VALUE bopt)
00451 {
00452     /* TODO: argument check */
00453     return rb_iseq_new_with_bopt_and_opt(node, name, path, absolute_path, first_lineno, parent, type,
00454                                            bopt, &COMPILE_OPTION_DEFAULT);
00455 }
00456 
00457 #define CHECK_ARRAY(v)   rb_convert_type((v), T_ARRAY, "Array", "to_ary")
00458 #define CHECK_STRING(v)  rb_convert_type((v), T_STRING, "String", "to_str")
00459 #define CHECK_SYMBOL(v)  rb_convert_type((v), T_SYMBOL, "Symbol", "to_sym")
00460 static inline VALUE CHECK_INTEGER(VALUE v) {(void)NUM2LONG(v); return v;}
00461 static VALUE
00462 iseq_load(VALUE self, VALUE data, VALUE parent, VALUE opt)
00463 {
00464     VALUE iseqval = iseq_alloc(self);
00465 
00466     VALUE magic, version1, version2, format_type, misc;
00467     VALUE name, path, absolute_path, first_lineno;
00468     VALUE type, body, locals, args, exception;
00469 
00470     st_data_t iseq_type;
00471     struct st_table *type_map = 0;
00472     rb_iseq_t *iseq;
00473     rb_compile_option_t option;
00474     int i = 0;
00475 
00476     /* [magic, major_version, minor_version, format_type, misc,
00477      *  label, path, first_lineno,
00478      *  type, locals, args, exception_table, body]
00479      */
00480 
00481     data        = CHECK_ARRAY(data);
00482 
00483     magic       = CHECK_STRING(rb_ary_entry(data, i++));
00484     version1    = CHECK_INTEGER(rb_ary_entry(data, i++));
00485     version2    = CHECK_INTEGER(rb_ary_entry(data, i++));
00486     format_type = CHECK_INTEGER(rb_ary_entry(data, i++));
00487     misc        = rb_ary_entry(data, i++); /* TODO */
00488     ((void)magic, (void)version1, (void)version2, (void)format_type, (void)misc);
00489 
00490     name        = CHECK_STRING(rb_ary_entry(data, i++));
00491     path        = CHECK_STRING(rb_ary_entry(data, i++));
00492     absolute_path = rb_ary_entry(data, i++);
00493     absolute_path = NIL_P(absolute_path) ? Qnil : CHECK_STRING(absolute_path);
00494     first_lineno = CHECK_INTEGER(rb_ary_entry(data, i++));
00495 
00496     type        = CHECK_SYMBOL(rb_ary_entry(data, i++));
00497     locals      = CHECK_ARRAY(rb_ary_entry(data, i++));
00498 
00499     args        = rb_ary_entry(data, i++);
00500     if (FIXNUM_P(args) || (args = CHECK_ARRAY(args))) {
00501         /* */
00502     }
00503 
00504     exception   = CHECK_ARRAY(rb_ary_entry(data, i++));
00505     body        = CHECK_ARRAY(rb_ary_entry(data, i++));
00506 
00507     GetISeqPtr(iseqval, iseq);
00508     iseq->self = iseqval;
00509 
00510     if (type_map == 0) {
00511         type_map = st_init_numtable();
00512         st_insert(type_map, ID2SYM(rb_intern("top")), ISEQ_TYPE_TOP);
00513         st_insert(type_map, ID2SYM(rb_intern("method")), ISEQ_TYPE_METHOD);
00514         st_insert(type_map, ID2SYM(rb_intern("block")), ISEQ_TYPE_BLOCK);
00515         st_insert(type_map, ID2SYM(rb_intern("class")), ISEQ_TYPE_CLASS);
00516         st_insert(type_map, ID2SYM(rb_intern("rescue")), ISEQ_TYPE_RESCUE);
00517         st_insert(type_map, ID2SYM(rb_intern("ensure")), ISEQ_TYPE_ENSURE);
00518         st_insert(type_map, ID2SYM(rb_intern("eval")), ISEQ_TYPE_EVAL);
00519         st_insert(type_map, ID2SYM(rb_intern("main")), ISEQ_TYPE_MAIN);
00520         st_insert(type_map, ID2SYM(rb_intern("defined_guard")), ISEQ_TYPE_DEFINED_GUARD);
00521     }
00522 
00523     if (st_lookup(type_map, type, &iseq_type) == 0) {
00524         ID typeid = SYM2ID(type);
00525         VALUE typename = rb_id2str(typeid);
00526         if (typename)
00527             rb_raise(rb_eTypeError, "unsupport type: :%"PRIsVALUE, typename);
00528         else
00529             rb_raise(rb_eTypeError, "unsupport type: %p", (void *)typeid);
00530     }
00531 
00532     if (parent == Qnil) {
00533         parent = 0;
00534     }
00535 
00536     make_compile_option(&option, opt);
00537     prepare_iseq_build(iseq, name, path, absolute_path, first_lineno,
00538                        parent, (enum iseq_type)iseq_type, 0, &option);
00539 
00540     rb_iseq_build_from_ary(iseq, locals, args, exception, body);
00541 
00542     cleanup_iseq_build(iseq);
00543     return iseqval;
00544 }
00545 
00546 /*
00547  * :nodoc:
00548  */
00549 static VALUE
00550 iseq_s_load(int argc, VALUE *argv, VALUE self)
00551 {
00552     VALUE data, opt=Qnil;
00553     rb_scan_args(argc, argv, "11", &data, &opt);
00554 
00555     return iseq_load(self, data, 0, opt);
00556 }
00557 
00558 VALUE
00559 rb_iseq_load(VALUE data, VALUE parent, VALUE opt)
00560 {
00561     return iseq_load(rb_cISeq, data, parent, opt);
00562 }
00563 
00564 static NODE *
00565 parse_string(VALUE str, const char *file, int line)
00566 {
00567     VALUE parser = rb_parser_new();
00568     NODE *node = rb_parser_compile_string(parser, file, str, line);
00569 
00570     if (!node) {
00571         rb_exc_raise(GET_THREAD()->errinfo);    /* TODO: check err */
00572     }
00573     return node;
00574 }
00575 
00576 VALUE
00577 rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE absolute_path, VALUE line, rb_block_t *base_block, VALUE opt)
00578 {
00579     int state;
00580     rb_thread_t *th = GET_THREAD();
00581     rb_block_t *prev_base_block = th->base_block;
00582     VALUE iseqval = Qundef;
00583 
00584     th->base_block = base_block;
00585 
00586     TH_PUSH_TAG(th);
00587     if ((state = EXEC_TAG()) == 0) {
00588         int ln = NUM2INT(line);
00589         const char *fn = StringValueCStr(file);
00590         NODE *node;
00591         rb_compile_option_t option;
00592 
00593         make_compile_option(&option, opt);
00594 
00595         if (RB_TYPE_P((src), T_FILE))
00596             node = rb_compile_file(fn, src, ln);
00597         else
00598             node = parse_string(StringValue(src), fn, ln);
00599 
00600         if (base_block && base_block->iseq) {
00601             iseqval = rb_iseq_new_with_opt(node, base_block->iseq->location.label,
00602                                            file, absolute_path, line, base_block->iseq->self,
00603                                            ISEQ_TYPE_EVAL, &option);
00604         }
00605         else {
00606             iseqval = rb_iseq_new_with_opt(node, rb_str_new2("<compiled>"), file, absolute_path, line, Qfalse,
00607                                            ISEQ_TYPE_TOP, &option);
00608         }
00609     }
00610     TH_POP_TAG();
00611 
00612     th->base_block = prev_base_block;
00613 
00614     if (state) {
00615         JUMP_TAG(state);
00616     }
00617 
00618     return iseqval;
00619 }
00620 
00621 VALUE
00622 rb_iseq_compile(VALUE src, VALUE file, VALUE line)
00623 {
00624     return rb_iseq_compile_with_option(src, file, Qnil, line, 0, Qnil);
00625 }
00626 
00627 VALUE
00628 rb_iseq_compile_on_base(VALUE src, VALUE file, VALUE line, rb_block_t *base_block)
00629 {
00630     return rb_iseq_compile_with_option(src, file, Qnil, line, base_block, Qnil);
00631 }
00632 
00633 /*
00634  *  call-seq:
00635  *     InstructionSequence.compile(source[, file[, path[, line[, options]]]]) -> iseq
00636  *     InstructionSequence.new(source[, file[, path[, line[, options]]]]) -> iseq
00637  *
00638  *  Takes +source+, a String of Ruby code and compiles it to an
00639  *  InstructionSequence.
00640  *
00641  *  Optionally takes +file+, +path+, and +line+ which describe the filename,
00642  *  absolute path and first line number of the ruby code in +source+ which are
00643  *  metadata attached to the returned +iseq+.
00644  *
00645  *  +options+, which can be +true+, +false+ or a +Hash+, is used to
00646  *  modify the default behavior of the Ruby iseq compiler.
00647  *
00648  *  For details regarding valid compile options see ::compile_option=.
00649  *
00650  *     RubyVM::InstructionSequence.compile("a = 1 + 2")
00651  *     #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
00652  *
00653  */
00654 static VALUE
00655 iseq_s_compile(int argc, VALUE *argv, VALUE self)
00656 {
00657     VALUE src, file = Qnil, path = Qnil, line = INT2FIX(1), opt = Qnil;
00658 
00659     rb_secure(1);
00660 
00661     rb_scan_args(argc, argv, "14", &src, &file, &path, &line, &opt);
00662     if (NIL_P(file)) file = rb_str_new2("<compiled>");
00663     if (NIL_P(line)) line = INT2FIX(1);
00664 
00665     return rb_iseq_compile_with_option(src, file, path, line, 0, opt);
00666 }
00667 
00668 /*
00669  *  call-seq:
00670  *      InstructionSequence.compile_file(file[, options]) -> iseq
00671  *
00672  *  Takes +file+, a String with the location of a Ruby source file, reads,
00673  *  parses and compiles the file, and returns +iseq+, the compiled
00674  *  InstructionSequence with source location metadata set.
00675  *
00676  *  Optionally takes +options+, which can be +true+, +false+ or a +Hash+, to
00677  *  modify the default behavior of the Ruby iseq compiler.
00678  *
00679  *  For details regarding valid compile options see ::compile_option=.
00680  *
00681  *      # /tmp/hello.rb
00682  *      puts "Hello, world!"
00683  *
00684  *      # elsewhere
00685  *      RubyVM::InstructionSequence.compile_file("/tmp/hello.rb")
00686  *      #=> <RubyVM::InstructionSequence:<main>@/tmp/hello.rb>
00687  */
00688 static VALUE
00689 iseq_s_compile_file(int argc, VALUE *argv, VALUE self)
00690 {
00691     VALUE file, line = INT2FIX(1), opt = Qnil;
00692     VALUE parser;
00693     VALUE f;
00694     NODE *node;
00695     const char *fname;
00696     rb_compile_option_t option;
00697 
00698     rb_secure(1);
00699     rb_scan_args(argc, argv, "11", &file, &opt);
00700     FilePathValue(file);
00701     fname = StringValueCStr(file);
00702 
00703     f = rb_file_open_str(file, "r");
00704 
00705     parser = rb_parser_new();
00706     node = rb_parser_compile_file(parser, fname, f, NUM2INT(line));
00707     make_compile_option(&option, opt);
00708     return rb_iseq_new_with_opt(node, rb_str_new2("<main>"), file,
00709                                 rb_realpath_internal(Qnil, file, 1), line, Qfalse,
00710                                 ISEQ_TYPE_TOP, &option);
00711 }
00712 
00713 /*
00714  *  call-seq:
00715  *     InstructionSequence.compile_option = options
00716  *
00717  *  Sets the default values for various optimizations in the Ruby iseq
00718  *  compiler.
00719  *
00720  *  Possible values for +options+ include +true+, which enables all options,
00721  *  +false+ which disables all options, and +nil+ which leaves all options
00722  *  unchanged.
00723  *
00724  *  You can also pass a +Hash+ of +options+ that you want to change, any
00725  *  options not present in the hash will be left unchanged.
00726  *
00727  *  Possible option names (which are keys in +options+) which can be set to
00728  *  +true+ or +false+ include:
00729  *
00730  *  * +:inline_const_cache+
00731  *  * +:instructions_unification+
00732  *  * +:operands_unification+
00733  *  * +:peephole_optimization+
00734  *  * +:specialized_instruction+
00735  *  * +:stack_caching+
00736  *  * +:tailcall_optimization+
00737  *  * +:trace_instruction+
00738  *
00739  *  Additionally, +:debug_level+ can be set to an integer.
00740  *
00741  *  These default options can be overwritten for a single run of the iseq
00742  *  compiler by passing any of the above values as the +options+ parameter to
00743  *  ::new, ::compile and ::compile_file.
00744  */
00745 static VALUE
00746 iseq_s_compile_option_set(VALUE self, VALUE opt)
00747 {
00748     rb_compile_option_t option;
00749     rb_secure(1);
00750     make_compile_option(&option, opt);
00751     COMPILE_OPTION_DEFAULT = option;
00752     return opt;
00753 }
00754 
00755 /*
00756  *  call-seq:
00757  *     InstructionSequence.compile_option -> options
00758  *
00759  *  Returns a hash of default options used by the Ruby iseq compiler.
00760  *
00761  *  For details, see InstructionSequence.compile_option=.
00762  */
00763 static VALUE
00764 iseq_s_compile_option_get(VALUE self)
00765 {
00766     return make_compile_option_value(&COMPILE_OPTION_DEFAULT);
00767 }
00768 
00769 static rb_iseq_t *
00770 iseq_check(VALUE val)
00771 {
00772     rb_iseq_t *iseq;
00773     GetISeqPtr(val, iseq);
00774     if (!iseq->location.label) {
00775         rb_raise(rb_eTypeError, "uninitialized InstructionSequence");
00776     }
00777     return iseq;
00778 }
00779 
00780 /*
00781  *  call-seq:
00782  *     iseq.eval -> obj
00783  *
00784  *  Evaluates the instruction sequence and returns the result.
00785  *
00786  *      RubyVM::InstructionSequence.compile("1 + 2").eval #=> 3
00787  */
00788 static VALUE
00789 iseq_eval(VALUE self)
00790 {
00791     rb_secure(1);
00792     return rb_iseq_eval(self);
00793 }
00794 
00795 /*
00796  *  Returns a human-readable string representation of this instruction
00797  *  sequence, including the #label and #path.
00798  */
00799 static VALUE
00800 iseq_inspect(VALUE self)
00801 {
00802     rb_iseq_t *iseq;
00803     GetISeqPtr(self, iseq);
00804     if (!iseq->location.label) {
00805         return rb_sprintf("#<%s: uninitialized>", rb_obj_classname(self));
00806     }
00807 
00808     return rb_sprintf("<%s:%s@%s>",
00809                       rb_obj_classname(self),
00810                       RSTRING_PTR(iseq->location.label), RSTRING_PTR(iseq->location.path));
00811 }
00812 
00813 /*
00814  *  Returns the path of this instruction sequence.
00815  *
00816  *  <code><compiled></code> if the iseq was evaluated from a string.
00817  *
00818  *  For example, using irb:
00819  *
00820  *      iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
00821  *      #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
00822  *      iseq.path
00823  *      #=> "<compiled>"
00824  *
00825  *  Using ::compile_file:
00826  *
00827  *      # /tmp/method.rb
00828  *      def hello
00829  *        puts "hello, world"
00830  *      end
00831  *
00832  *      # in irb
00833  *      > iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb')
00834  *      > iseq.path #=> /tmp/method.rb
00835  */
00836 static VALUE
00837 iseq_path(VALUE self)
00838 {
00839     rb_iseq_t *iseq;
00840     GetISeqPtr(self, iseq);
00841     return iseq->location.path;
00842 }
00843 
00844 /*
00845  *  Returns the absolute path of this instruction sequence.
00846  *
00847  *  +nil+ if the iseq was evaluated from a string.
00848  *
00849  *  For example, using ::compile_file:
00850  *
00851  *      # /tmp/method.rb
00852  *      def hello
00853  *        puts "hello, world"
00854  *      end
00855  *
00856  *      # in irb
00857  *      > iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb')
00858  *      > iseq.absolute_path #=> /tmp/method.rb
00859  */
00860 static VALUE
00861 iseq_absolute_path(VALUE self)
00862 {
00863     rb_iseq_t *iseq;
00864     GetISeqPtr(self, iseq);
00865     return iseq->location.absolute_path;
00866 }
00867 
00868 /*  Returns the label of this instruction sequence.
00869  *
00870  *  <code><main></code> if it's at the top level, <code><compiled></code> if it
00871  *  was evaluated from a string.
00872  *
00873  *  For example, using irb:
00874  *
00875  *      iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
00876  *      #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
00877  *      iseq.label
00878  *      #=> "<compiled>"
00879  *
00880  *  Using ::compile_file:
00881  *
00882  *      # /tmp/method.rb
00883  *      def hello
00884  *        puts "hello, world"
00885  *      end
00886  *
00887  *      # in irb
00888  *      > iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb')
00889  *      > iseq.label #=> <main>
00890  */
00891 static VALUE
00892 iseq_label(VALUE self)
00893 {
00894     rb_iseq_t *iseq;
00895     GetISeqPtr(self, iseq);
00896     return iseq->location.label;
00897 }
00898 
00899 /*  Returns the base label of this instruction sequence.
00900  *
00901  *  For example, using irb:
00902  *
00903  *      iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
00904  *      #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
00905  *      iseq.base_label
00906  *      #=> "<compiled>"
00907  *
00908  *  Using ::compile_file:
00909  *
00910  *      # /tmp/method.rb
00911  *      def hello
00912  *        puts "hello, world"
00913  *      end
00914  *
00915  *      # in irb
00916  *      > iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb')
00917  *      > iseq.base_label #=> <main>
00918  */
00919 static VALUE
00920 iseq_base_label(VALUE self)
00921 {
00922     rb_iseq_t *iseq;
00923     GetISeqPtr(self, iseq);
00924     return iseq->location.base_label;
00925 }
00926 
00927 /*  Returns the number of the first source line where the instruction sequence
00928  *  was loaded from.
00929  *
00930  *  For example, using irb:
00931  *
00932  *      iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
00933  *      #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
00934  *      iseq.first_lineno
00935  *      #=> 1
00936  */
00937 static VALUE
00938 iseq_first_lineno(VALUE self)
00939 {
00940     rb_iseq_t *iseq;
00941     GetISeqPtr(self, iseq);
00942     return iseq->location.first_lineno;
00943 }
00944 
00945 static
00946 VALUE iseq_data_to_ary(rb_iseq_t *iseq);
00947 
00948 /*
00949  *  call-seq:
00950  *     iseq.to_a -> ary
00951  *
00952  *  Returns an Array with 14 elements representing the instruction sequence
00953  *  with the following data:
00954  *
00955  *  [magic]
00956  *    A string identifying the data format. <b>Always
00957  *    +YARVInstructionSequence/SimpleDataFormat+.</b>
00958  *
00959  *  [major_version]
00960  *    The major version of the instruction sequence.
00961  *
00962  *  [minor_version]
00963  *    The minor version of the instruction sequence.
00964  *
00965  *  [format_type]
00966  *    A number identifying the data format. <b>Always 1</b>.
00967  *
00968  *  [misc]
00969  *    A hash containing:
00970  *
00971  *    [+:arg_size+]
00972  *      the total number of arguments taken by the method or the block (0 if
00973  *      _iseq_ doesn't represent a method or block)
00974  *    [+:local_size+]
00975  *      the number of local variables + 1
00976  *    [+:stack_max+]
00977  *      used in calculating the stack depth at which a SystemStackError is
00978  *      thrown.
00979  *
00980  *  [#label]
00981  *    The name of the context (block, method, class, module, etc.) that this
00982  *    instruction sequence belongs to.
00983  *
00984  *    <code><main></code> if it's at the top level, <code><compiled></code> if
00985  *    it was evaluated from a string.
00986  *
00987  *  [#path]
00988  *    The relative path to the Ruby file where the instruction sequence was
00989  *    loaded from.
00990  *
00991  *    <code><compiled></code> if the iseq was evaluated from a string.
00992  *
00993  *  [#absolute_path]
00994  *    The absolute path to the Ruby file where the instruction sequence was
00995  *    loaded from.
00996  *
00997  *    +nil+ if the iseq was evaluated from a string.
00998  *
00999  *  [#first_lineno]
01000  *    The number of the first source line where the instruction sequence was
01001  *    loaded from.
01002  *
01003  *  [type]
01004  *    The type of the instruction sequence.
01005  *
01006  *    Valid values are +:top+, +:method+, +:block+, +:class+, +:rescue+,
01007  *    +:ensure+, +:eval+, +:main+, and +:defined_guard+.
01008  *
01009  *  [locals]
01010  *    An array containing the names of all arguments and local variables as
01011  *    symbols.
01012  *
01013  *  [args]
01014  *    The arity if the method or block only has required arguments.
01015  *
01016  *    Otherwise an array of:
01017  *
01018  *      [required_argc, [optional_arg_labels, ...],
01019  *       splat_index, post_splat_argc, post_splat_index,
01020  *       block_index, simple]
01021  *
01022  *    More info about these values can be found in +vm_core.h+.
01023  *
01024  *  [catch_table]
01025  *    A list of exceptions and control flow operators (rescue, next, redo,
01026  *    break, etc.).
01027  *
01028  *  [bytecode]
01029  *    An array of arrays containing the instruction names and operands that
01030  *    make up the body of the instruction sequence.
01031  *
01032  */
01033 static VALUE
01034 iseq_to_a(VALUE self)
01035 {
01036     rb_iseq_t *iseq = iseq_check(self);
01037     rb_secure(1);
01038     return iseq_data_to_ary(iseq);
01039 }
01040 
01041 int
01042 rb_iseq_first_lineno(const rb_iseq_t *iseq)
01043 {
01044     return FIX2INT(iseq->location.first_lineno);
01045 }
01046 
01047 /* TODO: search algorithm is brute force.
01048          this should be binary search or so. */
01049 
01050 static struct iseq_line_info_entry *
01051 get_line_info(const rb_iseq_t *iseq, size_t pos)
01052 {
01053     size_t i = 0, size = iseq->line_info_size;
01054     struct iseq_line_info_entry *table = iseq->line_info_table;
01055     const int debug = 0;
01056 
01057     if (debug) {
01058         printf("size: %"PRIdSIZE"\n", size);
01059         printf("table[%"PRIdSIZE"]: position: %d, line: %d, pos: %"PRIdSIZE"\n",
01060                i, table[i].position, table[i].line_no, pos);
01061     }
01062 
01063     if (size == 0) {
01064         return 0;
01065     }
01066     else if (size == 1) {
01067         return &table[0];
01068     }
01069     else {
01070         for (i=1; i<size; i++) {
01071             if (debug) printf("table[%"PRIdSIZE"]: position: %d, line: %d, pos: %"PRIdSIZE"\n",
01072                               i, table[i].position, table[i].line_no, pos);
01073 
01074             if (table[i].position == pos) {
01075                 return &table[i];
01076             }
01077             if (table[i].position > pos) {
01078                 return &table[i-1];
01079             }
01080         }
01081     }
01082     return &table[i-1];
01083 }
01084 
01085 static unsigned int
01086 find_line_no(const rb_iseq_t *iseq, size_t pos)
01087 {
01088     struct iseq_line_info_entry *entry = get_line_info(iseq, pos);
01089     if (entry) {
01090         return entry->line_no;
01091     }
01092     else {
01093         return 0;
01094     }
01095 }
01096 
01097 unsigned int
01098 rb_iseq_line_no(const rb_iseq_t *iseq, size_t pos)
01099 {
01100     if (pos == 0) {
01101         return find_line_no(iseq, pos);
01102     }
01103     else {
01104         return find_line_no(iseq, pos - 1);
01105     }
01106 }
01107 
01108 static VALUE
01109 id_to_name(ID id, VALUE default_value)
01110 {
01111     VALUE str = rb_id2str(id);
01112     if (!str) {
01113         str = default_value;
01114     }
01115     else if (!rb_str_symname_p(str)) {
01116         str = rb_str_inspect(str);
01117     }
01118     return str;
01119 }
01120 
01121 VALUE
01122 insn_operand_intern(rb_iseq_t *iseq,
01123                     VALUE insn, int op_no, VALUE op,
01124                     int len, size_t pos, VALUE *pnop, VALUE child)
01125 {
01126     const char *types = insn_op_types(insn);
01127     char type = types[op_no];
01128     VALUE ret;
01129 
01130     switch (type) {
01131       case TS_OFFSET:           /* LONG */
01132         ret = rb_sprintf("%"PRIdVALUE, (VALUE)(pos + len + op));
01133         break;
01134 
01135       case TS_NUM:              /* ULONG */
01136         ret = rb_sprintf("%"PRIuVALUE, op);
01137         break;
01138 
01139       case TS_LINDEX:{
01140         if (insn == BIN(getlocal) || insn == BIN(setlocal)) {
01141             if (pnop) {
01142                 rb_iseq_t *diseq = iseq;
01143                 VALUE level = *pnop, i;
01144 
01145                 for (i = 0; i < level; i++) {
01146                     diseq = diseq->parent_iseq;
01147                 }
01148                 ret = id_to_name(diseq->local_table[diseq->local_size - op], INT2FIX('*'));
01149             }
01150             else {
01151                 ret = rb_sprintf("%"PRIuVALUE, op);
01152             }
01153         }
01154         else {
01155             ret = rb_inspect(INT2FIX(op));
01156         }
01157         break;
01158       }
01159       case TS_ID:               /* ID (symbol) */
01160         op = ID2SYM(op);
01161 
01162       case TS_VALUE:            /* VALUE */
01163         op = obj_resurrect(op);
01164         ret = rb_inspect(op);
01165         if (CLASS_OF(op) == rb_cISeq) {
01166             if (child) {
01167                 rb_ary_push(child, op);
01168             }
01169         }
01170         break;
01171 
01172       case TS_ISEQ:             /* iseq */
01173         {
01174             rb_iseq_t *iseq = (rb_iseq_t *)op;
01175             if (iseq) {
01176                 ret = iseq->location.label;
01177                 if (child) {
01178                     rb_ary_push(child, iseq->self);
01179                 }
01180             }
01181             else {
01182                 ret = rb_str_new2("nil");
01183             }
01184             break;
01185         }
01186       case TS_GENTRY:
01187         {
01188             struct rb_global_entry *entry = (struct rb_global_entry *)op;
01189             ret = rb_str_dup(rb_id2str(entry->id));
01190         }
01191         break;
01192 
01193       case TS_IC:
01194         ret = rb_sprintf("<ic:%"PRIdPTRDIFF">", (struct iseq_inline_cache_entry *)op - iseq->ic_entries);
01195         break;
01196 
01197       case TS_CALLINFO:
01198         {
01199             rb_call_info_t *ci = (rb_call_info_t *)op;
01200             VALUE ary = rb_ary_new();
01201 
01202             if (ci->mid) {
01203                 rb_ary_push(ary, rb_sprintf("mid:%s", rb_id2name(ci->mid)));
01204             }
01205 
01206             rb_ary_push(ary, rb_sprintf("argc:%d", ci->orig_argc));
01207 
01208             if (ci->blockiseq) {
01209                 if (child) {
01210                     rb_ary_push(child, ci->blockiseq->self);
01211                 }
01212                 rb_ary_push(ary, rb_sprintf("block:%"PRIsVALUE, ci->blockiseq->location.label));
01213             }
01214 
01215             if (ci->flag) {
01216                 VALUE flags = rb_ary_new();
01217                 if (ci->flag & VM_CALL_ARGS_SPLAT) rb_ary_push(flags, rb_str_new2("ARGS_SPLAT"));
01218                 if (ci->flag & VM_CALL_ARGS_BLOCKARG) rb_ary_push(flags, rb_str_new2("ARGS_BLOCKARG"));
01219                 if (ci->flag & VM_CALL_FCALL) rb_ary_push(flags, rb_str_new2("FCALL"));
01220                 if (ci->flag & VM_CALL_VCALL) rb_ary_push(flags, rb_str_new2("VCALL"));
01221                 if (ci->flag & VM_CALL_TAILCALL) rb_ary_push(flags, rb_str_new2("TAILCALL"));
01222                 if (ci->flag & VM_CALL_SUPER) rb_ary_push(flags, rb_str_new2("SUPER"));
01223                 if (ci->flag & VM_CALL_OPT_SEND) rb_ary_push(flags, rb_str_new2("SNED")); /* maybe not reachable */
01224                 if (ci->flag & VM_CALL_ARGS_SKIP_SETUP) rb_ary_push(flags, rb_str_new2("ARGS_SKIP")); /* maybe not reachable */
01225                 rb_ary_push(ary, rb_ary_join(flags, rb_str_new2("|")));
01226             }
01227             ret = rb_sprintf("<callinfo!%"PRIsVALUE">", rb_ary_join(ary, rb_str_new2(", ")));
01228         }
01229         break;
01230 
01231       case TS_CDHASH:
01232         ret = rb_str_new2("<cdhash>");
01233         break;
01234 
01235       case TS_FUNCPTR:
01236         ret = rb_str_new2("<funcptr>");
01237         break;
01238 
01239       default:
01240         rb_bug("insn_operand_intern: unknown operand type: %c", type);
01241     }
01242     return ret;
01243 }
01244 
01249 int
01250 rb_iseq_disasm_insn(VALUE ret, VALUE *iseq, size_t pos,
01251                     rb_iseq_t *iseqdat, VALUE child)
01252 {
01253     VALUE insn = iseq[pos];
01254     int len = insn_len(insn);
01255     int j;
01256     const char *types = insn_op_types(insn);
01257     VALUE str = rb_str_new(0, 0);
01258     const char *insn_name_buff;
01259 
01260     insn_name_buff = insn_name(insn);
01261     if (1) {
01262         rb_str_catf(str, "%04"PRIdSIZE" %-16s ", pos, insn_name_buff);
01263     }
01264     else {
01265         rb_str_catf(str, "%04"PRIdSIZE" %-16.*s ", pos,
01266                     (int)strcspn(insn_name_buff, "_"), insn_name_buff);
01267     }
01268 
01269     for (j = 0; types[j]; j++) {
01270         const char *types = insn_op_types(insn);
01271         VALUE opstr = insn_operand_intern(iseqdat, insn, j, iseq[pos + j + 1],
01272                                           len, pos, &iseq[pos + j + 2],
01273                                           child);
01274         rb_str_concat(str, opstr);
01275 
01276         if (types[j + 1]) {
01277             rb_str_cat2(str, ", ");
01278         }
01279     }
01280 
01281     {
01282         unsigned int line_no = find_line_no(iseqdat, pos);
01283         unsigned int prev = pos == 0 ? 0 : find_line_no(iseqdat, pos - 1);
01284         if (line_no && line_no != prev) {
01285             long slen = RSTRING_LEN(str);
01286             slen = (slen > 70) ? 0 : (70 - slen);
01287             str = rb_str_catf(str, "%*s(%4d)", (int)slen, "", line_no);
01288         }
01289     }
01290 
01291     if (ret) {
01292         rb_str_cat2(str, "\n");
01293         rb_str_concat(ret, str);
01294     }
01295     else {
01296         printf("%s\n", RSTRING_PTR(str));
01297     }
01298     return len;
01299 }
01300 
01301 static const char *
01302 catch_type(int type)
01303 {
01304     switch (type) {
01305       case CATCH_TYPE_RESCUE:
01306         return "rescue";
01307       case CATCH_TYPE_ENSURE:
01308         return "ensure";
01309       case CATCH_TYPE_RETRY:
01310         return "retry";
01311       case CATCH_TYPE_BREAK:
01312         return "break";
01313       case CATCH_TYPE_REDO:
01314         return "redo";
01315       case CATCH_TYPE_NEXT:
01316         return "next";
01317       default:
01318         rb_bug("unknown catch type (%d)", type);
01319         return 0;
01320     }
01321 }
01322 
01323 /*
01324  *  call-seq:
01325  *     iseq.disasm -> str
01326  *     iseq.disassemble -> str
01327  *
01328  *  Returns the instruction sequence as a +String+ in human readable form.
01329  *
01330  *    puts RubyVM::InstructionSequence.compile('1 + 2').disasm
01331  *
01332  *  Produces:
01333  *
01334  *    == disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
01335  *    0000 trace            1                                               (   1)
01336  *    0002 putobject        1
01337  *    0004 putobject        2
01338  *    0006 opt_plus         <ic:1>
01339  *    0008 leave
01340  */
01341 VALUE
01342 rb_iseq_disasm(VALUE self)
01343 {
01344     rb_iseq_t *iseqdat = iseq_check(self);
01345     VALUE *iseq;
01346     VALUE str = rb_str_new(0, 0);
01347     VALUE child = rb_ary_new();
01348     unsigned long size;
01349     int i;
01350     long l;
01351     ID *tbl;
01352     size_t n;
01353     enum {header_minlen = 72};
01354 
01355     rb_secure(1);
01356 
01357     iseq = iseqdat->iseq;
01358     size = iseqdat->iseq_size;
01359 
01360     rb_str_cat2(str, "== disasm: ");
01361 
01362     rb_str_concat(str, iseq_inspect(iseqdat->self));
01363     if ((l = RSTRING_LEN(str)) < header_minlen) {
01364         rb_str_resize(str, header_minlen);
01365         memset(RSTRING_PTR(str) + l, '=', header_minlen - l);
01366     }
01367     rb_str_cat2(str, "\n");
01368 
01369     /* show catch table information */
01370     if (iseqdat->catch_table_size != 0) {
01371         rb_str_cat2(str, "== catch table\n");
01372     }
01373     for (i = 0; i < iseqdat->catch_table_size; i++) {
01374         struct iseq_catch_table_entry *entry = &iseqdat->catch_table[i];
01375         rb_str_catf(str,
01376                     "| catch type: %-6s st: %04d ed: %04d sp: %04d cont: %04d\n",
01377                     catch_type((int)entry->type), (int)entry->start,
01378                     (int)entry->end, (int)entry->sp, (int)entry->cont);
01379         if (entry->iseq) {
01380             rb_str_concat(str, rb_iseq_disasm(entry->iseq));
01381         }
01382     }
01383     if (iseqdat->catch_table_size != 0) {
01384         rb_str_cat2(str, "|-------------------------------------"
01385                     "-----------------------------------\n");
01386     }
01387 
01388     /* show local table information */
01389     tbl = iseqdat->local_table;
01390 
01391     if (tbl) {
01392         rb_str_catf(str,
01393                     "local table (size: %d, argc: %d "
01394                     "[opts: %d, rest: %d, post: %d, block: %d] s%d)\n",
01395                     iseqdat->local_size, iseqdat->argc,
01396                     iseqdat->arg_opts, iseqdat->arg_rest,
01397                     iseqdat->arg_post_len, iseqdat->arg_block,
01398                     iseqdat->arg_simple);
01399 
01400         for (i = 0; i < iseqdat->local_table_size; i++) {
01401             long width;
01402             VALUE name = id_to_name(tbl[i], 0);
01403             char argi[0x100] = "";
01404             char opti[0x100] = "";
01405 
01406             if (iseqdat->arg_opts) {
01407                 int argc = iseqdat->argc;
01408                 int opts = iseqdat->arg_opts;
01409                 if (i >= argc && i < argc + opts - 1) {
01410                     snprintf(opti, sizeof(opti), "Opt=%"PRIdVALUE,
01411                              iseqdat->arg_opt_table[i - argc]);
01412                 }
01413             }
01414 
01415             snprintf(argi, sizeof(argi), "%s%s%s%s%s",  /* arg, opts, rest, post  block */
01416                      iseqdat->argc > i ? "Arg" : "",
01417                      opti,
01418                      iseqdat->arg_rest == i ? "Rest" : "",
01419                      (iseqdat->arg_post_start <= i &&
01420                       i < iseqdat->arg_post_start + iseqdat->arg_post_len) ? "Post" : "",
01421                      iseqdat->arg_block == i ? "Block" : "");
01422 
01423             rb_str_catf(str, "[%2d] ", iseqdat->local_size - i);
01424             width = RSTRING_LEN(str) + 11;
01425             if (name)
01426                 rb_str_append(str, name);
01427             else
01428                 rb_str_cat2(str, "?");
01429             if (*argi) rb_str_catf(str, "<%s>", argi);
01430             if ((width -= RSTRING_LEN(str)) > 0) rb_str_catf(str, "%*s", (int)width, "");
01431         }
01432         rb_str_cat2(str, "\n");
01433     }
01434 
01435     /* show each line */
01436     for (n = 0; n < size;) {
01437         n += rb_iseq_disasm_insn(str, iseq, n, iseqdat, child);
01438     }
01439 
01440     for (i = 0; i < RARRAY_LEN(child); i++) {
01441         VALUE isv = rb_ary_entry(child, i);
01442         rb_str_concat(str, rb_iseq_disasm(isv));
01443     }
01444 
01445     return str;
01446 }
01447 
01448 /*
01449  *  Returns the instruction sequence containing the given proc or method.
01450  *
01451  *  For example, using irb:
01452  *
01453  *      # a proc
01454  *      > p = proc { num = 1 + 2 }
01455  *      > RubyVM::InstructionSequence.of(p)
01456  *      > #=> <RubyVM::InstructionSequence:block in irb_binding@(irb)>
01457  *
01458  *      # for a method
01459  *      > def foo(bar); puts bar; end
01460  *      > RubyVM::InstructionSequence.of(method(:foo))
01461  *      > #=> <RubyVM::InstructionSequence:foo@(irb)>
01462  *
01463  *  Using ::compile_file:
01464  *
01465  *      # /tmp/iseq_of.rb
01466  *      def hello
01467  *        puts "hello, world"
01468  *      end
01469  *
01470  *      $a_global_proc = proc { str = 'a' + 'b' }
01471  *
01472  *      # in irb
01473  *      > require '/tmp/iseq_of.rb'
01474  *
01475  *      # first the method hello
01476  *      > RubyVM::InstructionSequence.of(method(:hello))
01477  *      > #=> #<RubyVM::InstructionSequence:0x007fb73d7cb1d0>
01478  *
01479  *      # then the global proc
01480  *      > RubyVM::InstructionSequence.of($a_global_proc)
01481  *      > #=> #<RubyVM::InstructionSequence:0x007fb73d7caf78>
01482  */
01483 static VALUE
01484 iseq_s_of(VALUE klass, VALUE body)
01485 {
01486     VALUE ret = Qnil;
01487     rb_iseq_t *iseq;
01488 
01489     rb_secure(1);
01490 
01491     if (rb_obj_is_proc(body)) {
01492         rb_proc_t *proc;
01493         GetProcPtr(body, proc);
01494         iseq = proc->block.iseq;
01495         if (RUBY_VM_NORMAL_ISEQ_P(iseq)) {
01496             ret = iseq->self;
01497         }
01498     }
01499     else if ((iseq = rb_method_get_iseq(body)) != 0) {
01500         ret = iseq->self;
01501     }
01502     return ret;
01503 }
01504 
01505 /*
01506  *  call-seq:
01507  *     InstructionSequence.disasm(body) -> str
01508  *     InstructionSequence.disassemble(body) -> str
01509  *
01510  *  Takes +body+, a Method or Proc object, and returns a String with the
01511  *  human readable instructions for +body+.
01512  *
01513  *  For a Method object:
01514  *
01515  *    # /tmp/method.rb
01516  *    def hello
01517  *      puts "hello, world"
01518  *    end
01519  *
01520  *    puts RubyVM::InstructionSequence.disasm(method(:hello))
01521  *
01522  *  Produces:
01523  *
01524  *    == disasm: <RubyVM::InstructionSequence:hello@/tmp/method.rb>============
01525  *    0000 trace            8                                               (   1)
01526  *    0002 trace            1                                               (   2)
01527  *    0004 putself
01528  *    0005 putstring        "hello, world"
01529  *    0007 send             :puts, 1, nil, 8, <ic:0>
01530  *    0013 trace            16                                              (   3)
01531  *    0015 leave                                                            (   2)
01532  *
01533  *  For a Proc:
01534  *
01535  *    # /tmp/proc.rb
01536  *    p = proc { num = 1 + 2 }
01537  *    puts RubyVM::InstructionSequence.disasm(p)
01538  *
01539  *  Produces:
01540  *
01541  *    == disasm: <RubyVM::InstructionSequence:block in <main>@/tmp/proc.rb>===
01542  *    == catch table
01543  *    | catch type: redo   st: 0000 ed: 0012 sp: 0000 cont: 0000
01544  *    | catch type: next   st: 0000 ed: 0012 sp: 0000 cont: 0012
01545  *    |------------------------------------------------------------------------
01546  *    local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
01547  *    [ 2] num
01548  *    0000 trace            1                                               (   1)
01549  *    0002 putobject        1
01550  *    0004 putobject        2
01551  *    0006 opt_plus         <ic:1>
01552  *    0008 dup
01553  *    0009 setlocal         num, 0
01554  *    0012 leave
01555  *
01556  */
01557 
01558 static VALUE
01559 iseq_s_disasm(VALUE klass, VALUE body)
01560 {
01561     VALUE iseqval = iseq_s_of(klass, body);
01562     return NIL_P(iseqval) ? Qnil : rb_iseq_disasm(iseqval);
01563 }
01564 
01565 const char *
01566 ruby_node_name(int node)
01567 {
01568     switch (node) {
01569 #include "node_name.inc"
01570       default:
01571         rb_bug("unknown node (%d)", node);
01572         return 0;
01573     }
01574 }
01575 
01576 #define DECL_SYMBOL(name) \
01577   static VALUE sym_##name
01578 
01579 #define INIT_SYMBOL(name) \
01580   sym_##name = ID2SYM(rb_intern(#name))
01581 
01582 static VALUE
01583 register_label(struct st_table *table, unsigned long idx)
01584 {
01585     VALUE sym;
01586     char buff[8 + (sizeof(idx) * CHAR_BIT * 32 / 100)];
01587 
01588     snprintf(buff, sizeof(buff), "label_%lu", idx);
01589     sym = ID2SYM(rb_intern(buff));
01590     st_insert(table, idx, sym);
01591     return sym;
01592 }
01593 
01594 static VALUE
01595 exception_type2symbol(VALUE type)
01596 {
01597     ID id;
01598     switch (type) {
01599       case CATCH_TYPE_RESCUE: CONST_ID(id, "rescue"); break;
01600       case CATCH_TYPE_ENSURE: CONST_ID(id, "ensure"); break;
01601       case CATCH_TYPE_RETRY:  CONST_ID(id, "retry");  break;
01602       case CATCH_TYPE_BREAK:  CONST_ID(id, "break");  break;
01603       case CATCH_TYPE_REDO:   CONST_ID(id, "redo");   break;
01604       case CATCH_TYPE_NEXT:   CONST_ID(id, "next");   break;
01605       default:
01606         rb_bug("...");
01607     }
01608     return ID2SYM(id);
01609 }
01610 
01611 static int
01612 cdhash_each(VALUE key, VALUE value, VALUE ary)
01613 {
01614     rb_ary_push(ary, obj_resurrect(key));
01615     rb_ary_push(ary, value);
01616     return ST_CONTINUE;
01617 }
01618 
01619 static VALUE
01620 iseq_data_to_ary(rb_iseq_t *iseq)
01621 {
01622     long i;
01623     size_t ti;
01624     unsigned int pos;
01625     unsigned int line = 0;
01626     VALUE *seq;
01627 
01628     VALUE val = rb_ary_new();
01629     VALUE type; /* Symbol */
01630     VALUE locals = rb_ary_new();
01631     VALUE args = rb_ary_new();
01632     VALUE body = rb_ary_new(); /* [[:insn1, ...], ...] */
01633     VALUE nbody;
01634     VALUE exception = rb_ary_new(); /* [[....]] */
01635     VALUE misc = rb_hash_new();
01636 
01637     static VALUE insn_syms[VM_INSTRUCTION_SIZE];
01638     struct st_table *labels_table = st_init_numtable();
01639 
01640     DECL_SYMBOL(top);
01641     DECL_SYMBOL(method);
01642     DECL_SYMBOL(block);
01643     DECL_SYMBOL(class);
01644     DECL_SYMBOL(rescue);
01645     DECL_SYMBOL(ensure);
01646     DECL_SYMBOL(eval);
01647     DECL_SYMBOL(main);
01648     DECL_SYMBOL(defined_guard);
01649 
01650     if (sym_top == 0) {
01651         int i;
01652         for (i=0; i<VM_INSTRUCTION_SIZE; i++) {
01653             insn_syms[i] = ID2SYM(rb_intern(insn_name(i)));
01654         }
01655         INIT_SYMBOL(top);
01656         INIT_SYMBOL(method);
01657         INIT_SYMBOL(block);
01658         INIT_SYMBOL(class);
01659         INIT_SYMBOL(rescue);
01660         INIT_SYMBOL(ensure);
01661         INIT_SYMBOL(eval);
01662         INIT_SYMBOL(main);
01663         INIT_SYMBOL(defined_guard);
01664     }
01665 
01666     /* type */
01667     switch (iseq->type) {
01668       case ISEQ_TYPE_TOP:    type = sym_top;    break;
01669       case ISEQ_TYPE_METHOD: type = sym_method; break;
01670       case ISEQ_TYPE_BLOCK:  type = sym_block;  break;
01671       case ISEQ_TYPE_CLASS:  type = sym_class;  break;
01672       case ISEQ_TYPE_RESCUE: type = sym_rescue; break;
01673       case ISEQ_TYPE_ENSURE: type = sym_ensure; break;
01674       case ISEQ_TYPE_EVAL:   type = sym_eval;   break;
01675       case ISEQ_TYPE_MAIN:   type = sym_main;   break;
01676       case ISEQ_TYPE_DEFINED_GUARD: type = sym_defined_guard; break;
01677       default: rb_bug("unsupported iseq type");
01678     };
01679 
01680     /* locals */
01681     for (i=0; i<iseq->local_table_size; i++) {
01682         ID lid = iseq->local_table[i];
01683         if (lid) {
01684             if (rb_id2str(lid)) rb_ary_push(locals, ID2SYM(lid));
01685         }
01686         else {
01687             rb_ary_push(locals, ID2SYM(rb_intern("#arg_rest")));
01688         }
01689     }
01690 
01691     /* args */
01692     {
01693         /*
01694          * [argc,                 # argc
01695          *  [label1, label2, ...] # opts
01696          *  rest index,
01697          *  post_len
01698          *  post_start
01699          *  block index,
01700          *  simple,
01701          * ]
01702          */
01703         VALUE arg_opt_labels = rb_ary_new();
01704         int j;
01705 
01706         for (j=0; j<iseq->arg_opts; j++) {
01707             rb_ary_push(arg_opt_labels,
01708                         register_label(labels_table, iseq->arg_opt_table[j]));
01709         }
01710 
01711         /* commit */
01712         if (iseq->arg_simple == 1) {
01713             args = INT2FIX(iseq->argc);
01714         }
01715         else {
01716             rb_ary_push(args, INT2FIX(iseq->argc));
01717             rb_ary_push(args, arg_opt_labels);
01718             rb_ary_push(args, INT2FIX(iseq->arg_post_len));
01719             rb_ary_push(args, INT2FIX(iseq->arg_post_start));
01720             rb_ary_push(args, INT2FIX(iseq->arg_rest));
01721             rb_ary_push(args, INT2FIX(iseq->arg_block));
01722             rb_ary_push(args, INT2FIX(iseq->arg_simple));
01723         }
01724     }
01725 
01726     /* body */
01727     for (seq = iseq->iseq; seq < iseq->iseq + iseq->iseq_size; ) {
01728         VALUE insn = *seq++;
01729         int j, len = insn_len(insn);
01730         VALUE *nseq = seq + len - 1;
01731         VALUE ary = rb_ary_new2(len);
01732 
01733         rb_ary_push(ary, insn_syms[insn]);
01734         for (j=0; j<len-1; j++, seq++) {
01735             switch (insn_op_type(insn, j)) {
01736               case TS_OFFSET: {
01737                 unsigned long idx = nseq - iseq->iseq + *seq;
01738                 rb_ary_push(ary, register_label(labels_table, idx));
01739                 break;
01740               }
01741               case TS_LINDEX:
01742               case TS_NUM:
01743                 rb_ary_push(ary, INT2FIX(*seq));
01744                 break;
01745               case TS_VALUE:
01746                 rb_ary_push(ary, obj_resurrect(*seq));
01747                 break;
01748               case TS_ISEQ:
01749                 {
01750                     rb_iseq_t *iseq = (rb_iseq_t *)*seq;
01751                     if (iseq) {
01752                         VALUE val = iseq_data_to_ary(iseq);
01753                         rb_ary_push(ary, val);
01754                     }
01755                     else {
01756                         rb_ary_push(ary, Qnil);
01757                     }
01758                 }
01759                 break;
01760               case TS_GENTRY:
01761                 {
01762                     struct rb_global_entry *entry = (struct rb_global_entry *)*seq;
01763                     rb_ary_push(ary, ID2SYM(entry->id));
01764                 }
01765                 break;
01766               case TS_IC: {
01767                   struct iseq_inline_cache_entry *ic = (struct iseq_inline_cache_entry *)*seq;
01768                     rb_ary_push(ary, INT2FIX(ic - iseq->ic_entries));
01769                 }
01770                 break;
01771               case TS_CALLINFO:
01772                 {
01773                     rb_call_info_t *ci = (rb_call_info_t *)*seq;
01774                     VALUE e = rb_hash_new();
01775                     rb_hash_aset(e, ID2SYM(rb_intern("mid")), ci->mid ? ID2SYM(ci->mid) : Qnil);
01776                     rb_hash_aset(e, ID2SYM(rb_intern("flag")), ULONG2NUM(ci->flag));
01777                     rb_hash_aset(e, ID2SYM(rb_intern("orig_argc")), INT2FIX(ci->orig_argc));
01778                     rb_hash_aset(e, ID2SYM(rb_intern("blockptr")), ci->blockiseq ? iseq_data_to_ary(ci->blockiseq) : Qnil);
01779                     rb_ary_push(ary, e);
01780                 }
01781                 break;
01782               case TS_ID:
01783                 rb_ary_push(ary, ID2SYM(*seq));
01784                 break;
01785               case TS_CDHASH:
01786                 {
01787                     VALUE hash = *seq;
01788                     VALUE val = rb_ary_new();
01789                     int i;
01790 
01791                     rb_hash_foreach(hash, cdhash_each, val);
01792 
01793                     for (i=0; i<RARRAY_LEN(val); i+=2) {
01794                         VALUE pos = FIX2INT(rb_ary_entry(val, i+1));
01795                         unsigned long idx = nseq - iseq->iseq + pos;
01796 
01797                         rb_ary_store(val, i+1,
01798                                      register_label(labels_table, idx));
01799                     }
01800                     rb_ary_push(ary, val);
01801                 }
01802                 break;
01803               default:
01804                 rb_bug("unknown operand: %c", insn_op_type(insn, j));
01805             }
01806         }
01807         rb_ary_push(body, ary);
01808     }
01809 
01810     nbody = body;
01811 
01812     /* exception */
01813     for (i=0; i<iseq->catch_table_size; i++) {
01814         VALUE ary = rb_ary_new();
01815         struct iseq_catch_table_entry *entry = &iseq->catch_table[i];
01816         rb_ary_push(ary, exception_type2symbol(entry->type));
01817         if (entry->iseq) {
01818             rb_iseq_t *eiseq;
01819             GetISeqPtr(entry->iseq, eiseq);
01820             rb_ary_push(ary, iseq_data_to_ary(eiseq));
01821         }
01822         else {
01823             rb_ary_push(ary, Qnil);
01824         }
01825         rb_ary_push(ary, register_label(labels_table, entry->start));
01826         rb_ary_push(ary, register_label(labels_table, entry->end));
01827         rb_ary_push(ary, register_label(labels_table, entry->cont));
01828         rb_ary_push(ary, INT2FIX(entry->sp));
01829         rb_ary_push(exception, ary);
01830     }
01831 
01832     /* make body with labels and insert line number */
01833     body = rb_ary_new();
01834     ti = 0;
01835 
01836     for (i=0, pos=0; i<RARRAY_LEN(nbody); i++) {
01837         VALUE ary = RARRAY_PTR(nbody)[i];
01838         st_data_t label;
01839 
01840         if (st_lookup(labels_table, pos, &label)) {
01841             rb_ary_push(body, (VALUE)label);
01842         }
01843 
01844         if (ti < iseq->line_info_size && iseq->line_info_table[ti].position == pos) {
01845             line = iseq->line_info_table[ti].line_no;
01846             rb_ary_push(body, INT2FIX(line));
01847             ti++;
01848         }
01849 
01850         rb_ary_push(body, ary);
01851         pos += RARRAY_LENINT(ary); /* reject too huge data */
01852     }
01853 
01854     st_free_table(labels_table);
01855 
01856     rb_hash_aset(misc, ID2SYM(rb_intern("arg_size")), INT2FIX(iseq->arg_size));
01857     rb_hash_aset(misc, ID2SYM(rb_intern("local_size")), INT2FIX(iseq->local_size));
01858     rb_hash_aset(misc, ID2SYM(rb_intern("stack_max")), INT2FIX(iseq->stack_max));
01859 
01860     /* TODO: compatibility issue */
01861     /*
01862      * [:magic, :major_version, :minor_version, :format_type, :misc,
01863      *  :name, :path, :absolute_path, :start_lineno, :type, :locals, :args,
01864      *  :catch_table, :bytecode]
01865      */
01866     rb_ary_push(val, rb_str_new2("YARVInstructionSequence/SimpleDataFormat"));
01867     rb_ary_push(val, INT2FIX(ISEQ_MAJOR_VERSION)); /* major */
01868     rb_ary_push(val, INT2FIX(ISEQ_MINOR_VERSION)); /* minor */
01869     rb_ary_push(val, INT2FIX(1));
01870     rb_ary_push(val, misc);
01871     rb_ary_push(val, iseq->location.label);
01872     rb_ary_push(val, iseq->location.path);
01873     rb_ary_push(val, iseq->location.absolute_path);
01874     rb_ary_push(val, iseq->location.first_lineno);
01875     rb_ary_push(val, type);
01876     rb_ary_push(val, locals);
01877     rb_ary_push(val, args);
01878     rb_ary_push(val, exception);
01879     rb_ary_push(val, body);
01880     return val;
01881 }
01882 
01883 VALUE
01884 rb_iseq_clone(VALUE iseqval, VALUE newcbase)
01885 {
01886     VALUE newiseq = iseq_alloc(rb_cISeq);
01887     rb_iseq_t *iseq0, *iseq1;
01888 
01889     GetISeqPtr(iseqval, iseq0);
01890     GetISeqPtr(newiseq, iseq1);
01891 
01892     *iseq1 = *iseq0;
01893     iseq1->self = newiseq;
01894     if (!iseq1->orig) {
01895         iseq1->orig = iseqval;
01896     }
01897     if (iseq0->local_iseq == iseq0) {
01898         iseq1->local_iseq = iseq1;
01899     }
01900     if (newcbase) {
01901         iseq1->cref_stack = NEW_CREF(newcbase);
01902         iseq1->cref_stack->nd_refinements = iseq0->cref_stack->nd_refinements;
01903         iseq1->cref_stack->nd_visi = iseq0->cref_stack->nd_visi;
01904         if (iseq0->cref_stack->nd_next) {
01905             iseq1->cref_stack->nd_next = iseq0->cref_stack->nd_next;
01906         }
01907         iseq1->klass = newcbase;
01908     }
01909 
01910     return newiseq;
01911 }
01912 
01913 VALUE
01914 rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc)
01915 {
01916     int i, r;
01917     VALUE a, args = rb_ary_new2(iseq->arg_size);
01918     ID req, opt, rest, block, key, keyrest;
01919 #define PARAM_TYPE(type) rb_ary_push(a = rb_ary_new2(2), ID2SYM(type))
01920 #define PARAM_ID(i) iseq->local_table[(i)]
01921 #define PARAM(i, type) (                      \
01922         PARAM_TYPE(type),                     \
01923         rb_id2str(PARAM_ID(i)) ?              \
01924         rb_ary_push(a, ID2SYM(PARAM_ID(i))) : \
01925         a)
01926 
01927     CONST_ID(req, "req");
01928     CONST_ID(opt, "opt");
01929     if (is_proc) {
01930         for (i = 0; i < iseq->argc; i++) {
01931             PARAM_TYPE(opt);
01932             rb_ary_push(a, rb_id2str(PARAM_ID(i)) ? ID2SYM(PARAM_ID(i)) : Qnil);
01933             rb_ary_push(args, a);
01934         }
01935     }
01936     else {
01937         for (i = 0; i < iseq->argc; i++) {
01938             rb_ary_push(args, PARAM(i, req));
01939         }
01940     }
01941     r = iseq->argc + iseq->arg_opts - 1;
01942     for (; i < r; i++) {
01943         PARAM_TYPE(opt);
01944         if (rb_id2str(PARAM_ID(i))) {
01945             rb_ary_push(a, ID2SYM(PARAM_ID(i)));
01946         }
01947         rb_ary_push(args, a);
01948     }
01949     if (iseq->arg_rest != -1) {
01950         CONST_ID(rest, "rest");
01951         rb_ary_push(args, PARAM(iseq->arg_rest, rest));
01952     }
01953     r = iseq->arg_post_start + iseq->arg_post_len;
01954     if (is_proc) {
01955         for (i = iseq->arg_post_start; i < r; i++) {
01956             PARAM_TYPE(opt);
01957             rb_ary_push(a, rb_id2str(PARAM_ID(i)) ? ID2SYM(PARAM_ID(i)) : Qnil);
01958             rb_ary_push(args, a);
01959         }
01960     }
01961     else {
01962         for (i = iseq->arg_post_start; i < r; i++) {
01963             rb_ary_push(args, PARAM(i, req));
01964         }
01965     }
01966     if (iseq->arg_keyword != -1) {
01967         CONST_ID(key, "key");
01968         for (i = 0; i < iseq->arg_keywords; i++) {
01969             PARAM_TYPE(key);
01970             if (rb_id2str(iseq->arg_keyword_table[i])) {
01971                 rb_ary_push(a, ID2SYM(iseq->arg_keyword_table[i]));
01972             }
01973             rb_ary_push(args, a);
01974         }
01975         if (rb_id2str(iseq->local_table[iseq->arg_keyword])) {
01976             CONST_ID(keyrest, "keyrest");
01977             rb_ary_push(args, PARAM(iseq->arg_keyword, keyrest));
01978         }
01979     }
01980     if (iseq->arg_block != -1) {
01981         CONST_ID(block, "block");
01982         rb_ary_push(args, PARAM(iseq->arg_block, block));
01983     }
01984     return args;
01985 }
01986 
01987 VALUE
01988 rb_iseq_defined_string(enum defined_type type)
01989 {
01990     static const char expr_names[][18] = {
01991         "nil",
01992         "instance-variable",
01993         "local-variable",
01994         "global-variable",
01995         "class variable",
01996         "constant",
01997         "method",
01998         "yield",
01999         "super",
02000         "self",
02001         "true",
02002         "false",
02003         "assignment",
02004         "expression",
02005     };
02006     const char *estr;
02007     VALUE *defs, str;
02008 
02009     if ((unsigned)(type - 1) >= (unsigned)numberof(expr_names)) return 0;
02010     estr = expr_names[type - 1];
02011     if (!estr[0]) return 0;
02012     defs = GET_VM()->defined_strings;
02013     if (!defs) {
02014         defs = ruby_xcalloc(numberof(expr_names), sizeof(VALUE));
02015         GET_VM()->defined_strings = defs;
02016     }
02017     str = defs[type-1];
02018     if (!str) {
02019         str = rb_str_new_cstr(estr);;
02020         OBJ_FREEZE(str);
02021         defs[type-1] = str;
02022     }
02023     return str;
02024 }
02025 
02026 /* ruby2cext */
02027 
02028 VALUE
02029 rb_iseq_build_for_ruby2cext(
02030     const rb_iseq_t *iseq_template,
02031     const rb_insn_func_t *func,
02032     const struct iseq_line_info_entry *line_info_table,
02033     const char **local_table,
02034     const VALUE *arg_opt_table,
02035     const struct iseq_catch_table_entry *catch_table,
02036     const char *name,
02037     const char *path,
02038     const unsigned short first_lineno)
02039 {
02040     unsigned long i;
02041     VALUE iseqval = iseq_alloc(rb_cISeq);
02042     rb_iseq_t *iseq;
02043     GetISeqPtr(iseqval, iseq);
02044 
02045     /* copy iseq */
02046     *iseq = *iseq_template;
02047     iseq->location.label = rb_str_new2(name);
02048     iseq->location.path = rb_str_new2(path);
02049     iseq->location.first_lineno = first_lineno;
02050     iseq->mark_ary = rb_ary_tmp_new(3);
02051     OBJ_UNTRUST(iseq->mark_ary);
02052     iseq->self = iseqval;
02053 
02054     iseq->iseq = ALLOC_N(VALUE, iseq->iseq_size);
02055 
02056     for (i=0; i<iseq->iseq_size; i+=2) {
02057         iseq->iseq[i] = BIN(opt_call_c_function);
02058         iseq->iseq[i+1] = (VALUE)func;
02059     }
02060 
02061     rb_iseq_translate_threaded_code(iseq);
02062 
02063 #define ALLOC_AND_COPY(dst, src, type, size) do { \
02064   if (size) { \
02065       (dst) = ALLOC_N(type, (size)); \
02066       MEMCPY((dst), (src), type, (size)); \
02067   } \
02068 } while (0)
02069 
02070     ALLOC_AND_COPY(iseq->line_info_table, line_info_table,
02071                    struct iseq_line_info_entry, iseq->line_info_size);
02072 
02073     ALLOC_AND_COPY(iseq->catch_table, catch_table,
02074                    struct iseq_catch_table_entry, iseq->catch_table_size);
02075 
02076     ALLOC_AND_COPY(iseq->arg_opt_table, arg_opt_table,
02077                    VALUE, iseq->arg_opts);
02078 
02079     set_relation(iseq, 0);
02080 
02081     return iseqval;
02082 }
02083 
02084 /* Experimental tracing support: trace(line) -> trace(specified_line)
02085  * MRI Specific.
02086  */
02087 
02088 int
02089 rb_iseq_line_trace_each(VALUE iseqval, int (*func)(int line, rb_event_flag_t *events_ptr, void *d), void *data)
02090 {
02091     int trace_num = 0;
02092     size_t pos, insn;
02093     rb_iseq_t *iseq;
02094     int cont = 1;
02095     GetISeqPtr(iseqval, iseq);
02096 
02097     for (pos = 0; cont && pos < iseq->iseq_size; pos += insn_len(insn)) {
02098         insn = iseq->iseq[pos];
02099 
02100         if (insn == BIN(trace)) {
02101             rb_event_flag_t current_events = (VALUE)iseq->iseq[pos+1];
02102 
02103             if (current_events & RUBY_EVENT_LINE) {
02104                 rb_event_flag_t events = current_events & RUBY_EVENT_SPECIFIED_LINE;
02105                 trace_num++;
02106 
02107                 if (func) {
02108                     int line = find_line_no(iseq, pos);
02109                     /* printf("line: %d\n", line); */
02110                     cont = (*func)(line, &events, data);
02111                     if (current_events != events) {
02112                         iseq->iseq[pos+1] = iseq->iseq_encoded[pos+1] =
02113                           (VALUE)(current_events | (events & RUBY_EVENT_SPECIFIED_LINE));
02114                     }
02115                 }
02116             }
02117         }
02118     }
02119     return trace_num;
02120 }
02121 
02122 static int
02123 collect_trace(int line, rb_event_flag_t *events_ptr, void *ptr)
02124 {
02125     VALUE result = (VALUE)ptr;
02126     rb_ary_push(result, INT2NUM(line));
02127     return 1;
02128 }
02129 
02130 /*
02131  * <b>Experimental MRI specific feature, only available as C level api.</b>
02132  *
02133  * Returns all +specified_line+ events.
02134  */
02135 VALUE
02136 rb_iseq_line_trace_all(VALUE iseqval)
02137 {
02138     VALUE result = rb_ary_new();
02139     rb_iseq_line_trace_each(iseqval, collect_trace, (void *)result);
02140     return result;
02141 }
02142 
02143 struct set_specifc_data {
02144     int pos;
02145     int set;
02146     int prev; /* 1: set, 2: unset, 0: not found */
02147 };
02148 
02149 static int
02150 line_trace_specify(int line, rb_event_flag_t *events_ptr, void *ptr)
02151 {
02152     struct set_specifc_data *data = (struct set_specifc_data *)ptr;
02153 
02154     if (data->pos == 0) {
02155         data->prev = *events_ptr & RUBY_EVENT_SPECIFIED_LINE ? 1 : 2;
02156         if (data->set) {
02157             *events_ptr = *events_ptr | RUBY_EVENT_SPECIFIED_LINE;
02158         }
02159         else {
02160             *events_ptr = *events_ptr & ~RUBY_EVENT_SPECIFIED_LINE;
02161         }
02162         return 0; /* found */
02163     }
02164     else {
02165         data->pos--;
02166         return 1;
02167     }
02168 }
02169 
02170 /*
02171  * <b>Experimental MRI specific feature, only available as C level api.</b>
02172  *
02173  * Set a +specified_line+ event at the given line position, if the +set+
02174  * parameter is +true+.
02175  *
02176  * This method is useful for building a debugger breakpoint at a specific line.
02177  *
02178  * A TypeError is raised if +set+ is not boolean.
02179  *
02180  * If +pos+ is a negative integer a TypeError exception is raised.
02181  */
02182 VALUE
02183 rb_iseq_line_trace_specify(VALUE iseqval, VALUE pos, VALUE set)
02184 {
02185     struct set_specifc_data data;
02186 
02187     data.prev = 0;
02188     data.pos = NUM2INT(pos);
02189     if (data.pos < 0) rb_raise(rb_eTypeError, "`pos' is negative");
02190 
02191     switch (set) {
02192       case Qtrue:  data.set = 1; break;
02193       case Qfalse: data.set = 0; break;
02194       default:
02195         rb_raise(rb_eTypeError, "`set' should be true/false");
02196     }
02197 
02198     rb_iseq_line_trace_each(iseqval, line_trace_specify, (void *)&data);
02199 
02200     if (data.prev == 0) {
02201         rb_raise(rb_eTypeError, "`pos' is out of range.");
02202     }
02203     return data.prev == 1 ? Qtrue : Qfalse;
02204 }
02205 
02206 /*
02207  *  Document-class: RubyVM::InstructionSequence
02208  *
02209  *  The InstructionSequence class represents a compiled sequence of
02210  *  instructions for the Ruby Virtual Machine.
02211  *
02212  *  With it, you can get a handle to the instructions that make up a method or
02213  *  a proc, compile strings of Ruby code down to VM instructions, and
02214  *  disassemble instruction sequences to strings for easy inspection. It is
02215  *  mostly useful if you want to learn how the Ruby VM works, but it also lets
02216  *  you control various settings for the Ruby iseq compiler.
02217  *
02218  *  You can find the source for the VM instructions in +insns.def+ in the Ruby
02219  *  source.
02220  *
02221  *  The instruction sequence results will almost certainly change as Ruby
02222  *  changes, so example output in this documentation may be different from what
02223  *  you see.
02224  */
02225 
02226 void
02227 Init_ISeq(void)
02228 {
02229     /* declare ::RubyVM::InstructionSequence */
02230     rb_cISeq = rb_define_class_under(rb_cRubyVM, "InstructionSequence", rb_cObject);
02231     rb_define_alloc_func(rb_cISeq, iseq_alloc);
02232     rb_define_method(rb_cISeq, "inspect", iseq_inspect, 0);
02233     rb_define_method(rb_cISeq, "disasm", rb_iseq_disasm, 0);
02234     rb_define_method(rb_cISeq, "disassemble", rb_iseq_disasm, 0);
02235     rb_define_method(rb_cISeq, "to_a", iseq_to_a, 0);
02236     rb_define_method(rb_cISeq, "eval", iseq_eval, 0);
02237 
02238     /* location APIs */
02239     rb_define_method(rb_cISeq, "path", iseq_path, 0);
02240     rb_define_method(rb_cISeq, "absolute_path", iseq_absolute_path, 0);
02241     rb_define_method(rb_cISeq, "label", iseq_label, 0);
02242     rb_define_method(rb_cISeq, "base_label", iseq_base_label, 0);
02243     rb_define_method(rb_cISeq, "first_lineno", iseq_first_lineno, 0);
02244 
02245 #if 0
02246     /* Now, it is experimental. No discussions, no tests. */
02247     /* They can be used from C level. Please give us feedback. */
02248     rb_define_method(rb_cISeq, "line_trace_all", rb_iseq_line_trace_all, 0);
02249     rb_define_method(rb_cISeq, "line_trace_specify", rb_iseq_line_trace_specify, 2);
02250 #else
02251     (void)rb_iseq_line_trace_all;
02252     (void)rb_iseq_line_trace_specify;
02253 #endif
02254 
02255 #if 0 /* TBD */
02256     rb_define_private_method(rb_cISeq, "marshal_dump", iseq_marshal_dump, 0);
02257     rb_define_private_method(rb_cISeq, "marshal_load", iseq_marshal_load, 1);
02258 #endif
02259 
02260     /* disable this feature because there is no verifier. */
02261     /* rb_define_singleton_method(rb_cISeq, "load", iseq_s_load, -1); */
02262     (void)iseq_s_load;
02263 
02264     rb_define_singleton_method(rb_cISeq, "compile", iseq_s_compile, -1);
02265     rb_define_singleton_method(rb_cISeq, "new", iseq_s_compile, -1);
02266     rb_define_singleton_method(rb_cISeq, "compile_file", iseq_s_compile_file, -1);
02267     rb_define_singleton_method(rb_cISeq, "compile_option", iseq_s_compile_option_get, 0);
02268     rb_define_singleton_method(rb_cISeq, "compile_option=", iseq_s_compile_option_set, 1);
02269     rb_define_singleton_method(rb_cISeq, "disasm", iseq_s_disasm, 1);
02270     rb_define_singleton_method(rb_cISeq, "disassemble", iseq_s_disasm, 1);
02271     rb_define_singleton_method(rb_cISeq, "of", iseq_s_of, 1);
02272 }
02273