mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	 2f522b9cc6
			
		
	
	
		2f522b9cc6
		
	
	
	
	
		
			
			variable.c, vm.c, vm_core.c, vm_insnhelper.c, vm_insnhelper.h, vm_method.c: Implement class hierarchy method cache invalidation. [ruby-core:55053] [Feature #8426] [GH-387] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@42822 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
		
			
				
	
	
		
			5918 lines
		
	
	
	
		
			144 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			5918 lines
		
	
	
	
		
			144 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /**********************************************************************
 | |
| 
 | |
|   compile.c - ruby node tree -> VM instruction sequence
 | |
| 
 | |
|   $Author$
 | |
|   created at: 04/01/01 03:42:15 JST
 | |
| 
 | |
|   Copyright (C) 2004-2007 Koichi Sasada
 | |
| 
 | |
| **********************************************************************/
 | |
| 
 | |
| #include "ruby/ruby.h"
 | |
| #include "internal.h"
 | |
| #include <math.h>
 | |
| 
 | |
| #define USE_INSN_STACK_INCREASE 1
 | |
| #include "vm_core.h"
 | |
| #include "iseq.h"
 | |
| #include "insns.inc"
 | |
| #include "insns_info.inc"
 | |
| 
 | |
| #define FIXNUM_INC(n, i) ((n)+(INT2FIX(i)&~FIXNUM_FLAG))
 | |
| #define FIXNUM_OR(n, i) ((n)|INT2FIX(i))
 | |
| 
 | |
| typedef struct iseq_link_element {
 | |
|     enum {
 | |
| 	ISEQ_ELEMENT_NONE,
 | |
| 	ISEQ_ELEMENT_LABEL,
 | |
| 	ISEQ_ELEMENT_INSN,
 | |
| 	ISEQ_ELEMENT_ADJUST
 | |
|     } type;
 | |
|     struct iseq_link_element *next;
 | |
|     struct iseq_link_element *prev;
 | |
| } LINK_ELEMENT;
 | |
| 
 | |
| typedef struct iseq_link_anchor {
 | |
|     LINK_ELEMENT anchor;
 | |
|     LINK_ELEMENT *last;
 | |
| } LINK_ANCHOR;
 | |
| 
 | |
| typedef struct iseq_label_data {
 | |
|     LINK_ELEMENT link;
 | |
|     int label_no;
 | |
|     int position;
 | |
|     int sc_state;
 | |
|     int set;
 | |
|     int sp;
 | |
| } LABEL;
 | |
| 
 | |
| typedef struct iseq_insn_data {
 | |
|     LINK_ELEMENT link;
 | |
|     enum ruby_vminsn_type insn_id;
 | |
|     unsigned int line_no;
 | |
|     int operand_size;
 | |
|     int sc_state;
 | |
|     VALUE *operands;
 | |
| } INSN;
 | |
| 
 | |
| typedef struct iseq_adjust_data {
 | |
|     LINK_ELEMENT link;
 | |
|     LABEL *label;
 | |
|     int line_no;
 | |
| } ADJUST;
 | |
| 
 | |
| struct ensure_range {
 | |
|     LABEL *begin;
 | |
|     LABEL *end;
 | |
|     struct ensure_range *next;
 | |
| };
 | |
| 
 | |
| struct iseq_compile_data_ensure_node_stack {
 | |
|     NODE *ensure_node;
 | |
|     struct iseq_compile_data_ensure_node_stack *prev;
 | |
|     struct ensure_range *erange;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * debug function(macro) interface depend on CPDEBUG
 | |
|  * if it is less than 0, runtime option is in effect.
 | |
|  *
 | |
|  * debug level:
 | |
|  *  0: no debug output
 | |
|  *  1: show node type
 | |
|  *  2: show node important parameters
 | |
|  *  ...
 | |
|  *  5: show other parameters
 | |
|  * 10: show every AST array
 | |
|  */
 | |
| 
 | |
| #ifndef CPDEBUG
 | |
| #define CPDEBUG 0
 | |
| #endif
 | |
| 
 | |
| #if CPDEBUG >= 0
 | |
| #define compile_debug CPDEBUG
 | |
| #else
 | |
| #define compile_debug iseq->compile_data->option->debug_level
 | |
| #endif
 | |
| 
 | |
| #if CPDEBUG
 | |
| 
 | |
| #define compile_debug_print_indent(level) \
 | |
|     ruby_debug_print_indent((level), compile_debug, gl_node_level * 2)
 | |
| 
 | |
| #define debugp(header, value) (void) \
 | |
|   (compile_debug_print_indent(1) && \
 | |
|    ruby_debug_print_value(1, compile_debug, (header), (value)))
 | |
| 
 | |
| #define debugi(header, id)  (void) \
 | |
|   (compile_debug_print_indent(1) && \
 | |
|    ruby_debug_print_id(1, compile_debug, (header), (id)))
 | |
| 
 | |
| #define debugp_param(header, value)  (void) \
 | |
|   (compile_debug_print_indent(1) && \
 | |
|    ruby_debug_print_value(1, compile_debug, (header), (value)))
 | |
| 
 | |
| #define debugp_verbose(header, value)  (void) \
 | |
|   (compile_debug_print_indent(2) && \
 | |
|    ruby_debug_print_value(2, compile_debug, (header), (value)))
 | |
| 
 | |
| #define debugp_verbose_node(header, value)  (void) \
 | |
|   (compile_debug_print_indent(10) && \
 | |
|    ruby_debug_print_value(10, compile_debug, (header), (value)))
 | |
| 
 | |
| #define debug_node_start(node)  ((void) \
 | |
|   (compile_debug_print_indent(1) && \
 | |
|    (ruby_debug_print_node(1, CPDEBUG, "", (NODE *)(node)), gl_node_level)), \
 | |
|    gl_node_level++)
 | |
| 
 | |
| #define debug_node_end()  gl_node_level --
 | |
| 
 | |
| #else
 | |
| 
 | |
| static inline ID
 | |
| r_id(ID id)
 | |
| {
 | |
|     return id;
 | |
| }
 | |
| 
 | |
| static inline VALUE
 | |
| r_value(VALUE value)
 | |
| {
 | |
|     return value;
 | |
| }
 | |
| 
 | |
| #define debugi(header, id)                 r_id(id)
 | |
| #define debugp(header, value)              r_value(value)
 | |
| #define debugp_verbose(header, value)      r_value(value)
 | |
| #define debugp_verbose_node(header, value) r_value(value)
 | |
| #define debugp_param(header, value)        r_value(value)
 | |
| #define debug_node_start(node)             ((void)0)
 | |
| #define debug_node_end()                   ((void)0)
 | |
| #endif
 | |
| 
 | |
| #if CPDEBUG > 1 || CPDEBUG < 0
 | |
| #define debugs if (compile_debug_print_indent(1)) ruby_debug_printf
 | |
| #define debug_compile(msg, v) ((void)(compile_debug_print_indent(1) && fputs((msg), stderr)), (v))
 | |
| #else
 | |
| #define debugs                             if(0)printf
 | |
| #define debug_compile(msg, v) (v)
 | |
| #endif
 | |
| 
 | |
| 
 | |
| /* create new label */
 | |
| #define NEW_LABEL(l) new_label_body(iseq, (l))
 | |
| 
 | |
| #define iseq_path(iseq) \
 | |
|   (((rb_iseq_t*)DATA_PTR(iseq))->location.path)
 | |
| 
 | |
| #define iseq_absolute_path(iseq) \
 | |
|   (((rb_iseq_t*)DATA_PTR(iseq))->location.absolute_path)
 | |
| 
 | |
| #define NEW_ISEQVAL(node, name, type, line_no)       \
 | |
|   new_child_iseq(iseq, (node), (name), 0, (type), (line_no))
 | |
| 
 | |
| #define NEW_CHILD_ISEQVAL(node, name, type, line_no)       \
 | |
|   new_child_iseq(iseq, (node), (name), iseq->self, (type), (line_no))
 | |
| 
 | |
| /* add instructions */
 | |
| #define ADD_SEQ(seq1, seq2) \
 | |
|   APPEND_LIST((seq1), (seq2))
 | |
| 
 | |
| /* add an instruction */
 | |
| #define ADD_INSN(seq, line, insn) \
 | |
|   ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_body(iseq, (line), BIN(insn), 0))
 | |
| 
 | |
| /* add an instruction with some operands (1, 2, 3, 5) */
 | |
| #define ADD_INSN1(seq, line, insn, op1) \
 | |
|   ADD_ELEM((seq), (LINK_ELEMENT *) \
 | |
|            new_insn_body(iseq, (line), BIN(insn), 1, (VALUE)(op1)))
 | |
| 
 | |
| /* add an instruction with label operand (alias of ADD_INSN1) */
 | |
| #define ADD_INSNL(seq, line, insn, label) ADD_INSN1(seq, line, insn, label)
 | |
| 
 | |
| #define ADD_INSN2(seq, line, insn, op1, op2) \
 | |
|   ADD_ELEM((seq), (LINK_ELEMENT *) \
 | |
|            new_insn_body(iseq, (line), BIN(insn), 2, (VALUE)(op1), (VALUE)(op2)))
 | |
| 
 | |
| #define ADD_INSN3(seq, line, insn, op1, op2, op3) \
 | |
|   ADD_ELEM((seq), (LINK_ELEMENT *) \
 | |
|            new_insn_body(iseq, (line), BIN(insn), 3, (VALUE)(op1), (VALUE)(op2), (VALUE)(op3)))
 | |
| 
 | |
| /* Specific Insn factory */
 | |
| #define ADD_SEND(seq, line, id, argc) \
 | |
|   ADD_SEND_R((seq), (line), (id), (argc), (VALUE)Qfalse, (VALUE)INT2FIX(0))
 | |
| 
 | |
| #define ADD_CALL_RECEIVER(seq, line) \
 | |
|   ADD_INSN((seq), (line), putself)
 | |
| 
 | |
| #define ADD_CALL(seq, line, id, argc) \
 | |
|   ADD_SEND_R((seq), (line), (id), (argc), (VALUE)Qfalse, (VALUE)INT2FIX(VM_CALL_FCALL))
 | |
| 
 | |
| #define ADD_CALL_WITH_BLOCK(seq, line, id, argc, block) \
 | |
|   ADD_SEND_R((seq), (line), (id), (argc), (block), (VALUE)INT2FIX(VM_CALL_FCALL))
 | |
| 
 | |
| #define ADD_SEND_R(seq, line, id, argc, block, flag) \
 | |
|   ADD_ELEM((seq), (LINK_ELEMENT *) \
 | |
|            new_insn_send(iseq, (line), \
 | |
|                          (VALUE)(id), (VALUE)(argc), (VALUE)(block), (VALUE)(flag)))
 | |
| 
 | |
| #define ADD_TRACE(seq, line, event) \
 | |
|   do { \
 | |
|       if ((event) == RUBY_EVENT_LINE && iseq->coverage && \
 | |
| 	  (line) != iseq->compile_data->last_coverable_line) { \
 | |
| 	  RARRAY_ASET(iseq->coverage, (line) - 1, INT2FIX(0)); \
 | |
| 	  iseq->compile_data->last_coverable_line = (line); \
 | |
| 	  ADD_INSN1((seq), (line), trace, INT2FIX(RUBY_EVENT_COVERAGE)); \
 | |
|       } \
 | |
|       if (iseq->compile_data->option->trace_instruction) { \
 | |
| 	  ADD_INSN1((seq), (line), trace, INT2FIX(event)); \
 | |
|       } \
 | |
|   } while (0)
 | |
| 
 | |
| /* add label */
 | |
| #define ADD_LABEL(seq, label) \
 | |
|   ADD_ELEM((seq), (LINK_ELEMENT *) (label))
 | |
| 
 | |
| #define APPEND_LABEL(seq, before, label) \
 | |
|   APPEND_ELEM((seq), (before), (LINK_ELEMENT *) (label))
 | |
| 
 | |
| #define ADD_ADJUST(seq, line, label) \
 | |
|   ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), (line)))
 | |
| 
 | |
| #define ADD_ADJUST_RESTORE(seq, label) \
 | |
|   ADD_ELEM((seq), (LINK_ELEMENT *) new_adjust_body(iseq, (label), -1))
 | |
| 
 | |
| #define ADD_CATCH_ENTRY(type, ls, le, iseqv, lc)		\
 | |
|     (rb_ary_push(iseq->compile_data->catch_table_ary,		\
 | |
| 		 rb_ary_new3(5, (type),				\
 | |
| 			     (VALUE)(ls) | 1, (VALUE)(le) | 1,	\
 | |
| 			     (VALUE)(iseqv), (VALUE)(lc) | 1)))
 | |
| 
 | |
| /* compile node */
 | |
| #define COMPILE(anchor, desc, node) \
 | |
|   (debug_compile("== " desc "\n", \
 | |
|                  iseq_compile_each(iseq, (anchor), (node), 0)))
 | |
| 
 | |
| /* compile node, this node's value will be popped */
 | |
| #define COMPILE_POPED(anchor, desc, node)    \
 | |
|   (debug_compile("== " desc "\n", \
 | |
|                  iseq_compile_each(iseq, (anchor), (node), 1)))
 | |
| 
 | |
| /* compile node, which is popped when 'poped' is true */
 | |
| #define COMPILE_(anchor, desc, node, poped)  \
 | |
|   (debug_compile("== " desc "\n", \
 | |
|                  iseq_compile_each(iseq, (anchor), (node), (poped))))
 | |
| 
 | |
| #define OPERAND_AT(insn, idx) \
 | |
|   (((INSN*)(insn))->operands[(idx)])
 | |
| 
 | |
| #define INSN_OF(insn) \
 | |
|   (((INSN*)(insn))->insn_id)
 | |
| 
 | |
| /* error */
 | |
| #define COMPILE_ERROR(strs)                        \
 | |
| {                                                  \
 | |
|   VALUE tmp = GET_THREAD()->errinfo;               \
 | |
|   if (compile_debug) rb_compile_bug strs;          \
 | |
|   GET_THREAD()->errinfo = iseq->compile_data->err_info;  \
 | |
|   rb_compile_error strs;                           \
 | |
|   OBJ_WRITE(iseq->self, &iseq->compile_data->err_info, GET_THREAD()->errinfo); \
 | |
|   GET_THREAD()->errinfo = tmp;                     \
 | |
|   ret = 0;                                         \
 | |
|   break;                                           \
 | |
| }
 | |
| 
 | |
| #define ERROR_ARGS ruby_sourcefile, nd_line(node),
 | |
| 
 | |
| 
 | |
| #define COMPILE_OK 1
 | |
| #define COMPILE_NG 0
 | |
| 
 | |
| 
 | |
| /* leave name uninitialized so that compiler warn if INIT_ANCHOR is
 | |
|  * missing */
 | |
| #define DECL_ANCHOR(name) \
 | |
|   LINK_ANCHOR *name, name##_body__ = {{0,},}
 | |
| #define INIT_ANCHOR(name) \
 | |
|   (name##_body__.last = &name##_body__.anchor, name = &name##_body__)
 | |
| 
 | |
| #define hide_obj(obj) do {OBJ_FREEZE(obj); RBASIC_CLEAR_CLASS(obj);} while (0)
 | |
| 
 | |
| #include "optinsn.inc"
 | |
| #if OPT_INSTRUCTIONS_UNIFICATION
 | |
| #include "optunifs.inc"
 | |
| #endif
 | |
| 
 | |
| /* for debug */
 | |
| #if CPDEBUG < 0
 | |
| #define ISEQ_ARG iseq,
 | |
| #define ISEQ_ARG_DECLARE rb_iseq_t *iseq,
 | |
| #else
 | |
| #define ISEQ_ARG
 | |
| #define ISEQ_ARG_DECLARE
 | |
| #endif
 | |
| 
 | |
| #if CPDEBUG
 | |
| #define gl_node_level iseq->compile_data->node_level
 | |
| #if 0
 | |
| static void debug_list(ISEQ_ARG_DECLARE LINK_ANCHOR *anchor);
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| static void dump_disasm_list(LINK_ELEMENT *elem);
 | |
| 
 | |
| static int insn_data_length(INSN *iobj);
 | |
| static int insn_data_line_no(INSN *iobj);
 | |
| static int calc_sp_depth(int depth, INSN *iobj);
 | |
| 
 | |
| static INSN *new_insn_body(rb_iseq_t *iseq, int line_no, int insn_id, int argc, ...);
 | |
| static LABEL *new_label_body(rb_iseq_t *iseq, long line);
 | |
| static ADJUST *new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line);
 | |
| 
 | |
| static int iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *anchor, NODE * n, int);
 | |
| static int iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
 | |
| static int iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
 | |
| static int iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
 | |
| 
 | |
| static int iseq_set_local_table(rb_iseq_t *iseq, ID *tbl);
 | |
| static int iseq_set_exception_local_table(rb_iseq_t *iseq);
 | |
| static int iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *anchor, NODE * node);
 | |
| 
 | |
| static int iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
 | |
| static int iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor);
 | |
| static int iseq_set_exception_table(rb_iseq_t *iseq);
 | |
| static int iseq_set_optargs_table(rb_iseq_t *iseq);
 | |
| 
 | |
| /*
 | |
|  * To make Array to LinkedList, use link_anchor
 | |
|  */
 | |
| 
 | |
| static void
 | |
| verify_list(ISEQ_ARG_DECLARE const char *info, LINK_ANCHOR *anchor)
 | |
| {
 | |
| #if CPDEBUG
 | |
|     int flag = 0;
 | |
|     LINK_ELEMENT *list, *plist;
 | |
| 
 | |
|     if (!compile_debug) return;
 | |
| 
 | |
|     list = anchor->anchor.next;
 | |
|     plist = &anchor->anchor;
 | |
|     while (list) {
 | |
| 	if (plist != list->prev) {
 | |
| 	    flag += 1;
 | |
| 	}
 | |
| 	plist = list;
 | |
| 	list = list->next;
 | |
|     }
 | |
| 
 | |
|     if (anchor->last != plist && anchor->last != 0) {
 | |
| 	flag |= 0x70000;
 | |
|     }
 | |
| 
 | |
|     if (flag != 0) {
 | |
| 	rb_bug("list verify error: %08x (%s)", flag, info);
 | |
|     }
 | |
| #endif
 | |
| }
 | |
| #if CPDEBUG < 0
 | |
| #define verify_list(info, anchor) verify_list(iseq, (info), (anchor))
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * elem1, elem2 => elem1, elem2, elem
 | |
|  */
 | |
| static void
 | |
| ADD_ELEM(ISEQ_ARG_DECLARE LINK_ANCHOR *anchor, LINK_ELEMENT *elem)
 | |
| {
 | |
|     elem->prev = anchor->last;
 | |
|     anchor->last->next = elem;
 | |
|     anchor->last = elem;
 | |
|     verify_list("add", anchor);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * elem1, before, elem2 => elem1, before, elem, elem2
 | |
|  */
 | |
| static void
 | |
| APPEND_ELEM(ISEQ_ARG_DECLARE LINK_ANCHOR *anchor, LINK_ELEMENT *before, LINK_ELEMENT *elem)
 | |
| {
 | |
|     elem->prev = before;
 | |
|     elem->next = before->next;
 | |
|     elem->next->prev = elem;
 | |
|     before->next = elem;
 | |
|     if (before == anchor->last) anchor->last = elem;
 | |
|     verify_list("add", anchor);
 | |
| }
 | |
| #if CPDEBUG < 0
 | |
| #define ADD_ELEM(anchor, elem) ADD_ELEM(iseq, (anchor), (elem))
 | |
| #define APPEND_ELEM(anchor, before, elem) ADD_ELEM(iseq, (anchor), (before), (elem))
 | |
| #endif
 | |
| 
 | |
| static int
 | |
| iseq_add_mark_object(rb_iseq_t *iseq, VALUE v)
 | |
| {
 | |
|     if (!SPECIAL_CONST_P(v)) {
 | |
| 	rb_iseq_add_mark_object(iseq, v);
 | |
|     }
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| #define ruby_sourcefile		RSTRING_PTR(iseq->location.path)
 | |
| 
 | |
| static int
 | |
| iseq_add_mark_object_compile_time(rb_iseq_t *iseq, VALUE v)
 | |
| {
 | |
|     if (!SPECIAL_CONST_P(v)) {
 | |
| 	rb_ary_push(iseq->compile_data->mark_ary, v);
 | |
|     }
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| static int
 | |
| validate_label(st_data_t name, st_data_t label, st_data_t arg)
 | |
| {
 | |
|     rb_iseq_t *iseq = (rb_iseq_t *)arg;
 | |
|     LABEL *lobj = (LABEL *)label;
 | |
|     if (!lobj->link.next) {
 | |
| 	do {
 | |
| 	    int ret;
 | |
| 	    COMPILE_ERROR((ruby_sourcefile, lobj->position,
 | |
| 			   "%s: undefined label", rb_id2name((ID)name)));
 | |
| 	    if (ret) break;
 | |
| 	} while (0);
 | |
|     }
 | |
|     return ST_CONTINUE;
 | |
| }
 | |
| 
 | |
| static void
 | |
| validate_labels(rb_iseq_t *iseq, st_table *labels_table)
 | |
| {
 | |
|     st_foreach(labels_table, validate_label, (st_data_t)iseq);
 | |
|     if (!NIL_P(iseq->compile_data->err_info)) {
 | |
| 	rb_exc_raise(iseq->compile_data->err_info);
 | |
|     }
 | |
| }
 | |
| 
 | |
| VALUE
 | |
| rb_iseq_compile_node(VALUE self, NODE *node)
 | |
| {
 | |
|     DECL_ANCHOR(ret);
 | |
|     rb_iseq_t *iseq;
 | |
|     INIT_ANCHOR(ret);
 | |
|     GetISeqPtr(self, iseq);
 | |
| 
 | |
|     if (node == 0) {
 | |
| 	COMPILE(ret, "nil", node);
 | |
| 	iseq_set_local_table(iseq, 0);
 | |
|     }
 | |
|     else if (nd_type(node) == NODE_SCOPE) {
 | |
| 	/* iseq type of top, method, class, block */
 | |
| 	iseq_set_local_table(iseq, node->nd_tbl);
 | |
| 	iseq_set_arguments(iseq, ret, node->nd_args);
 | |
| 
 | |
| 	switch (iseq->type) {
 | |
| 	  case ISEQ_TYPE_BLOCK:
 | |
| 	    {
 | |
| 		LABEL *start = iseq->compile_data->start_label = NEW_LABEL(0);
 | |
| 		LABEL *end = iseq->compile_data->end_label = NEW_LABEL(0);
 | |
| 
 | |
| 		ADD_LABEL(ret, start);
 | |
| 		ADD_TRACE(ret, FIX2INT(iseq->location.first_lineno), RUBY_EVENT_B_CALL);
 | |
| 		COMPILE(ret, "block body", node->nd_body);
 | |
| 		ADD_LABEL(ret, end);
 | |
| 		ADD_TRACE(ret, nd_line(node), RUBY_EVENT_B_RETURN);
 | |
| 
 | |
| 		/* wide range catch handler must put at last */
 | |
| 		ADD_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, 0, start);
 | |
| 		ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, 0, end);
 | |
| 		break;
 | |
| 	    }
 | |
| 	  case ISEQ_TYPE_CLASS:
 | |
| 	    {
 | |
| 		ADD_TRACE(ret, FIX2INT(iseq->location.first_lineno), RUBY_EVENT_CLASS);
 | |
| 		COMPILE(ret, "scoped node", node->nd_body);
 | |
| 		ADD_TRACE(ret, nd_line(node), RUBY_EVENT_END);
 | |
| 		break;
 | |
| 	    }
 | |
| 	  case ISEQ_TYPE_METHOD:
 | |
| 	    {
 | |
| 		ADD_TRACE(ret, FIX2INT(iseq->location.first_lineno), RUBY_EVENT_CALL);
 | |
| 		COMPILE(ret, "scoped node", node->nd_body);
 | |
| 		ADD_TRACE(ret, nd_line(node), RUBY_EVENT_RETURN);
 | |
| 		break;
 | |
| 	    }
 | |
| 	  default: {
 | |
| 	    COMPILE(ret, "scoped node", node->nd_body);
 | |
| 	    break;
 | |
| 	  }
 | |
| 	}
 | |
|     }
 | |
|     else if (nd_type(node) == NODE_IFUNC) {
 | |
| 	/* user callback */
 | |
| 	(*node->nd_cfnc)(iseq, ret, node->nd_tval);
 | |
|     }
 | |
|     else {
 | |
| 	switch (iseq->type) {
 | |
| 	  case ISEQ_TYPE_METHOD:
 | |
| 	  case ISEQ_TYPE_CLASS:
 | |
| 	  case ISEQ_TYPE_BLOCK:
 | |
| 	  case ISEQ_TYPE_EVAL:
 | |
| 	  case ISEQ_TYPE_MAIN:
 | |
| 	  case ISEQ_TYPE_TOP:
 | |
| 	    rb_compile_error(ERROR_ARGS "compile/should not be reached: %s:%d",
 | |
| 			     __FILE__, __LINE__);
 | |
| 	    break;
 | |
| 	  case ISEQ_TYPE_RESCUE:
 | |
| 	    iseq_set_exception_local_table(iseq);
 | |
| 	    COMPILE(ret, "rescue", node);
 | |
| 	    break;
 | |
| 	  case ISEQ_TYPE_ENSURE:
 | |
| 	    iseq_set_exception_local_table(iseq);
 | |
| 	    COMPILE_POPED(ret, "ensure", node);
 | |
| 	    break;
 | |
| 	  case ISEQ_TYPE_DEFINED_GUARD:
 | |
| 	    iseq_set_local_table(iseq, 0);
 | |
| 	    COMPILE(ret, "defined guard", node);
 | |
| 	    break;
 | |
| 	  default:
 | |
| 	    rb_bug("unknown scope");
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     if (iseq->type == ISEQ_TYPE_RESCUE || iseq->type == ISEQ_TYPE_ENSURE) {
 | |
| 	ADD_INSN2(ret, 0, getlocal, INT2FIX(2), INT2FIX(0));
 | |
| 	ADD_INSN1(ret, 0, throw, INT2FIX(0) /* continue throw */ );
 | |
|     }
 | |
|     else {
 | |
| 	ADD_INSN(ret, iseq->compile_data->last_line, leave);
 | |
|     }
 | |
| 
 | |
| #if SUPPORT_JOKE
 | |
|     if (iseq->compile_data->labels_table) {
 | |
| 	validate_labels(iseq, iseq->compile_data->labels_table);
 | |
|     }
 | |
| #endif
 | |
|     return iseq_setup(iseq, ret);
 | |
| }
 | |
| 
 | |
| int
 | |
| rb_iseq_translate_threaded_code(rb_iseq_t *iseq)
 | |
| {
 | |
| #if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
 | |
|     const void * const *table = rb_vm_get_insns_address_table();
 | |
|     unsigned long i;
 | |
| 
 | |
|     iseq->iseq_encoded = ALLOC_N(VALUE, iseq->iseq_size);
 | |
|     MEMCPY(iseq->iseq_encoded, iseq->iseq, VALUE, iseq->iseq_size);
 | |
| 
 | |
|     for (i = 0; i < iseq->iseq_size; /* */ ) {
 | |
| 	int insn = (int)iseq->iseq_encoded[i];
 | |
| 	int len = insn_len(insn);
 | |
| 	iseq->iseq_encoded[i] = (VALUE)table[insn];
 | |
| 	i += len;
 | |
|     }
 | |
| #else
 | |
|     iseq->iseq_encoded = iseq->iseq;
 | |
| #endif
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| /*********************************************/
 | |
| /* definition of data structure for compiler */
 | |
| /*********************************************/
 | |
| 
 | |
| static void *
 | |
| compile_data_alloc(rb_iseq_t *iseq, size_t size)
 | |
| {
 | |
|     void *ptr = 0;
 | |
|     struct iseq_compile_data_storage *storage =
 | |
| 	iseq->compile_data->storage_current;
 | |
| 
 | |
|     if (storage->pos + size > storage->size) {
 | |
| 	unsigned long alloc_size = storage->size * 2;
 | |
| 
 | |
|       retry:
 | |
| 	if (alloc_size < size) {
 | |
| 	    alloc_size *= 2;
 | |
| 	    goto retry;
 | |
| 	}
 | |
| 	storage->next = (void *)ALLOC_N(char, alloc_size +
 | |
| 					sizeof(struct
 | |
| 					       iseq_compile_data_storage));
 | |
| 	storage = iseq->compile_data->storage_current = storage->next;
 | |
| 	storage->next = 0;
 | |
| 	storage->pos = 0;
 | |
| 	storage->size = alloc_size;
 | |
| 	storage->buff = (char *)(&storage->buff + 1);
 | |
|     }
 | |
| 
 | |
|     ptr = (void *)&storage->buff[storage->pos];
 | |
|     storage->pos += size;
 | |
|     return ptr;
 | |
| }
 | |
| 
 | |
| static INSN *
 | |
| compile_data_alloc_insn(rb_iseq_t *iseq)
 | |
| {
 | |
|     return (INSN *)compile_data_alloc(iseq, sizeof(INSN));
 | |
| }
 | |
| 
 | |
| static LABEL *
 | |
| compile_data_alloc_label(rb_iseq_t *iseq)
 | |
| {
 | |
|     return (LABEL *)compile_data_alloc(iseq, sizeof(LABEL));
 | |
| }
 | |
| 
 | |
| static ADJUST *
 | |
| compile_data_alloc_adjust(rb_iseq_t *iseq)
 | |
| {
 | |
|     return (ADJUST *)compile_data_alloc(iseq, sizeof(ADJUST));
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * elem1, elemX => elem1, elem2, elemX
 | |
|  */
 | |
| static void
 | |
| INSERT_ELEM_NEXT(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
 | |
| {
 | |
|     elem2->next = elem1->next;
 | |
|     elem2->prev = elem1;
 | |
|     elem1->next = elem2;
 | |
|     if (elem2->next) {
 | |
| 	elem2->next->prev = elem2;
 | |
|     }
 | |
| }
 | |
| 
 | |
| #if 0 /* unused */
 | |
| /*
 | |
|  * elemX, elem1 => elemX, elem2, elem1
 | |
|  */
 | |
| static void
 | |
| INSERT_ELEM_PREV(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
 | |
| {
 | |
|     elem2->prev = elem1->prev;
 | |
|     elem2->next = elem1;
 | |
|     elem1->prev = elem2;
 | |
|     if (elem2->prev) {
 | |
| 	elem2->prev->next = elem2;
 | |
|     }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * elemX, elem1, elemY => elemX, elem2, elemY
 | |
|  */
 | |
| static void
 | |
| REPLACE_ELEM(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
 | |
| {
 | |
|     elem2->prev = elem1->prev;
 | |
|     elem2->next = elem1->next;
 | |
|     if (elem1->prev) {
 | |
| 	elem1->prev->next = elem2;
 | |
|     }
 | |
|     if (elem1->next) {
 | |
| 	elem1->next->prev = elem2;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| REMOVE_ELEM(LINK_ELEMENT *elem)
 | |
| {
 | |
|     elem->prev->next = elem->next;
 | |
|     if (elem->next) {
 | |
| 	elem->next->prev = elem->prev;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static LINK_ELEMENT *
 | |
| FIRST_ELEMENT(LINK_ANCHOR *anchor)
 | |
| {
 | |
|     return anchor->anchor.next;
 | |
| }
 | |
| 
 | |
| #if 0 /* unused */
 | |
| static LINK_ELEMENT *
 | |
| LAST_ELEMENT(LINK_ANCHOR *anchor)
 | |
| {
 | |
|   return anchor->last;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static LINK_ELEMENT *
 | |
| POP_ELEMENT(ISEQ_ARG_DECLARE LINK_ANCHOR *anchor)
 | |
| {
 | |
|     LINK_ELEMENT *elem = anchor->last;
 | |
|     anchor->last = anchor->last->prev;
 | |
|     anchor->last->next = 0;
 | |
|     verify_list("pop", anchor);
 | |
|     return elem;
 | |
| }
 | |
| #if CPDEBUG < 0
 | |
| #define POP_ELEMENT(anchor) POP_ELEMENT(iseq, (anchor))
 | |
| #endif
 | |
| 
 | |
| #if 0 /* unused */
 | |
| static LINK_ELEMENT *
 | |
| SHIFT_ELEMENT(LINK_ANCHOR *anchor)
 | |
| {
 | |
|     LINK_ELEMENT *elem = anchor->anchor.next;
 | |
|     if (elem) {
 | |
| 	anchor->anchor.next = elem->next;
 | |
|     }
 | |
|     return elem;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #if 0 /* unused */
 | |
| static int
 | |
| LIST_SIZE(LINK_ANCHOR *anchor)
 | |
| {
 | |
|     LINK_ELEMENT *elem = anchor->anchor.next;
 | |
|     int size = 0;
 | |
|     while (elem) {
 | |
| 	size += 1;
 | |
| 	elem = elem->next;
 | |
|     }
 | |
|     return size;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static int
 | |
| LIST_SIZE_ZERO(LINK_ANCHOR *anchor)
 | |
| {
 | |
|     if (anchor->anchor.next == 0) {
 | |
| 	return 1;
 | |
|     }
 | |
|     else {
 | |
| 	return 0;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * anc1: e1, e2, e3
 | |
|  * anc2: e4, e5
 | |
|  *#=>
 | |
|  * anc1: e1, e2, e3, e4, e5
 | |
|  * anc2: e4, e5 (broken)
 | |
|  */
 | |
| static void
 | |
| APPEND_LIST(ISEQ_ARG_DECLARE LINK_ANCHOR *anc1, LINK_ANCHOR *anc2)
 | |
| {
 | |
|     if (anc2->anchor.next) {
 | |
| 	anc1->last->next = anc2->anchor.next;
 | |
| 	anc2->anchor.next->prev = anc1->last;
 | |
| 	anc1->last = anc2->last;
 | |
|     }
 | |
|     verify_list("append", anc1);
 | |
| }
 | |
| #if CPDEBUG < 0
 | |
| #define APPEND_LIST(anc1, anc2) APPEND_LIST(iseq, (anc1), (anc2))
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * anc1: e1, e2, e3
 | |
|  * anc2: e4, e5
 | |
|  *#=>
 | |
|  * anc1: e4, e5, e1, e2, e3
 | |
|  * anc2: e4, e5 (broken)
 | |
|  */
 | |
| static void
 | |
| INSERT_LIST(ISEQ_ARG_DECLARE LINK_ANCHOR *anc1, LINK_ANCHOR *anc2)
 | |
| {
 | |
|     if (anc2->anchor.next) {
 | |
| 	LINK_ELEMENT *first = anc1->anchor.next;
 | |
| 	anc1->anchor.next = anc2->anchor.next;
 | |
| 	anc1->anchor.next->prev = &anc1->anchor;
 | |
| 	anc2->last->next = first;
 | |
| 	if (first) {
 | |
| 	    first->prev = anc2->last;
 | |
| 	}
 | |
| 	else {
 | |
| 	    anc1->last = anc2->last;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     verify_list("append", anc1);
 | |
| }
 | |
| #if CPDEBUG < 0
 | |
| #define INSERT_LIST(anc1, anc2) INSERT_LIST(iseq, (anc1), (anc2))
 | |
| #endif
 | |
| 
 | |
| #if 0 /* unused */
 | |
| /*
 | |
|  * anc1: e1, e2, e3
 | |
|  * anc2: e4, e5
 | |
|  *#=>
 | |
|  * anc1: e4, e5
 | |
|  * anc2: e1, e2, e3
 | |
|  */
 | |
| static void
 | |
| SWAP_LIST(ISEQ_ARG_DECLARE LINK_ANCHOR *anc1, LINK_ANCHOR *anc2)
 | |
| {
 | |
|     LINK_ANCHOR tmp = *anc2;
 | |
| 
 | |
|     /* it has bug */
 | |
|     *anc2 = *anc1;
 | |
|     *anc1 = tmp;
 | |
| 
 | |
|     verify_list("swap1", anc1);
 | |
|     verify_list("swap2", anc2);
 | |
| }
 | |
| #if CPDEBUG < 0
 | |
| #define SWAP_LIST(anc1, anc2) SWAP_LIST(iseq, (anc1), (anc2))
 | |
| #endif
 | |
| 
 | |
| static LINK_ANCHOR *
 | |
| REVERSE_LIST(ISEQ_ARG_DECLARE LINK_ANCHOR *anc)
 | |
| {
 | |
|     LINK_ELEMENT *first, *last, *elem, *e;
 | |
|     first = &anc->anchor;
 | |
|     elem = first->next;
 | |
|     last = anc->last;
 | |
| 
 | |
|     if (elem != 0) {
 | |
| 	anc->anchor.next = last;
 | |
| 	anc->last = elem;
 | |
|     }
 | |
|     else {
 | |
| 	/* null list */
 | |
| 	return anc;
 | |
|     }
 | |
|     while (elem) {
 | |
| 	e = elem->next;
 | |
| 	elem->next = elem->prev;
 | |
| 	elem->prev = e;
 | |
| 	elem = e;
 | |
|     }
 | |
| 
 | |
|     first->next = last;
 | |
|     last->prev = first;
 | |
|     anc->last->next = 0;
 | |
| 
 | |
|     verify_list("reverse", anc);
 | |
|     return anc;
 | |
| }
 | |
| #if CPDEBUG < 0
 | |
| #define REVERSE_LIST(anc) REVERSE_LIST(iseq, (anc))
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| #if CPDEBUG && 0
 | |
| static void
 | |
| debug_list(ISEQ_ARG_DECLARE LINK_ANCHOR *anchor)
 | |
| {
 | |
|     LINK_ELEMENT *list = FIRST_ELEMENT(anchor);
 | |
|     printf("----\n");
 | |
|     printf("anch: %p, frst: %p, last: %p\n", &anchor->anchor,
 | |
| 	   anchor->anchor.next, anchor->last);
 | |
|     while (list) {
 | |
| 	printf("curr: %p, next: %p, prev: %p, type: %d\n", list, list->next,
 | |
| 	       list->prev, FIX2INT(list->type));
 | |
| 	list = list->next;
 | |
|     }
 | |
|     printf("----\n");
 | |
| 
 | |
|     dump_disasm_list(anchor->anchor.next);
 | |
|     verify_list("debug list", anchor);
 | |
| }
 | |
| #if CPDEBUG < 0
 | |
| #define debug_list(anc) debug_list(iseq, (anc))
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| static LABEL *
 | |
| new_label_body(rb_iseq_t *iseq, long line)
 | |
| {
 | |
|     LABEL *labelobj = compile_data_alloc_label(iseq);
 | |
| 
 | |
|     labelobj->link.type = ISEQ_ELEMENT_LABEL;
 | |
|     labelobj->link.next = 0;
 | |
| 
 | |
|     labelobj->label_no = iseq->compile_data->label_no++;
 | |
|     labelobj->sc_state = 0;
 | |
|     labelobj->sp = -1;
 | |
|     return labelobj;
 | |
| }
 | |
| 
 | |
| static ADJUST *
 | |
| new_adjust_body(rb_iseq_t *iseq, LABEL *label, int line)
 | |
| {
 | |
|     ADJUST *adjust = compile_data_alloc_adjust(iseq);
 | |
|     adjust->link.type = ISEQ_ELEMENT_ADJUST;
 | |
|     adjust->link.next = 0;
 | |
|     adjust->label = label;
 | |
|     adjust->line_no = line;
 | |
|     return adjust;
 | |
| }
 | |
| 
 | |
| static INSN *
 | |
| new_insn_core(rb_iseq_t *iseq, int line_no,
 | |
| 	      int insn_id, int argc, VALUE *argv)
 | |
| {
 | |
|     INSN *iobj = compile_data_alloc_insn(iseq);
 | |
|     /* printf("insn_id: %d, line: %d\n", insn_id, line_no); */
 | |
| 
 | |
|     iobj->link.type = ISEQ_ELEMENT_INSN;
 | |
|     iobj->link.next = 0;
 | |
|     iobj->insn_id = insn_id;
 | |
|     iobj->line_no = line_no;
 | |
|     iobj->operands = argv;
 | |
|     iobj->operand_size = argc;
 | |
|     iobj->sc_state = 0;
 | |
|     return iobj;
 | |
| }
 | |
| 
 | |
| static INSN *
 | |
| new_insn_body(rb_iseq_t *iseq, int line_no, int insn_id, int argc, ...)
 | |
| {
 | |
|     VALUE *operands = 0;
 | |
|     va_list argv;
 | |
|     if (argc > 0) {
 | |
| 	int i;
 | |
| 	va_init_list(argv, argc);
 | |
| 	operands = (VALUE *)compile_data_alloc(iseq, sizeof(VALUE) * argc);
 | |
| 	for (i = 0; i < argc; i++) {
 | |
| 	    VALUE v = va_arg(argv, VALUE);
 | |
| 	    operands[i] = v;
 | |
| 	}
 | |
| 	va_end(argv);
 | |
|     }
 | |
|     return new_insn_core(iseq, line_no, insn_id, argc, operands);
 | |
| }
 | |
| 
 | |
| static rb_call_info_t *
 | |
| new_callinfo(rb_iseq_t *iseq, ID mid, int argc, VALUE block, unsigned long flag)
 | |
| {
 | |
|     rb_call_info_t *ci = (rb_call_info_t *)compile_data_alloc(iseq, sizeof(rb_call_info_t));
 | |
|     ci->mid = mid;
 | |
|     ci->flag = flag;
 | |
|     ci->orig_argc = argc;
 | |
|     ci->argc = argc;
 | |
| 
 | |
|     if (block) {
 | |
| 	GetISeqPtr(block, ci->blockiseq);
 | |
|     }
 | |
|     else {
 | |
| 	ci->blockiseq = 0;
 | |
| 	if (!(ci->flag & (VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG))) {
 | |
| 	    ci->flag |= VM_CALL_ARGS_SKIP_SETUP;
 | |
| 	}
 | |
|     }
 | |
|     ci->vmstat = 0;
 | |
|     ci->seq = 0;
 | |
|     ci->blockptr = 0;
 | |
|     ci->recv = Qundef;
 | |
|     ci->call = 0; /* TODO: should set default function? */
 | |
| 
 | |
|     ci->aux.index = iseq->callinfo_size++;
 | |
| 
 | |
|     return ci;
 | |
| }
 | |
| 
 | |
| static INSN *
 | |
| new_insn_send(rb_iseq_t *iseq, int line_no, VALUE id, VALUE argc, VALUE block, VALUE flag)
 | |
| {
 | |
|     VALUE *operands = (VALUE *)compile_data_alloc(iseq, sizeof(VALUE) * 1);
 | |
|     operands[0] = (VALUE)new_callinfo(iseq, SYM2ID(id), FIX2INT(argc), block, FIX2INT(flag));
 | |
|     return new_insn_core(iseq, line_no, BIN(send), 1, operands);
 | |
| }
 | |
| 
 | |
| static VALUE
 | |
| new_child_iseq(rb_iseq_t *iseq, NODE *node,
 | |
| 	       VALUE name, VALUE parent, enum iseq_type type, int line_no)
 | |
| {
 | |
|     VALUE ret;
 | |
| 
 | |
|     debugs("[new_child_iseq]> ---------------------------------------\n");
 | |
|     ret = rb_iseq_new_with_opt(node, name,
 | |
| 			       iseq_path(iseq->self), iseq_absolute_path(iseq->self),
 | |
| 			       INT2FIX(line_no), parent, type, iseq->compile_data->option);
 | |
|     debugs("[new_child_iseq]< ---------------------------------------\n");
 | |
|     iseq_add_mark_object(iseq, ret);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
 | |
| {
 | |
|     /* debugs("[compile step 2] (iseq_array_to_linkedlist)\n"); */
 | |
| 
 | |
|     if (compile_debug > 5)
 | |
| 	dump_disasm_list(FIRST_ELEMENT(anchor));
 | |
| 
 | |
|     debugs("[compile step 3.1 (iseq_optimize)]\n");
 | |
|     iseq_optimize(iseq, anchor);
 | |
| 
 | |
|     if (compile_debug > 5)
 | |
| 	dump_disasm_list(FIRST_ELEMENT(anchor));
 | |
| 
 | |
|     if (iseq->compile_data->option->instructions_unification) {
 | |
| 	debugs("[compile step 3.2 (iseq_insns_unification)]\n");
 | |
| 	iseq_insns_unification(iseq, anchor);
 | |
| 	if (compile_debug > 5)
 | |
| 	    dump_disasm_list(FIRST_ELEMENT(anchor));
 | |
|     }
 | |
| 
 | |
|     if (iseq->compile_data->option->stack_caching) {
 | |
| 	debugs("[compile step 3.3 (iseq_set_sequence_stackcaching)]\n");
 | |
| 	iseq_set_sequence_stackcaching(iseq, anchor);
 | |
| 	if (compile_debug > 5)
 | |
| 	    dump_disasm_list(FIRST_ELEMENT(anchor));
 | |
|     }
 | |
| 
 | |
|     debugs("[compile step 4.1 (iseq_set_sequence)]\n");
 | |
|     iseq_set_sequence(iseq, anchor);
 | |
|     if (compile_debug > 5)
 | |
| 	dump_disasm_list(FIRST_ELEMENT(anchor));
 | |
| 
 | |
|     debugs("[compile step 4.2 (iseq_set_exception_table)]\n");
 | |
|     iseq_set_exception_table(iseq);
 | |
| 
 | |
|     debugs("[compile step 4.3 (set_optargs_table)] \n");
 | |
|     iseq_set_optargs_table(iseq);
 | |
| 
 | |
|     debugs("[compile step 5 (iseq_translate_threaded_code)] \n");
 | |
|     rb_iseq_translate_threaded_code(iseq);
 | |
| 
 | |
|     if (compile_debug > 1) {
 | |
| 	VALUE str = rb_iseq_disasm(iseq->self);
 | |
| 	printf("%s\n", StringValueCStr(str));
 | |
| 	fflush(stdout);
 | |
|     }
 | |
|     debugs("[compile step: finish]\n");
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| iseq_set_exception_local_table(rb_iseq_t *iseq)
 | |
| {
 | |
|     ID id_dollar_bang;
 | |
| 
 | |
|     CONST_ID(id_dollar_bang, "#$!");
 | |
|     iseq->local_table = (ID *)ALLOC_N(ID, 1);
 | |
|     iseq->local_table_size = 1;
 | |
|     iseq->local_size = iseq->local_table_size + 1;
 | |
|     iseq->local_table[0] = id_dollar_bang;
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| static int
 | |
| get_lvar_level(rb_iseq_t *iseq)
 | |
| {
 | |
|     int lev = 0;
 | |
|     while (iseq != iseq->local_iseq) {
 | |
| 	lev++;
 | |
| 	iseq = iseq->parent_iseq;
 | |
|     }
 | |
|     return lev;
 | |
| }
 | |
| 
 | |
| static int
 | |
| get_dyna_var_idx_at_raw(rb_iseq_t *iseq, ID id)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     for (i = 0; i < iseq->local_table_size; i++) {
 | |
| 	if (iseq->local_table[i] == id) {
 | |
| 	    return i;
 | |
| 	}
 | |
|     }
 | |
|     return -1;
 | |
| }
 | |
| 
 | |
| static int
 | |
| get_local_var_idx(rb_iseq_t *iseq, ID id)
 | |
| {
 | |
|     int idx = get_dyna_var_idx_at_raw(iseq->local_iseq, id);
 | |
| 
 | |
|     if (idx < 0) {
 | |
| 	rb_bug("get_local_var_idx: %d", idx);
 | |
|     }
 | |
| 
 | |
|     return idx;
 | |
| }
 | |
| 
 | |
| static int
 | |
| get_dyna_var_idx(rb_iseq_t *iseq, ID id, int *level, int *ls)
 | |
| {
 | |
|     int lv = 0, idx = -1;
 | |
| 
 | |
|     while (iseq) {
 | |
| 	idx = get_dyna_var_idx_at_raw(iseq, id);
 | |
| 	if (idx >= 0) {
 | |
| 	    break;
 | |
| 	}
 | |
| 	iseq = iseq->parent_iseq;
 | |
| 	lv++;
 | |
|     }
 | |
| 
 | |
|     if (idx < 0) {
 | |
| 	rb_bug("get_dyna_var_idx: -1");
 | |
|     }
 | |
| 
 | |
|     *level = lv;
 | |
|     *ls = iseq->local_size;
 | |
|     return idx;
 | |
| }
 | |
| 
 | |
| static int
 | |
| iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *optargs, NODE *node_args)
 | |
| {
 | |
|     debugs("iseq_set_arguments: %s\n", node_args ? "" : "0");
 | |
| 
 | |
|     if (node_args) {
 | |
| 	struct rb_args_info *args = node_args->nd_ainfo;
 | |
| 	ID rest_id = 0;
 | |
| 	int last_comma = 0;
 | |
| 	ID block_id = 0;
 | |
| 
 | |
| 	if (nd_type(node_args) != NODE_ARGS) {
 | |
| 	    rb_bug("iseq_set_arguments: NODE_ARGS is expected, but %s",
 | |
| 		   ruby_node_name(nd_type(node_args)));
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	iseq->argc = (int)args->pre_args_num;
 | |
| 	debugs("  - argc: %d\n", iseq->argc);
 | |
| 
 | |
| 	rest_id = args->rest_arg;
 | |
| 	if (rest_id == 1) {
 | |
| 	    last_comma = 1;
 | |
| 	    rest_id = 0;
 | |
| 	}
 | |
| 	block_id = args->block_arg;
 | |
| 
 | |
| 	if (args->first_post_arg) {
 | |
| 	    iseq->arg_post_start = get_dyna_var_idx_at_raw(iseq, args->first_post_arg);
 | |
| 	    iseq->arg_post_len = args->post_args_num;
 | |
| 	}
 | |
| 
 | |
| 	if (args->opt_args) {
 | |
| 	    NODE *node = args->opt_args;
 | |
| 	    LABEL *label;
 | |
| 	    VALUE labels = rb_ary_tmp_new(1);
 | |
| 	    int i = 0, j;
 | |
| 
 | |
| 	    while (node) {
 | |
| 		label = NEW_LABEL(nd_line(node));
 | |
| 		rb_ary_push(labels, (VALUE)label | 1);
 | |
| 		ADD_LABEL(optargs, label);
 | |
| 		COMPILE_POPED(optargs, "optarg", node->nd_body);
 | |
| 		node = node->nd_next;
 | |
| 		i += 1;
 | |
| 	    }
 | |
| 
 | |
| 	    /* last label */
 | |
| 	    label = NEW_LABEL(nd_line(node_args));
 | |
| 	    rb_ary_push(labels, (VALUE)label | 1);
 | |
| 	    ADD_LABEL(optargs, label);
 | |
| 	    i += 1;
 | |
| 
 | |
| 	    iseq->arg_opts = i;
 | |
| 	    iseq->arg_opt_table = ALLOC_N(VALUE, i);
 | |
| 	    MEMCPY(iseq->arg_opt_table, RARRAY_RAWPTR(labels), VALUE, i);
 | |
| 	    for (j = 0; j < i; j++) {
 | |
| 		iseq->arg_opt_table[j] &= ~1;
 | |
| 	    }
 | |
| 	    rb_ary_clear(labels);
 | |
| 	}
 | |
| 	else {
 | |
| 	    iseq->arg_opts = 0;
 | |
| 	}
 | |
| 
 | |
| 	if (args->kw_args) {
 | |
| 	    NODE *node = args->kw_args;
 | |
| 	    VALUE keywords = rb_ary_tmp_new(1);
 | |
| 	    VALUE required = 0;
 | |
| 	    int i = 0, j, r = 0;
 | |
| 
 | |
| 	    iseq->arg_keyword = get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_vid);
 | |
| 	    COMPILE(optargs, "kwarg", args->kw_rest_arg);
 | |
| 	    while (node) {
 | |
| 		VALUE list = keywords;
 | |
| 		if (node->nd_body->nd_value == (NODE *)-1) {
 | |
| 		    ++r;
 | |
| 		    if (!required) required = rb_ary_tmp_new(1);
 | |
| 		    list = required;
 | |
| 		}
 | |
| 		rb_ary_push(list, INT2FIX(node->nd_body->nd_vid));
 | |
| 		COMPILE_POPED(optargs, "kwarg", node); /* nd_type(node) == NODE_KW_ARG */
 | |
| 		node = node->nd_next;
 | |
| 		i += 1;
 | |
| 	    }
 | |
| 	    iseq->arg_keyword_check = (args->kw_rest_arg->nd_vid & ID_SCOPE_MASK) == ID_JUNK;
 | |
| 	    iseq->arg_keywords = i;
 | |
| 	    iseq->arg_keyword_required = r;
 | |
| 	    iseq->arg_keyword_table = ALLOC_N(ID, i);
 | |
| 	    if (r) {
 | |
| 		rb_ary_concat(required, keywords);
 | |
| 		keywords = required;
 | |
| 	    }
 | |
| 	    for (j = 0; j < i; j++) {
 | |
| 		iseq->arg_keyword_table[j] = FIX2INT(RARRAY_AREF(keywords, j));
 | |
| 	    }
 | |
| 	    ADD_INSN(optargs, nd_line(args->kw_args), pop);
 | |
| 	}
 | |
| 	else if (args->kw_rest_arg) {
 | |
| 	    iseq->arg_keyword = get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_vid);
 | |
| 	    COMPILE(optargs, "kwarg", args->kw_rest_arg);
 | |
| 	    ADD_INSN(optargs, nd_line(args->kw_rest_arg), pop);
 | |
| 	}
 | |
| 	else {
 | |
| 	    iseq->arg_keyword = -1;
 | |
| 	}
 | |
| 
 | |
| 	if (args->pre_init) { /* m_init */
 | |
| 	    COMPILE_POPED(optargs, "init arguments (m)", args->pre_init);
 | |
| 	}
 | |
| 	if (args->post_init) { /* p_init */
 | |
| 	    COMPILE_POPED(optargs, "init arguments (p)", args->post_init);
 | |
| 	}
 | |
| 
 | |
| 	if (rest_id) {
 | |
| 	    iseq->arg_rest = get_dyna_var_idx_at_raw(iseq, rest_id);
 | |
| 
 | |
| 	    if (iseq->arg_rest == -1) {
 | |
| 		rb_bug("arg_rest: -1");
 | |
| 	    }
 | |
| 
 | |
| 	    if (iseq->arg_post_start == 0) {
 | |
| 		iseq->arg_post_start = iseq->arg_rest + 1;
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
| 	if (block_id) {
 | |
| 	    iseq->arg_block = get_dyna_var_idx_at_raw(iseq, block_id);
 | |
| 	}
 | |
| 
 | |
| 	if (iseq->arg_opts != 0 || iseq->arg_post_len != 0 ||
 | |
| 	    iseq->arg_rest != -1 || iseq->arg_block != -1 ||
 | |
| 	    iseq->arg_keyword != -1) {
 | |
| 	    iseq->arg_simple = 0;
 | |
| 
 | |
| 	    /* set arg_size: size of arguments */
 | |
| 	    if (iseq->arg_keyword != -1) {
 | |
| 		iseq->arg_size = iseq->arg_keyword + 1;
 | |
| 	    }
 | |
| 	    else if (iseq->arg_block != -1) {
 | |
| 		iseq->arg_size = iseq->arg_block + 1;
 | |
| 	    }
 | |
| 	    else if (iseq->arg_post_len) {
 | |
| 		iseq->arg_size = iseq->arg_post_start + iseq->arg_post_len;
 | |
| 	    }
 | |
| 	    else if (iseq->arg_rest != -1) {
 | |
| 		iseq->arg_size = iseq->arg_rest + 1;
 | |
| 	    }
 | |
| 	    else if (iseq->arg_opts) {
 | |
| 		iseq->arg_size = iseq->argc + iseq->arg_opts - 1;
 | |
| 	    }
 | |
| 	    else {
 | |
| 		iseq->arg_size = iseq->argc;
 | |
| 	    }
 | |
| 	}
 | |
| 	else {
 | |
| 	    iseq->arg_simple = 1;
 | |
| 	    iseq->arg_size = iseq->argc;
 | |
| 	}
 | |
| 
 | |
| 	if (iseq->type == ISEQ_TYPE_BLOCK) {
 | |
| 	    if (iseq->arg_opts == 0 && iseq->arg_post_len == 0 &&
 | |
| 		iseq->arg_rest == -1 && iseq->arg_keyword == -1) {
 | |
| 		if (iseq->argc == 1 && last_comma == 0) {
 | |
| 		    /* {|a|} */
 | |
| 		    iseq->arg_simple |= 0x02;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     else {
 | |
| 	iseq->arg_simple = 1;
 | |
|     }
 | |
| 
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| static int
 | |
| iseq_set_local_table(rb_iseq_t *iseq, ID *tbl)
 | |
| {
 | |
|     int size;
 | |
| 
 | |
|     if (tbl) {
 | |
| 	size = (int)*tbl;
 | |
| 	tbl++;
 | |
|     }
 | |
|     else {
 | |
| 	size = 0;
 | |
|     }
 | |
| 
 | |
|     if (size > 0) {
 | |
| 	iseq->local_table = (ID *)ALLOC_N(ID, size);
 | |
| 	MEMCPY(iseq->local_table, tbl, ID, size);
 | |
|     }
 | |
| 
 | |
|     iseq->local_size = iseq->local_table_size = size;
 | |
|     iseq->local_size += 1;
 | |
|     /*
 | |
|       if (lfp == dfp ) { // top, class, method
 | |
| 	  dfp[-1]: svar
 | |
|       else {             // block
 | |
|           dfp[-1]: cref
 | |
|       }
 | |
|      */
 | |
| 
 | |
|     debugs("iseq_set_local_table: %d, %d\n", iseq->local_size, iseq->local_table_size);
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| static int
 | |
| cdhash_cmp(VALUE val, VALUE lit)
 | |
| {
 | |
|     if (val == lit) return 0;
 | |
|     if (SPECIAL_CONST_P(lit)) {
 | |
| 	return val != lit;
 | |
|     }
 | |
|     if (SPECIAL_CONST_P(val) || BUILTIN_TYPE(val) != BUILTIN_TYPE(lit)) {
 | |
| 	return -1;
 | |
|     }
 | |
|     if (BUILTIN_TYPE(lit) == T_STRING) {
 | |
| 	return rb_str_hash_cmp(lit, val);
 | |
|     }
 | |
|     return !rb_eql(lit, val);
 | |
| }
 | |
| 
 | |
| static st_index_t
 | |
| cdhash_hash(VALUE a)
 | |
| {
 | |
|     if (SPECIAL_CONST_P(a)) return (st_index_t)a;
 | |
|     if (RB_TYPE_P(a, T_STRING)) return rb_str_hash(a);
 | |
|     {
 | |
| 	VALUE hval = rb_hash(a);
 | |
| 	return (st_index_t)FIX2LONG(hval);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static const struct st_hash_type cdhash_type = {
 | |
|     cdhash_cmp,
 | |
|     cdhash_hash,
 | |
| };
 | |
| 
 | |
| struct cdhash_set_label_struct {
 | |
|     VALUE hash;
 | |
|     int pos;
 | |
|     int len;
 | |
| };
 | |
| 
 | |
| static int
 | |
| cdhash_set_label_i(VALUE key, VALUE val, void *ptr)
 | |
| {
 | |
|     struct cdhash_set_label_struct *data = (struct cdhash_set_label_struct *)ptr;
 | |
|     LABEL *lobj = (LABEL *)(val & ~1);
 | |
|     rb_hash_aset(data->hash, key, INT2FIX(lobj->position - (data->pos+data->len)));
 | |
|     return ST_CONTINUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   ruby insn object list -> raw instruction sequence
 | |
|  */
 | |
| static int
 | |
| iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
 | |
| {
 | |
|     LABEL *lobj;
 | |
|     INSN *iobj;
 | |
|     struct iseq_line_info_entry *line_info_table;
 | |
|     unsigned int last_line = 0;
 | |
|     LINK_ELEMENT *list;
 | |
|     VALUE *generated_iseq;
 | |
| 
 | |
|     int k, pos, sp, stack_max = 0, line = 0;
 | |
| 
 | |
|     /* set label position */
 | |
|     list = FIRST_ELEMENT(anchor);
 | |
|     k = pos = 0;
 | |
|     while (list) {
 | |
| 	switch (list->type) {
 | |
| 	  case ISEQ_ELEMENT_INSN:
 | |
| 	    {
 | |
| 		iobj = (INSN *)list;
 | |
| 		line = iobj->line_no;
 | |
| 		pos += insn_data_length(iobj);
 | |
| 		k++;
 | |
| 		break;
 | |
| 	    }
 | |
| 	  case ISEQ_ELEMENT_LABEL:
 | |
| 	    {
 | |
| 		lobj = (LABEL *)list;
 | |
| 		lobj->position = pos;
 | |
| 		lobj->set = TRUE;
 | |
| 		break;
 | |
| 	    }
 | |
| 	  case ISEQ_ELEMENT_NONE:
 | |
| 	    {
 | |
| 		/* ignore */
 | |
| 		break;
 | |
| 	    }
 | |
| 	  case ISEQ_ELEMENT_ADJUST:
 | |
| 	    {
 | |
| 		ADJUST *adjust = (ADJUST *)list;
 | |
| 		if (adjust->line_no != -1) {
 | |
| 		    pos += 2 /* insn + 1 operand */;
 | |
| 		    k++;
 | |
| 		}
 | |
| 		break;
 | |
| 	    }
 | |
| 	  default:
 | |
| 	    dump_disasm_list(FIRST_ELEMENT(anchor));
 | |
| 	    dump_disasm_list(list);
 | |
| 	    rb_compile_error(RSTRING_PTR(iseq->location.path), line,
 | |
| 			     "error: set_sequence");
 | |
| 	    break;
 | |
| 	}
 | |
| 	list = list->next;
 | |
|     }
 | |
| 
 | |
|     /* make instruction sequence */
 | |
|     generated_iseq = ALLOC_N(VALUE, pos);
 | |
|     line_info_table = ALLOC_N(struct iseq_line_info_entry, k);
 | |
|     iseq->is_entries = ALLOC_N(union iseq_inline_storage_entry, iseq->is_size);
 | |
|     MEMZERO(iseq->is_entries, union iseq_inline_storage_entry, iseq->is_size);
 | |
|     iseq->callinfo_entries = ALLOC_N(rb_call_info_t, iseq->callinfo_size);
 | |
|     /* MEMZERO(iseq->callinfo_entries, rb_call_info_t, iseq->callinfo_size); */
 | |
| 
 | |
|     list = FIRST_ELEMENT(anchor);
 | |
|     k = pos = sp = 0;
 | |
| 
 | |
|     while (list) {
 | |
| 	switch (list->type) {
 | |
| 	  case ISEQ_ELEMENT_INSN:
 | |
| 	    {
 | |
| 		int j, len, insn;
 | |
| 		const char *types;
 | |
| 		VALUE *operands;
 | |
| 
 | |
| 		iobj = (INSN *)list;
 | |
| 
 | |
| 		/* update sp */
 | |
| 		sp = calc_sp_depth(sp, iobj);
 | |
| 		if (sp > stack_max) {
 | |
| 		    stack_max = sp;
 | |
| 		}
 | |
| 
 | |
| 		/* fprintf(stderr, "insn: %-16s, sp: %d\n", insn_name(iobj->insn_id), sp); */
 | |
| 		operands = iobj->operands;
 | |
| 		insn = iobj->insn_id;
 | |
| 		generated_iseq[pos] = insn;
 | |
| 		types = insn_op_types(insn);
 | |
| 		len = insn_len(insn);
 | |
| 
 | |
| 		/* operand check */
 | |
| 		if (iobj->operand_size != len - 1) {
 | |
| 		    /* printf("operand size miss! (%d, %d)\n", iobj->operand_size, len); */
 | |
| 		    dump_disasm_list(list);
 | |
| 		    rb_compile_error(RSTRING_PTR(iseq->location.path), iobj->line_no,
 | |
| 				     "operand size miss! (%d for %d)",
 | |
| 				     iobj->operand_size, len - 1);
 | |
| 		    xfree(generated_iseq);
 | |
| 		    xfree(line_info_table);
 | |
| 		    return 0;
 | |
| 		}
 | |
| 
 | |
| 		for (j = 0; types[j]; j++) {
 | |
| 		    char type = types[j];
 | |
| 		    /* printf("--> [%c - (%d-%d)]\n", type, k, j); */
 | |
| 		    switch (type) {
 | |
| 		      case TS_OFFSET:
 | |
| 			{
 | |
| 			    /* label(destination position) */
 | |
| 			    lobj = (LABEL *)operands[j];
 | |
| 			    if (!lobj->set) {
 | |
| 				rb_compile_error(RSTRING_PTR(iseq->location.path), iobj->line_no,
 | |
| 						 "unknown label");
 | |
| 			    }
 | |
| 			    if (lobj->sp == -1) {
 | |
| 				lobj->sp = sp;
 | |
| 			    }
 | |
| 			    generated_iseq[pos + 1 + j] = lobj->position - (pos + len);
 | |
| 			    break;
 | |
| 			}
 | |
| 		      case TS_CDHASH:
 | |
| 			{
 | |
| 			    VALUE map = operands[j];
 | |
| 			    struct cdhash_set_label_struct data;
 | |
|                             data.hash = map;
 | |
|                             data.pos = pos;
 | |
|                             data.len = len;
 | |
| 			    rb_hash_foreach(map, cdhash_set_label_i, (VALUE)&data);
 | |
| 
 | |
| 			    hide_obj(map);
 | |
| 			    generated_iseq[pos + 1 + j] = map;
 | |
| 			    break;
 | |
| 			}
 | |
| 		      case TS_LINDEX:
 | |
| 		      case TS_NUM:	/* ulong */
 | |
| 			generated_iseq[pos + 1 + j] = FIX2INT(operands[j]);
 | |
| 			break;
 | |
| 		      case TS_ISEQ:	/* iseq */
 | |
| 			{
 | |
| 			    VALUE v = operands[j];
 | |
| 			    rb_iseq_t *block = 0;
 | |
| 			    if (v) {
 | |
| 				GetISeqPtr(v, block);
 | |
| 			    }
 | |
| 			    generated_iseq[pos + 1 + j] = (VALUE)block;
 | |
| 			    break;
 | |
| 			}
 | |
| 		      case TS_VALUE:	/* VALUE */
 | |
| 			{
 | |
| 			    VALUE v = operands[j];
 | |
| 			    generated_iseq[pos + 1 + j] = v;
 | |
| 			    /* to mark ruby object */
 | |
| 			    iseq_add_mark_object(iseq, v);
 | |
| 			    break;
 | |
| 			}
 | |
| 		      case TS_IC: /* inline cache */
 | |
| 			{
 | |
| 			    int ic_index = FIX2INT(operands[j]);
 | |
| 			    IC ic = (IC)&iseq->is_entries[ic_index];
 | |
| 			    if (UNLIKELY(ic_index >= iseq->is_size)) {
 | |
| 				rb_bug("iseq_set_sequence: ic_index overflow: index: %d, size: %d", ic_index, iseq->is_size);
 | |
| 			    }
 | |
| 			    generated_iseq[pos + 1 + j] = (VALUE)ic;
 | |
| 			    break;
 | |
| 			}
 | |
| 		      case TS_CALLINFO: /* call info */
 | |
| 			{
 | |
| 			    rb_call_info_t *base_ci = (rb_call_info_t *)operands[j];
 | |
| 			    rb_call_info_t *ci = &iseq->callinfo_entries[base_ci->aux.index];
 | |
| 			    *ci = *base_ci;
 | |
| 
 | |
| 			    if (UNLIKELY(base_ci->aux.index >= iseq->callinfo_size)) {
 | |
| 				rb_bug("iseq_set_sequence: ci_index overflow: index: %d, size: %d", base_ci->argc, iseq->callinfo_size);
 | |
| 			    }
 | |
| 			    generated_iseq[pos + 1 + j] = (VALUE)ci;
 | |
| 			    break;
 | |
| 			}
 | |
| 		      case TS_ID: /* ID */
 | |
| 			generated_iseq[pos + 1 + j] = SYM2ID(operands[j]);
 | |
| 			break;
 | |
| 		      case TS_GENTRY:
 | |
| 			{
 | |
| 			    struct rb_global_entry *entry =
 | |
| 				(struct rb_global_entry *)(operands[j] & (~1));
 | |
| 			    generated_iseq[pos + 1 + j] = (VALUE)entry;
 | |
| 			}
 | |
| 			break;
 | |
| 		      default:
 | |
| 			rb_compile_error(RSTRING_PTR(iseq->location.path), iobj->line_no,
 | |
| 					 "unknown operand type: %c", type);
 | |
| 			xfree(generated_iseq);
 | |
| 			xfree(line_info_table);
 | |
| 			return 0;
 | |
| 		    }
 | |
| 		}
 | |
| 		if (last_line != iobj->line_no) {
 | |
| 		    line_info_table[k].line_no = last_line = iobj->line_no;
 | |
| 		    line_info_table[k].position = pos;
 | |
| 		    k++;
 | |
| 		}
 | |
| 		pos += len;
 | |
| 		break;
 | |
| 	    }
 | |
| 	  case ISEQ_ELEMENT_LABEL:
 | |
| 	    {
 | |
| 		lobj = (LABEL *)list;
 | |
| 		if (lobj->sp == -1) {
 | |
| 		    lobj->sp = sp;
 | |
| 		}
 | |
| 		else {
 | |
| 		    sp = lobj->sp;
 | |
| 		}
 | |
| 		break;
 | |
| 	    }
 | |
| 	  case ISEQ_ELEMENT_ADJUST:
 | |
| 	    {
 | |
| 		ADJUST *adjust = (ADJUST *)list;
 | |
| 		int orig_sp = sp;
 | |
| 
 | |
| 		if (adjust->label) {
 | |
| 		    sp = adjust->label->sp;
 | |
| 		}
 | |
| 		else {
 | |
| 		    sp = 0;
 | |
| 		}
 | |
| 
 | |
| 		if (adjust->line_no != -1) {
 | |
| 		    if (orig_sp - sp > 0) {
 | |
| 			if (last_line != (unsigned int)adjust->line_no) {
 | |
| 			    line_info_table[k].line_no = last_line = adjust->line_no;
 | |
| 			    line_info_table[k].position = pos;
 | |
| 			    k++;
 | |
| 			}
 | |
| 			generated_iseq[pos++] = BIN(adjuststack);
 | |
| 			generated_iseq[pos++] = orig_sp - sp;
 | |
| 		    }
 | |
| 		    else if (orig_sp - sp == 0) {
 | |
| 			/* jump to next insn */
 | |
| 			if (last_line != (unsigned int)adjust->line_no) {
 | |
| 			    line_info_table[k].line_no = last_line = adjust->line_no;
 | |
| 			    line_info_table[k].position = pos;
 | |
| 			    k++;
 | |
| 			}
 | |
| 			generated_iseq[pos++] = BIN(jump);
 | |
| 			generated_iseq[pos++] = 0;
 | |
| 		    }
 | |
| 		    else {
 | |
| 			rb_bug("iseq_set_sequence: adjust bug");
 | |
| 		    }
 | |
| 		}
 | |
| 		break;
 | |
| 	    }
 | |
| 	  default:
 | |
| 	    /* ignore */
 | |
| 	    break;
 | |
| 	}
 | |
| 	list = list->next;
 | |
|     }
 | |
| 
 | |
| #if 0 /* XXX */
 | |
|     /* this check need dead code elimination */
 | |
|     if (sp != 1) {
 | |
| 	rb_bug("SP is not 0 on %s (%d)\n", RSTRING_PTR(iseq->name), sp);
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     iseq->iseq = (void *)generated_iseq;
 | |
|     iseq->iseq_size = pos;
 | |
|     iseq->stack_max = stack_max;
 | |
| 
 | |
|     line_info_table = ruby_xrealloc(line_info_table, k * sizeof(struct iseq_line_info_entry));
 | |
|     iseq->line_info_table = line_info_table;
 | |
|     iseq->line_info_size = k;
 | |
| 
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| static int
 | |
| label_get_position(LABEL *lobj)
 | |
| {
 | |
|     return lobj->position;
 | |
| }
 | |
| 
 | |
| static int
 | |
| label_get_sp(LABEL *lobj)
 | |
| {
 | |
|     return lobj->sp;
 | |
| }
 | |
| 
 | |
| static int
 | |
| iseq_set_exception_table(rb_iseq_t *iseq)
 | |
| {
 | |
|     const VALUE *tptr, *ptr;
 | |
|     int tlen, i;
 | |
|     struct iseq_catch_table_entry *entry;
 | |
| 
 | |
|     tlen = (int)RARRAY_LEN(iseq->compile_data->catch_table_ary);
 | |
|     tptr = RARRAY_RAWPTR(iseq->compile_data->catch_table_ary);
 | |
| 
 | |
|     iseq->catch_table = tlen ? ALLOC_N(struct iseq_catch_table_entry, tlen) : 0;
 | |
|     iseq->catch_table_size = tlen;
 | |
| 
 | |
|     for (i = 0; i < tlen; i++) {
 | |
| 	ptr = RARRAY_RAWPTR(tptr[i]);
 | |
| 	entry = &iseq->catch_table[i];
 | |
| 	entry->type = (enum catch_type)(ptr[0] & 0xffff);
 | |
| 	entry->start = label_get_position((LABEL *)(ptr[1] & ~1));
 | |
| 	entry->end = label_get_position((LABEL *)(ptr[2] & ~1));
 | |
| 	entry->iseq = ptr[3];
 | |
| 
 | |
| 	/* register iseq as mark object */
 | |
| 	if (entry->iseq != 0) {
 | |
| 	    iseq_add_mark_object(iseq, entry->iseq);
 | |
| 	}
 | |
| 
 | |
| 	/* stack depth */
 | |
| 	if (ptr[4]) {
 | |
| 	    LABEL *lobj = (LABEL *)(ptr[4] & ~1);
 | |
| 	    entry->cont = label_get_position(lobj);
 | |
| 	    entry->sp = label_get_sp(lobj);
 | |
| 
 | |
| 	    /* TODO: Dirty Hack!  Fix me */
 | |
| 	    if (entry->type == CATCH_TYPE_RESCUE ||
 | |
| 		entry->type == CATCH_TYPE_BREAK ||
 | |
| 		entry->type == CATCH_TYPE_NEXT) {
 | |
| 		entry->sp--;
 | |
| 	    }
 | |
| 	}
 | |
| 	else {
 | |
| 	    entry->cont = 0;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     OBJ_WRITE(iseq->self, &iseq->compile_data->catch_table_ary, 0); /* free */
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * set optional argument table
 | |
|  *   def foo(a, b=expr1, c=expr2)
 | |
|  *   =>
 | |
|  *    b:
 | |
|  *      expr1
 | |
|  *    c:
 | |
|  *      expr2
 | |
|  */
 | |
| static int
 | |
| iseq_set_optargs_table(rb_iseq_t *iseq)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     if (iseq->arg_opts != 0) {
 | |
| 	for (i = 0; i < iseq->arg_opts; i++) {
 | |
| 	    iseq->arg_opt_table[i] =
 | |
| 		label_get_position((LABEL *)iseq->arg_opt_table[i]);
 | |
| 	}
 | |
|     }
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| static LINK_ELEMENT *
 | |
| get_destination_insn(INSN *iobj)
 | |
| {
 | |
|     LABEL *lobj = (LABEL *)OPERAND_AT(iobj, 0);
 | |
|     LINK_ELEMENT *list;
 | |
| 
 | |
|     list = lobj->link.next;
 | |
|     while (list) {
 | |
| 	if (list->type == ISEQ_ELEMENT_INSN || list->type == ISEQ_ELEMENT_ADJUST) {
 | |
| 	    break;
 | |
| 	}
 | |
| 	list = list->next;
 | |
|     }
 | |
|     return list;
 | |
| }
 | |
| 
 | |
| static LINK_ELEMENT *
 | |
| get_next_insn(INSN *iobj)
 | |
| {
 | |
|     LINK_ELEMENT *list = iobj->link.next;
 | |
| 
 | |
|     while (list) {
 | |
| 	if (list->type == ISEQ_ELEMENT_INSN || list->type == ISEQ_ELEMENT_ADJUST) {
 | |
| 	    return list;
 | |
| 	}
 | |
| 	list = list->next;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static LINK_ELEMENT *
 | |
| get_prev_insn(INSN *iobj)
 | |
| {
 | |
|     LINK_ELEMENT *list = iobj->link.prev;
 | |
| 
 | |
|     while (list) {
 | |
| 	if (list->type == ISEQ_ELEMENT_INSN || list->type == ISEQ_ELEMENT_ADJUST) {
 | |
| 	    return list;
 | |
| 	}
 | |
| 	list = list->prev;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcallopt)
 | |
| {
 | |
|     INSN *iobj = (INSN *)list;
 | |
|   again:
 | |
|     if (iobj->insn_id == BIN(jump)) {
 | |
| 	INSN *niobj, *diobj, *piobj;
 | |
| 	/*
 | |
| 	 *  useless jump elimination:
 | |
| 	 *     jump LABEL1
 | |
| 	 *     ...
 | |
| 	 *   LABEL1:
 | |
| 	 *     jump LABEL2
 | |
| 	 *
 | |
| 	 *   => in this case, first jump instruction should jump to
 | |
| 	 *      LABEL2 directly
 | |
| 	 */
 | |
| 	diobj = (INSN *)get_destination_insn(iobj);
 | |
| 	niobj = (INSN *)get_next_insn(iobj);
 | |
| 
 | |
| 	if (diobj == niobj) {
 | |
| 	    /*
 | |
| 	     *   jump LABEL
 | |
| 	     *  LABEL:
 | |
| 	     * =>
 | |
| 	     *   LABEL:
 | |
| 	     */
 | |
| 	    REMOVE_ELEM(&iobj->link);
 | |
| 	}
 | |
| 	else if (iobj != diobj && diobj->insn_id == BIN(jump)) {
 | |
| 	    if (OPERAND_AT(iobj, 0) != OPERAND_AT(diobj, 0)) {
 | |
| 		OPERAND_AT(iobj, 0) = OPERAND_AT(diobj, 0);
 | |
| 		goto again;
 | |
| 	    }
 | |
| 	}
 | |
| 	else if (diobj->insn_id == BIN(leave)) {
 | |
| 	    /*
 | |
| 	     *  jump LABEL
 | |
| 	     *  ...
 | |
| 	     * LABEL:
 | |
| 	     *  leave
 | |
| 	     * =>
 | |
| 	     *  leave
 | |
| 	     *  ...
 | |
| 	     * LABEL:
 | |
| 	     *  leave
 | |
| 	     */
 | |
| 	    INSN *eiobj = new_insn_core(iseq, iobj->line_no, BIN(leave),
 | |
| 					diobj->operand_size, diobj->operands);
 | |
| 	    INSN *popiobj = new_insn_core(iseq, iobj->line_no,
 | |
| 					  BIN(pop), 0, 0);
 | |
| 	    /* replace */
 | |
| 	    REPLACE_ELEM((LINK_ELEMENT *)iobj, (LINK_ELEMENT *)eiobj);
 | |
| 	    INSERT_ELEM_NEXT((LINK_ELEMENT *)eiobj, (LINK_ELEMENT *)popiobj);
 | |
| 	    iobj = popiobj;
 | |
| 	}
 | |
| 	/*
 | |
| 	 * useless jump elimination (if/unless destination):
 | |
| 	 *   if   L1
 | |
| 	 *   jump L2
 | |
| 	 * L1:
 | |
| 	 *   ...
 | |
| 	 * L2:
 | |
| 	 *
 | |
| 	 * ==>
 | |
| 	 *   unless L2
 | |
| 	 * L1:
 | |
| 	 *   ...
 | |
| 	 * L2:
 | |
| 	 */
 | |
| 	else if ((piobj = (INSN *)get_prev_insn(iobj)) != 0 &&
 | |
| 		 (piobj->insn_id == BIN(branchif) ||
 | |
| 		  piobj->insn_id == BIN(branchunless))) {
 | |
| 	    if (niobj == (INSN *)get_destination_insn(piobj)) {
 | |
| 		piobj->insn_id = (piobj->insn_id == BIN(branchif))
 | |
| 		  ? BIN(branchunless) : BIN(branchif);
 | |
| 		OPERAND_AT(piobj, 0) = OPERAND_AT(iobj, 0);
 | |
| 		REMOVE_ELEM(&iobj->link);
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     if (iobj->insn_id == BIN(branchif) ||
 | |
| 	iobj->insn_id == BIN(branchunless)) {
 | |
| 	/*
 | |
| 	 *   if L1
 | |
| 	 *   ...
 | |
| 	 * L1:
 | |
| 	 *   jump L2
 | |
| 	 * =>
 | |
| 	 *   if L2
 | |
| 	 */
 | |
| 	INSN *nobj = (INSN *)get_destination_insn(iobj);
 | |
| 	if (nobj->insn_id == BIN(jump)) {
 | |
| 	    OPERAND_AT(iobj, 0) = OPERAND_AT(nobj, 0);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     if (do_tailcallopt && iobj->insn_id == BIN(leave)) {
 | |
| 	/*
 | |
| 	 *  send ...
 | |
| 	 *  leave
 | |
| 	 * =>
 | |
| 	 *  send ..., ... | VM_CALL_TAILCALL, ...
 | |
| 	 *  leave # unreachable
 | |
| 	 */
 | |
| 	INSN *piobj = (INSN *)get_prev_insn((INSN *)list);
 | |
| 
 | |
| 	if (piobj->insn_id == BIN(send) || piobj->insn_id == BIN(opt_send_simple)) {
 | |
| 	    rb_call_info_t *ci = (rb_call_info_t *)piobj->operands[0];
 | |
| 	    if (ci->blockiseq == 0) {
 | |
| 		ci->flag |= VM_CALL_TAILCALL;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| static int
 | |
| insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id)
 | |
| {
 | |
|     int old_opsize = iobj->operand_size;
 | |
|     iobj->insn_id = insn_id;
 | |
|     iobj->operand_size = insn_len(insn_id) - 1;
 | |
| 
 | |
|     if (iobj->operand_size > old_opsize) {
 | |
| 	VALUE *old_operands = iobj->operands;
 | |
| 	if (insn_id != BIN(opt_neq)) {
 | |
| 	    rb_bug("insn_set_specialized_instruction: unknown insn: %d", insn_id);
 | |
| 	}
 | |
| 	iobj->operands = (VALUE *)compile_data_alloc(iseq, iobj->operand_size * sizeof(VALUE));
 | |
| 	iobj->operands[0] = old_operands[0];
 | |
| 	iobj->operands[1] = (VALUE)new_callinfo(iseq, idEq, 1, 0, 0);
 | |
|     }
 | |
| 
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| static int
 | |
| iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
 | |
| {
 | |
|     if (iobj->insn_id == BIN(send)) {
 | |
| 	rb_call_info_t *ci = (rb_call_info_t *)OPERAND_AT(iobj, 0);
 | |
| 
 | |
| #define SP_INSN(opt) insn_set_specialized_instruction(iseq, iobj, BIN(opt_##opt))
 | |
| 	if (ci->blockiseq == 0 && (ci->flag & ~VM_CALL_ARGS_SKIP_SETUP) == 0) {
 | |
| 	    switch (ci->orig_argc) {
 | |
| 	      case 0:
 | |
| 		switch (ci->mid) {
 | |
| 		  case idLength: SP_INSN(length); return COMPILE_OK;
 | |
| 		  case idSize:	 SP_INSN(size);	  return COMPILE_OK;
 | |
| 		  case idEmptyP: SP_INSN(empty_p);return COMPILE_OK;
 | |
| 		  case idSucc:	 SP_INSN(succ);	  return COMPILE_OK;
 | |
| 		  case idNot:	 SP_INSN(not);	  return COMPILE_OK;
 | |
| 		}
 | |
| 		break;
 | |
| 	      case 1:
 | |
| 		switch (ci->mid) {
 | |
| 		  case idPLUS:	 SP_INSN(plus);	  return COMPILE_OK;
 | |
| 		  case idMINUS:	 SP_INSN(minus);  return COMPILE_OK;
 | |
| 		  case idMULT:	 SP_INSN(mult);	  return COMPILE_OK;
 | |
| 		  case idDIV:	 SP_INSN(div);	  return COMPILE_OK;
 | |
| 		  case idMOD:	 SP_INSN(mod);	  return COMPILE_OK;
 | |
| 		  case idEq:	 SP_INSN(eq);	  return COMPILE_OK;
 | |
| 		  case idNeq:	 SP_INSN(neq);	  return COMPILE_OK;
 | |
| 		  case idLT:	 SP_INSN(lt);	  return COMPILE_OK;
 | |
| 		  case idLE:	 SP_INSN(le);	  return COMPILE_OK;
 | |
| 		  case idGT:	 SP_INSN(gt);	  return COMPILE_OK;
 | |
| 		  case idGE:	 SP_INSN(ge);	  return COMPILE_OK;
 | |
| 		  case idLTLT:	 SP_INSN(ltlt);	  return COMPILE_OK;
 | |
| 		  case idAREF:	 SP_INSN(aref);	  return COMPILE_OK;
 | |
| 		}
 | |
| 		break;
 | |
| 	    }
 | |
| 	}
 | |
| 	if (ci->flag & VM_CALL_ARGS_SKIP_SETUP) {
 | |
| 	    iobj->insn_id = BIN(opt_send_simple);
 | |
| 	}
 | |
|     }
 | |
| #undef SP_INSN
 | |
| 
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| static int
 | |
| iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
 | |
| {
 | |
|     LINK_ELEMENT *list;
 | |
|     const int do_peepholeopt = iseq->compile_data->option->peephole_optimization;
 | |
|     const int do_tailcallopt = iseq->compile_data->option->tailcall_optimization;
 | |
|     const int do_si = iseq->compile_data->option->specialized_instruction;
 | |
|     const int do_ou = iseq->compile_data->option->operands_unification;
 | |
|     list = FIRST_ELEMENT(anchor);
 | |
| 
 | |
|     while (list) {
 | |
| 	if (list->type == ISEQ_ELEMENT_INSN) {
 | |
| 	    if (do_peepholeopt) {
 | |
| 		iseq_peephole_optimize(iseq, list, do_tailcallopt);
 | |
| 	    }
 | |
| 	    if (do_si) {
 | |
| 		iseq_specialized_instruction(iseq, (INSN *)list);
 | |
| 	    }
 | |
| 	    if (do_ou) {
 | |
| 		insn_operands_unification((INSN *)list);
 | |
| 	    }
 | |
| 	}
 | |
| 	list = list->next;
 | |
|     }
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| #if OPT_INSTRUCTIONS_UNIFICATION
 | |
| static INSN *
 | |
| new_unified_insn(rb_iseq_t *iseq,
 | |
| 		 int insn_id, int size, LINK_ELEMENT *seq_list)
 | |
| {
 | |
|     INSN *iobj = 0;
 | |
|     LINK_ELEMENT *list = seq_list;
 | |
|     int i, argc = 0;
 | |
|     VALUE *operands = 0, *ptr = 0;
 | |
| 
 | |
| 
 | |
|     /* count argc */
 | |
|     for (i = 0; i < size; i++) {
 | |
| 	iobj = (INSN *)list;
 | |
| 	argc += iobj->operand_size;
 | |
| 	list = list->next;
 | |
|     }
 | |
| 
 | |
|     if (argc > 0) {
 | |
| 	ptr = operands =
 | |
| 	    (VALUE *)compile_data_alloc(iseq, sizeof(VALUE) * argc);
 | |
|     }
 | |
| 
 | |
|     /* copy operands */
 | |
|     list = seq_list;
 | |
|     for (i = 0; i < size; i++) {
 | |
| 	iobj = (INSN *)list;
 | |
| 	MEMCPY(ptr, iobj->operands, VALUE, iobj->operand_size);
 | |
| 	ptr += iobj->operand_size;
 | |
| 	list = list->next;
 | |
|     }
 | |
| 
 | |
|     return new_insn_core(iseq, iobj->line_no, insn_id, argc, operands);
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /*
 | |
|  * This scheme can get more performance if do this optimize with
 | |
|  * label address resolving.
 | |
|  * It's future work (if compile time was bottle neck).
 | |
|  */
 | |
| static int
 | |
| iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
 | |
| {
 | |
| #if OPT_INSTRUCTIONS_UNIFICATION
 | |
|     LINK_ELEMENT *list;
 | |
|     INSN *iobj, *niobj;
 | |
|     int id, k;
 | |
|     intptr_t j;
 | |
| 
 | |
|     list = FIRST_ELEMENT(anchor);
 | |
|     while (list) {
 | |
| 	if (list->type == ISEQ_ELEMENT_INSN) {
 | |
| 	    iobj = (INSN *)list;
 | |
| 	    id = iobj->insn_id;
 | |
| 	    if (unified_insns_data[id] != 0) {
 | |
| 		const int *const *entry = unified_insns_data[id];
 | |
| 		for (j = 1; j < (intptr_t)entry[0]; j++) {
 | |
| 		    const int *unified = entry[j];
 | |
| 		    LINK_ELEMENT *li = list->next;
 | |
| 		    for (k = 2; k < unified[1]; k++) {
 | |
| 			if (li->type != ISEQ_ELEMENT_INSN ||
 | |
| 			    ((INSN *)li)->insn_id != unified[k]) {
 | |
| 			    goto miss;
 | |
| 			}
 | |
| 			li = li->next;
 | |
| 		    }
 | |
| 		    /* matched */
 | |
| 		    niobj =
 | |
| 			new_unified_insn(iseq, unified[0], unified[1] - 1,
 | |
| 					 list);
 | |
| 
 | |
| 		    /* insert to list */
 | |
| 		    niobj->link.prev = (LINK_ELEMENT *)iobj->link.prev;
 | |
| 		    niobj->link.next = li;
 | |
| 		    if (li) {
 | |
| 			li->prev = (LINK_ELEMENT *)niobj;
 | |
| 		    }
 | |
| 
 | |
| 		    list->prev->next = (LINK_ELEMENT *)niobj;
 | |
| 		    list = (LINK_ELEMENT *)niobj;
 | |
| 		    break;
 | |
| 		  miss:;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 	list = list->next;
 | |
|     }
 | |
| #endif
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| #if OPT_STACK_CACHING
 | |
| 
 | |
| #define SC_INSN(insn, stat) sc_insn_info[(insn)][(stat)]
 | |
| #define SC_NEXT(insn)       sc_insn_next[(insn)]
 | |
| 
 | |
| #include "opt_sc.inc"
 | |
| 
 | |
| static int
 | |
| insn_set_sc_state(rb_iseq_t *iseq, INSN *iobj, int state)
 | |
| {
 | |
|     int nstate;
 | |
|     int insn_id;
 | |
| 
 | |
|     insn_id = iobj->insn_id;
 | |
|     iobj->insn_id = SC_INSN(insn_id, state);
 | |
|     nstate = SC_NEXT(iobj->insn_id);
 | |
| 
 | |
|     if (insn_id == BIN(jump) ||
 | |
| 	insn_id == BIN(branchif) || insn_id == BIN(branchunless)) {
 | |
| 	LABEL *lobj = (LABEL *)OPERAND_AT(iobj, 0);
 | |
| 
 | |
| 	if (lobj->sc_state != 0) {
 | |
| 	    if (lobj->sc_state != nstate) {
 | |
| 		dump_disasm_list((LINK_ELEMENT *)iobj);
 | |
| 		dump_disasm_list((LINK_ELEMENT *)lobj);
 | |
| 		printf("\n-- %d, %d\n", lobj->sc_state, nstate);
 | |
| 		rb_compile_error(RSTRING_PTR(iseq->location.path), iobj->line_no,
 | |
| 				 "insn_set_sc_state error\n");
 | |
| 		return 0;
 | |
| 	    }
 | |
| 	}
 | |
| 	else {
 | |
| 	    lobj->sc_state = nstate;
 | |
| 	}
 | |
| 	if (insn_id == BIN(jump)) {
 | |
| 	    nstate = SCS_XX;
 | |
| 	}
 | |
|     }
 | |
|     else if (insn_id == BIN(leave)) {
 | |
| 	nstate = SCS_XX;
 | |
|     }
 | |
| 
 | |
|     return nstate;
 | |
| }
 | |
| 
 | |
| static int
 | |
| label_set_sc_state(LABEL *lobj, int state)
 | |
| {
 | |
|     if (lobj->sc_state != 0) {
 | |
| 	if (lobj->sc_state != state) {
 | |
| 	    state = lobj->sc_state;
 | |
| 	}
 | |
|     }
 | |
|     else {
 | |
| 	lobj->sc_state = state;
 | |
|     }
 | |
| 
 | |
|     return state;
 | |
| }
 | |
| 
 | |
| 
 | |
| #endif
 | |
| 
 | |
| static int
 | |
| iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *anchor)
 | |
| {
 | |
| #if OPT_STACK_CACHING
 | |
|     LINK_ELEMENT *list;
 | |
|     int state, insn_id;
 | |
| 
 | |
|     /* initialize */
 | |
|     state = SCS_XX;
 | |
|     list = FIRST_ELEMENT(anchor);
 | |
|     /* dump_disasm_list(list); */
 | |
| 
 | |
|     /* for each list element */
 | |
|     while (list) {
 | |
|       redo_point:
 | |
| 	switch (list->type) {
 | |
| 	  case ISEQ_ELEMENT_INSN:
 | |
| 	    {
 | |
| 		INSN *iobj = (INSN *)list;
 | |
| 		insn_id = iobj->insn_id;
 | |
| 
 | |
| 		/* dump_disasm_list(list); */
 | |
| 
 | |
| 		switch (insn_id) {
 | |
| 		  case BIN(nop):
 | |
| 		    {
 | |
| 			/* exception merge point */
 | |
| 			if (state != SCS_AX) {
 | |
| 			    INSN *rpobj =
 | |
| 				new_insn_body(iseq, 0, BIN(reput), 0);
 | |
| 
 | |
| 			    /* replace this insn */
 | |
| 			    REPLACE_ELEM(list, (LINK_ELEMENT *)rpobj);
 | |
| 			    list = (LINK_ELEMENT *)rpobj;
 | |
| 			    goto redo_point;
 | |
| 			}
 | |
| 			break;
 | |
| 		    }
 | |
| 		  case BIN(swap):
 | |
| 		    {
 | |
| 			if (state == SCS_AB || state == SCS_BA) {
 | |
| 			    state = (state == SCS_AB ? SCS_BA : SCS_AB);
 | |
| 
 | |
| 			    REMOVE_ELEM(list);
 | |
| 			    list = list->next;
 | |
| 			    goto redo_point;
 | |
| 			}
 | |
| 			break;
 | |
| 		    }
 | |
| 		  case BIN(pop):
 | |
| 		    {
 | |
| 			switch (state) {
 | |
| 			  case SCS_AX:
 | |
| 			  case SCS_BX:
 | |
| 			    state = SCS_XX;
 | |
| 			    break;
 | |
| 			  case SCS_AB:
 | |
| 			    state = SCS_AX;
 | |
| 			    break;
 | |
| 			  case SCS_BA:
 | |
| 			    state = SCS_BX;
 | |
| 			    break;
 | |
| 			  case SCS_XX:
 | |
| 			    goto normal_insn;
 | |
| 			  default:
 | |
| 			    rb_compile_error(RSTRING_PTR(iseq->location.path), iobj->line_no,
 | |
| 					     "unreachable");
 | |
| 			}
 | |
| 			/* remove useless pop */
 | |
| 			REMOVE_ELEM(list);
 | |
| 			list = list->next;
 | |
| 			goto redo_point;
 | |
| 		    }
 | |
| 		  default:;
 | |
| 		    /* none */
 | |
| 		}		/* end of switch */
 | |
| 	      normal_insn:
 | |
| 		state = insn_set_sc_state(iseq, iobj, state);
 | |
| 		break;
 | |
| 	    }
 | |
| 	  case ISEQ_ELEMENT_LABEL:
 | |
| 	    {
 | |
| 		LABEL *lobj;
 | |
| 		lobj = (LABEL *)list;
 | |
| 
 | |
| 		state = label_set_sc_state(lobj, state);
 | |
| 	    }
 | |
| 	  default:
 | |
| 	    break;
 | |
| 	}
 | |
| 	list = list->next;
 | |
|     }
 | |
| #endif
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| static int
 | |
| compile_dstr_fragments(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node, int *cntp)
 | |
| {
 | |
|     NODE *list = node->nd_next;
 | |
|     VALUE lit = node->nd_lit;
 | |
|     int cnt = 0;
 | |
| 
 | |
|     debugp_param("nd_lit", lit);
 | |
|     if (!NIL_P(lit)) {
 | |
| 	hide_obj(lit);
 | |
| 	cnt++;
 | |
| 	ADD_INSN1(ret, nd_line(node), putobject, lit);
 | |
|     }
 | |
| 
 | |
|     while (list) {
 | |
| 	node = list->nd_head;
 | |
| 	if (nd_type(node) == NODE_STR) {
 | |
| 	    hide_obj(node->nd_lit);
 | |
| 	    ADD_INSN1(ret, nd_line(node), putobject, node->nd_lit);
 | |
| 	}
 | |
| 	else {
 | |
| 	    COMPILE(ret, "each string", node);
 | |
| 	}
 | |
| 	cnt++;
 | |
| 	list = list->nd_next;
 | |
|     }
 | |
|     *cntp = cnt;
 | |
| 
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| static int
 | |
| compile_dstr(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node)
 | |
| {
 | |
|     int cnt;
 | |
|     compile_dstr_fragments(iseq, ret, node, &cnt);
 | |
|     ADD_INSN1(ret, nd_line(node), concatstrings, INT2FIX(cnt));
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| static int
 | |
| compile_dregx(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node)
 | |
| {
 | |
|     int cnt;
 | |
|     compile_dstr_fragments(iseq, ret, node, &cnt);
 | |
|     ADD_INSN2(ret, nd_line(node), toregexp, INT2FIX(node->nd_cflag), INT2FIX(cnt));
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| static int
 | |
| compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * cond,
 | |
| 			 LABEL *then_label, LABEL *else_label)
 | |
| {
 | |
|     switch (nd_type(cond)) {
 | |
|       case NODE_AND:
 | |
| 	{
 | |
| 	    LABEL *label = NEW_LABEL(nd_line(cond));
 | |
| 	    compile_branch_condition(iseq, ret, cond->nd_1st, label,
 | |
| 				     else_label);
 | |
| 	    ADD_LABEL(ret, label);
 | |
| 	    compile_branch_condition(iseq, ret, cond->nd_2nd, then_label,
 | |
| 				     else_label);
 | |
| 	    break;
 | |
| 	}
 | |
|       case NODE_OR:
 | |
| 	{
 | |
| 	    LABEL *label = NEW_LABEL(nd_line(cond));
 | |
| 	    compile_branch_condition(iseq, ret, cond->nd_1st, then_label,
 | |
| 				     label);
 | |
| 	    ADD_LABEL(ret, label);
 | |
| 	    compile_branch_condition(iseq, ret, cond->nd_2nd, then_label,
 | |
| 				     else_label);
 | |
| 	    break;
 | |
| 	}
 | |
|       case NODE_LIT:		/* NODE_LIT is always not true */
 | |
|       case NODE_TRUE:
 | |
|       case NODE_STR:
 | |
| 	/* printf("useless condition eliminate (%s)\n",  ruby_node_name(nd_type(cond))); */
 | |
| 	ADD_INSNL(ret, nd_line(cond), jump, then_label);
 | |
| 	break;
 | |
|       case NODE_FALSE:
 | |
|       case NODE_NIL:
 | |
| 	/* printf("useless condition eliminate (%s)\n", ruby_node_name(nd_type(cond))); */
 | |
| 	ADD_INSNL(ret, nd_line(cond), jump, else_label);
 | |
| 	break;
 | |
|       default:
 | |
| 	COMPILE(ret, "branch condition", cond);
 | |
| 	ADD_INSNL(ret, nd_line(cond), branchunless, else_label);
 | |
| 	ADD_INSNL(ret, nd_line(cond), jump, then_label);
 | |
| 	break;
 | |
|     }
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| enum compile_array_type_t {
 | |
|     COMPILE_ARRAY_TYPE_ARRAY,
 | |
|     COMPILE_ARRAY_TYPE_HASH,
 | |
|     COMPILE_ARRAY_TYPE_ARGS
 | |
| };
 | |
| 
 | |
| static int
 | |
| compile_array_(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root,
 | |
| 	       enum compile_array_type_t type, int poped)
 | |
| {
 | |
|     NODE *node = node_root;
 | |
|     int line = (int)nd_line(node);
 | |
|     int len = 0;
 | |
| 
 | |
|     if (nd_type(node) == NODE_ZARRAY) {
 | |
| 	if (!poped) {
 | |
| 	    switch (type) {
 | |
| 	      case COMPILE_ARRAY_TYPE_ARRAY: ADD_INSN1(ret, line, newarray, INT2FIX(0)); break;
 | |
| 	      case COMPILE_ARRAY_TYPE_HASH: ADD_INSN1(ret, line, newhash, INT2FIX(0)); break;
 | |
| 	      case COMPILE_ARRAY_TYPE_ARGS: /* do nothing */ break;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     else {
 | |
| 	int opt_p = 1;
 | |
| 	int first = 1, i;
 | |
| 
 | |
| 	while (node) {
 | |
| 	    NODE *start_node = node, *end_node;
 | |
| 	    NODE *kw = 0;
 | |
| 	    const int max = 0x100;
 | |
| 	    DECL_ANCHOR(anchor);
 | |
| 	    INIT_ANCHOR(anchor);
 | |
| 
 | |
| 	    for (i=0; i<max && node; i++, len++, node = node->nd_next) {
 | |
| 		if (CPDEBUG > 0 && nd_type(node) != NODE_ARRAY) {
 | |
| 		    rb_bug("compile_array: This node is not NODE_ARRAY, but %s", ruby_node_name(nd_type(node)));
 | |
| 		}
 | |
| 
 | |
| 		if (type == COMPILE_ARRAY_TYPE_HASH && !node->nd_head) {
 | |
| 		    opt_p = 0;
 | |
| 		    kw = node->nd_next;
 | |
| 		    node = kw->nd_next;
 | |
| 		    kw = kw->nd_head;
 | |
| 		    break;
 | |
| 		}
 | |
| 		if (opt_p && nd_type(node->nd_head) != NODE_LIT) {
 | |
| 		    opt_p = 0;
 | |
| 		}
 | |
| 
 | |
| 		COMPILE_(anchor, "array element", node->nd_head, poped);
 | |
| 	    }
 | |
| 
 | |
| 	    if (opt_p && type != COMPILE_ARRAY_TYPE_ARGS) {
 | |
| 		if (!poped) {
 | |
| 		    VALUE ary = rb_ary_tmp_new(i);
 | |
| 
 | |
| 		    end_node = node;
 | |
| 		    node = start_node;
 | |
| 
 | |
| 		    while (node != end_node) {
 | |
| 			rb_ary_push(ary, node->nd_head->nd_lit);
 | |
| 			node = node->nd_next;
 | |
| 		    }
 | |
| 		    while (node && nd_type(node->nd_head) == NODE_LIT &&
 | |
| 			   node->nd_next && nd_type(node->nd_next->nd_head) == NODE_LIT) {
 | |
| 			rb_ary_push(ary, node->nd_head->nd_lit);
 | |
| 			node = node->nd_next;
 | |
| 			rb_ary_push(ary, node->nd_head->nd_lit);
 | |
| 			node = node->nd_next;
 | |
| 			len++;
 | |
| 		    }
 | |
| 
 | |
| 		    OBJ_FREEZE(ary);
 | |
| 
 | |
| 		    iseq_add_mark_object_compile_time(iseq, ary);
 | |
| 
 | |
| 		    if (first) {
 | |
| 			first = 0;
 | |
| 			if (type == COMPILE_ARRAY_TYPE_ARRAY) {
 | |
| 			    ADD_INSN1(ret, line, duparray, ary);
 | |
| 			}
 | |
| 			else { /* COMPILE_ARRAY_TYPE_HASH */
 | |
| 			    ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | |
| 			    ADD_INSN1(ret, line, putobject, ary);
 | |
| 			    ADD_SEND(ret, line, ID2SYM(id_core_hash_from_ary), INT2FIX(1));
 | |
| 			}
 | |
| 		    }
 | |
| 		    else {
 | |
| 			if (type == COMPILE_ARRAY_TYPE_ARRAY) {
 | |
| 			    ADD_INSN1(ret, line, putobject, ary);
 | |
| 			    ADD_INSN(ret, line, concatarray);
 | |
| 			}
 | |
| 			else {
 | |
| 			    ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | |
| 			    ADD_INSN1(ret, line, putobject, ary);
 | |
| 			    ADD_SEND(ret, line, ID2SYM(id_core_hash_merge_ary), INT2FIX(1));
 | |
| 			}
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	    else {
 | |
| 		if (!poped) {
 | |
| 		    switch (type) {
 | |
| 		      case COMPILE_ARRAY_TYPE_ARRAY:
 | |
| 			ADD_INSN1(anchor, line, newarray, INT2FIX(i));
 | |
| 
 | |
| 			if (first) {
 | |
| 			    first = 0;
 | |
| 			}
 | |
| 			else {
 | |
| 			    ADD_INSN(anchor, line, concatarray);
 | |
| 			}
 | |
| 
 | |
| 			APPEND_LIST(ret, anchor);
 | |
| 			break;
 | |
| 		      case COMPILE_ARRAY_TYPE_HASH:
 | |
| 			if (first) {
 | |
| 			    first = 0;
 | |
| 			    ADD_INSN1(anchor, line, newhash, INT2FIX(i));
 | |
| 			    APPEND_LIST(ret, anchor);
 | |
| 			}
 | |
| 			else if (i > 0) {
 | |
| 			    ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | |
| 			    ADD_INSN(ret, line, swap);
 | |
| 			    APPEND_LIST(ret, anchor);
 | |
| 			    ADD_SEND(ret, line, ID2SYM(id_core_hash_merge_ptr), INT2FIX(i + 1));
 | |
| 			}
 | |
| 			if (kw) {
 | |
| 			    ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | |
| 			    ADD_INSN(ret, line, swap);
 | |
| 			    COMPILE(ret, "keyword splat", kw);
 | |
| 			    ADD_SEND(ret, line, ID2SYM(id_core_hash_merge_kwd), INT2FIX(2));
 | |
| 			}
 | |
| 			break;
 | |
| 		      case COMPILE_ARRAY_TYPE_ARGS:
 | |
| 			APPEND_LIST(ret, anchor);
 | |
| 			break;
 | |
| 		    }
 | |
| 		}
 | |
| 		else {
 | |
| 		    /* poped */
 | |
| 		    APPEND_LIST(ret, anchor);
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     return len;
 | |
| }
 | |
| 
 | |
| static VALUE
 | |
| compile_array(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE* node_root, enum compile_array_type_t type)
 | |
| {
 | |
|     return compile_array_(iseq, ret, node_root, type, 0);
 | |
| }
 | |
| 
 | |
| static VALUE
 | |
| case_when_optimizable_literal(NODE * node)
 | |
| {
 | |
|     switch (nd_type(node)) {
 | |
|       case NODE_LIT: {
 | |
| 	VALUE v = node->nd_lit;
 | |
| 	double ival;
 | |
| 	if (RB_TYPE_P(v, T_FLOAT) &&
 | |
| 	    modf(RFLOAT_VALUE(v), &ival) == 0.0) {
 | |
| 	    return FIXABLE(ival) ? LONG2FIX((long)ival) : rb_dbl2big(ival);
 | |
| 	}
 | |
| 	if (SYMBOL_P(v) || RB_TYPE_P(v, T_STRING) ||
 | |
| 	    rb_obj_is_kind_of(v, rb_cNumeric)) {
 | |
| 	    return v;
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_STR:
 | |
| 	return node->nd_lit;
 | |
|     }
 | |
|     return Qundef;
 | |
| }
 | |
| 
 | |
| static int
 | |
| when_vals(rb_iseq_t *iseq, LINK_ANCHOR *cond_seq, NODE *vals, LABEL *l1, int only_special_literals, VALUE literals)
 | |
| {
 | |
|     while (vals) {
 | |
| 	NODE* val = vals->nd_head;
 | |
| 	VALUE lit = case_when_optimizable_literal(val);
 | |
| 
 | |
| 	if (lit == Qundef) {
 | |
| 	    only_special_literals = 0;
 | |
| 	}
 | |
| 	else {
 | |
| 	    if (rb_hash_lookup(literals, lit) != Qnil) {
 | |
| 		rb_compile_warning(RSTRING_PTR(iseq->location.path), nd_line(val), "duplicated when clause is ignored");
 | |
| 	    }
 | |
| 	    else {
 | |
| 		rb_hash_aset(literals, lit, (VALUE)(l1) | 1);
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
| 	ADD_INSN(cond_seq, nd_line(val), dup); /* dup target */
 | |
| 
 | |
| 	if (nd_type(val) == NODE_STR) {
 | |
| 	    debugp_param("nd_lit", val->nd_lit);
 | |
| 	    OBJ_FREEZE(val->nd_lit);
 | |
| 	    ADD_INSN1(cond_seq, nd_line(val), putobject, val->nd_lit);
 | |
| 	}
 | |
| 	else {
 | |
| 	    COMPILE(cond_seq, "when cond", val);
 | |
| 	}
 | |
| 
 | |
| 	ADD_INSN1(cond_seq, nd_line(vals), checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE));
 | |
| 	ADD_INSNL(cond_seq, nd_line(val), branchif, l1);
 | |
| 	vals = vals->nd_next;
 | |
|     }
 | |
|     return only_special_literals;
 | |
| }
 | |
| 
 | |
| static int
 | |
| compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node)
 | |
| {
 | |
|     switch (nd_type(node)) {
 | |
|       case NODE_ATTRASGN: {
 | |
| 	INSN *iobj;
 | |
| 	rb_call_info_t *ci;
 | |
| 	VALUE dupidx;
 | |
| 
 | |
| 	COMPILE_POPED(ret, "masgn lhs (NODE_ATTRASGN)", node);
 | |
| 
 | |
| 	POP_ELEMENT(ret);        /* pop pop insn */
 | |
| 	iobj = (INSN *)POP_ELEMENT(ret); /* pop send insn */
 | |
| 	ci = (rb_call_info_t *)iobj->operands[0];
 | |
| 	ci->orig_argc += 1; ci->argc = ci->orig_argc;
 | |
| 	dupidx = INT2FIX(ci->orig_argc);
 | |
| 
 | |
| 	ADD_INSN1(ret, nd_line(node), topn, dupidx);
 | |
| 	ADD_ELEM(ret, (LINK_ELEMENT *)iobj);
 | |
| 	ADD_INSN(ret, nd_line(node), pop);	/* result */
 | |
| 	ADD_INSN(ret, nd_line(node), pop);	/* rhs    */
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_MASGN: {
 | |
| 	DECL_ANCHOR(anchor);
 | |
| 	INIT_ANCHOR(anchor);
 | |
| 	COMPILE_POPED(anchor, "nest masgn lhs", node);
 | |
| 	REMOVE_ELEM(FIRST_ELEMENT(anchor));
 | |
| 	ADD_SEQ(ret, anchor);
 | |
| 	break;
 | |
|       }
 | |
|       default: {
 | |
| 	DECL_ANCHOR(anchor);
 | |
| 	INIT_ANCHOR(anchor);
 | |
| 	COMPILE_POPED(anchor, "masgn lhs", node);
 | |
| 	REMOVE_ELEM(FIRST_ELEMENT(anchor));
 | |
| 	ADD_SEQ(ret, anchor);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| static void
 | |
| compile_massign_opt_lhs(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *lhsn)
 | |
| {
 | |
|     if (lhsn) {
 | |
| 	compile_massign_opt_lhs(iseq, ret, lhsn->nd_next);
 | |
| 	compile_massign_lhs(iseq, ret, lhsn->nd_head);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static int
 | |
| compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *ret,
 | |
| 		    NODE *rhsn, NODE *orig_lhsn)
 | |
| {
 | |
|     VALUE mem[64];
 | |
|     const int memsize = numberof(mem);
 | |
|     int memindex = 0;
 | |
|     int llen = 0, rlen = 0;
 | |
|     int i;
 | |
|     NODE *lhsn = orig_lhsn;
 | |
| 
 | |
| #define MEMORY(v) { \
 | |
|     int i; \
 | |
|     if (memindex == memsize) return 0; \
 | |
|     for (i=0; i<memindex; i++) { \
 | |
| 	if (mem[i] == (v)) return 0; \
 | |
|     } \
 | |
|     mem[memindex++] = (v); \
 | |
| }
 | |
| 
 | |
|     if (rhsn == 0 || nd_type(rhsn) != NODE_ARRAY) {
 | |
| 	return 0;
 | |
|     }
 | |
| 
 | |
|     while (lhsn) {
 | |
| 	NODE *ln = lhsn->nd_head;
 | |
| 	switch (nd_type(ln)) {
 | |
| 	  case NODE_LASGN:
 | |
| 	    MEMORY(ln->nd_vid);
 | |
| 	    break;
 | |
| 	  case NODE_DASGN:
 | |
| 	  case NODE_DASGN_CURR:
 | |
| 	  case NODE_IASGN:
 | |
| 	  case NODE_IASGN2:
 | |
| 	  case NODE_CVASGN:
 | |
| 	    MEMORY(ln->nd_vid);
 | |
| 	    break;
 | |
| 	  default:
 | |
| 	    return 0;
 | |
| 	}
 | |
| 	lhsn = lhsn->nd_next;
 | |
| 	llen++;
 | |
|     }
 | |
| 
 | |
|     while (rhsn) {
 | |
| 	if (llen <= rlen) {
 | |
| 	    COMPILE_POPED(ret, "masgn val (popped)", rhsn->nd_head);
 | |
| 	}
 | |
| 	else {
 | |
| 	    COMPILE(ret, "masgn val", rhsn->nd_head);
 | |
| 	}
 | |
| 	rhsn = rhsn->nd_next;
 | |
| 	rlen++;
 | |
|     }
 | |
| 
 | |
|     if (llen > rlen) {
 | |
| 	for (i=0; i<llen-rlen; i++) {
 | |
| 	    ADD_INSN(ret, nd_line(orig_lhsn), putnil);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     compile_massign_opt_lhs(iseq, ret, orig_lhsn);
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| static int
 | |
| compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node, int poped)
 | |
| {
 | |
|     NODE *rhsn = node->nd_value;
 | |
|     NODE *splatn = node->nd_args;
 | |
|     NODE *lhsn = node->nd_head;
 | |
|     int lhs_splat = (splatn && (VALUE)splatn != (VALUE)-1) ? 1 : 0;
 | |
| 
 | |
|     if (!poped || splatn || !compile_massign_opt(iseq, ret, rhsn, lhsn)) {
 | |
| 	int llen = 0;
 | |
| 	DECL_ANCHOR(lhsseq);
 | |
| 
 | |
| 	INIT_ANCHOR(lhsseq);
 | |
| 
 | |
| 	while (lhsn) {
 | |
| 	    compile_massign_lhs(iseq, lhsseq, lhsn->nd_head);
 | |
| 	    llen += 1;
 | |
| 	    lhsn = lhsn->nd_next;
 | |
| 	}
 | |
| 
 | |
| 	COMPILE(ret, "normal masgn rhs", rhsn);
 | |
| 
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN(ret, nd_line(node), dup);
 | |
| 	}
 | |
| 
 | |
| 	ADD_INSN2(ret, nd_line(node), expandarray,
 | |
| 		  INT2FIX(llen), INT2FIX(lhs_splat));
 | |
| 	ADD_SEQ(ret, lhsseq);
 | |
| 
 | |
| 	if (lhs_splat) {
 | |
| 	    if (nd_type(splatn) == NODE_POSTARG) {
 | |
| 		/*a, b, *r, p1, p2 */
 | |
| 		NODE *postn = splatn->nd_2nd;
 | |
| 		NODE *restn = splatn->nd_1st;
 | |
| 		int num = (int)postn->nd_alen;
 | |
| 		int flag = 0x02 | (((VALUE)restn == (VALUE)-1) ? 0x00 : 0x01);
 | |
| 
 | |
| 		ADD_INSN2(ret, nd_line(splatn), expandarray,
 | |
| 			  INT2FIX(num), INT2FIX(flag));
 | |
| 
 | |
| 		if ((VALUE)restn != (VALUE)-1) {
 | |
| 		    compile_massign_lhs(iseq, ret, restn);
 | |
| 		}
 | |
| 		while (postn) {
 | |
| 		    compile_massign_lhs(iseq, ret, postn->nd_head);
 | |
| 		    postn = postn->nd_next;
 | |
| 		}
 | |
| 	    }
 | |
| 	    else {
 | |
| 		/* a, b, *r */
 | |
| 		compile_massign_lhs(iseq, ret, splatn);
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| static int
 | |
| compile_colon2(rb_iseq_t *iseq, NODE * node,
 | |
| 	       LINK_ANCHOR *pref, LINK_ANCHOR *body)
 | |
| {
 | |
|     switch (nd_type(node)) {
 | |
|       case NODE_CONST:
 | |
| 	debugi("compile_colon2 - colon", node->nd_vid);
 | |
| 	ADD_INSN1(body, nd_line(node), getconstant, ID2SYM(node->nd_vid));
 | |
| 	break;
 | |
|       case NODE_COLON3:
 | |
| 	debugi("compile_colon2 - colon3", node->nd_mid);
 | |
| 	ADD_INSN(body, nd_line(node), pop);
 | |
| 	ADD_INSN1(body, nd_line(node), putobject, rb_cObject);
 | |
| 	ADD_INSN1(body, nd_line(node), getconstant, ID2SYM(node->nd_mid));
 | |
| 	break;
 | |
|       case NODE_COLON2:
 | |
| 	compile_colon2(iseq, node->nd_head, pref, body);
 | |
| 	debugi("compile_colon2 - colon2", node->nd_mid);
 | |
| 	ADD_INSN1(body, nd_line(node), getconstant, ID2SYM(node->nd_mid));
 | |
| 	break;
 | |
|       default:
 | |
| 	COMPILE(pref, "const colon2 prefix", node);
 | |
| 	break;
 | |
|     }
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| static VALUE
 | |
| compile_cpath(LINK_ANCHOR *ret, rb_iseq_t *iseq, NODE *cpath)
 | |
| {
 | |
|     if (nd_type(cpath) == NODE_COLON3) {
 | |
| 	/* toplevel class ::Foo */
 | |
| 	ADD_INSN1(ret, nd_line(cpath), putobject, rb_cObject);
 | |
| 	return Qfalse;
 | |
|     }
 | |
|     else if (cpath->nd_head) {
 | |
| 	/* Bar::Foo */
 | |
| 	COMPILE(ret, "nd_else->nd_head", cpath->nd_head);
 | |
| 	return Qfalse;
 | |
|     }
 | |
|     else {
 | |
| 	/* class at cbase Foo */
 | |
| 	ADD_INSN1(ret, nd_line(cpath), putspecialobject,
 | |
| 		  INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
 | |
| 	return Qtrue;
 | |
|     }
 | |
| }
 | |
| 
 | |
| #define defined_expr defined_expr0
 | |
| static int
 | |
| defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *ret,
 | |
| 	     NODE *node, LABEL **lfinish, VALUE needstr)
 | |
| {
 | |
|     enum defined_type expr_type = 0;
 | |
|     enum node_type type;
 | |
| 
 | |
|     switch (type = nd_type(node)) {
 | |
| 
 | |
| 	/* easy literals */
 | |
|       case NODE_NIL:
 | |
| 	expr_type = DEFINED_NIL;
 | |
| 	break;
 | |
|       case NODE_SELF:
 | |
| 	expr_type = DEFINED_SELF;
 | |
| 	break;
 | |
|       case NODE_TRUE:
 | |
| 	expr_type = DEFINED_TRUE;
 | |
| 	break;
 | |
|       case NODE_FALSE:
 | |
| 	expr_type = DEFINED_FALSE;
 | |
| 	break;
 | |
| 
 | |
|       case NODE_ARRAY:{
 | |
| 	NODE *vals = node;
 | |
| 
 | |
| 	do {
 | |
| 	    defined_expr(iseq, ret, vals->nd_head, lfinish, Qfalse);
 | |
| 
 | |
| 	    if (!lfinish[1]) {
 | |
| 		lfinish[1] = NEW_LABEL(nd_line(node));
 | |
| 	    }
 | |
| 	    ADD_INSNL(ret, nd_line(node), branchunless, lfinish[1]);
 | |
| 	} while ((vals = vals->nd_next) != NULL);
 | |
|       }
 | |
|       case NODE_STR:
 | |
|       case NODE_LIT:
 | |
|       case NODE_ZARRAY:
 | |
|       case NODE_AND:
 | |
|       case NODE_OR:
 | |
|       default:
 | |
| 	expr_type = DEFINED_EXPR;
 | |
| 	break;
 | |
| 
 | |
| 	/* variables */
 | |
|       case NODE_LVAR:
 | |
|       case NODE_DVAR:
 | |
| 	expr_type = DEFINED_LVAR;
 | |
| 	break;
 | |
| 
 | |
|       case NODE_IVAR:
 | |
| 	ADD_INSN(ret, nd_line(node), putnil);
 | |
| 	ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_IVAR),
 | |
| 		  ID2SYM(node->nd_vid), needstr);
 | |
| 	return 1;
 | |
| 
 | |
|       case NODE_GVAR:
 | |
| 	ADD_INSN(ret, nd_line(node), putnil);
 | |
| 	ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_GVAR),
 | |
| 		  ID2SYM(node->nd_entry->id), needstr);
 | |
| 	return 1;
 | |
| 
 | |
|       case NODE_CVAR:
 | |
| 	ADD_INSN(ret, nd_line(node), putnil);
 | |
| 	ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_CVAR),
 | |
| 		  ID2SYM(node->nd_vid), needstr);
 | |
| 	return 1;
 | |
| 
 | |
|       case NODE_CONST:
 | |
| 	ADD_INSN(ret, nd_line(node), putnil);
 | |
| 	ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_CONST),
 | |
| 		  ID2SYM(node->nd_vid), needstr);
 | |
| 	return 1;
 | |
|       case NODE_COLON2:
 | |
| 	if (!lfinish[1]) {
 | |
| 	    lfinish[1] = NEW_LABEL(nd_line(node));
 | |
| 	}
 | |
| 	defined_expr(iseq, ret, node->nd_head, lfinish, Qfalse);
 | |
| 	ADD_INSNL(ret, nd_line(node), branchunless, lfinish[1]);
 | |
| 
 | |
| 	if (rb_is_const_id(node->nd_mid)) {
 | |
| 	    COMPILE(ret, "defined/colon2#nd_head", node->nd_head);
 | |
| 	    ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_CONST),
 | |
| 		      ID2SYM(node->nd_mid), needstr);
 | |
| 	}
 | |
| 	else {
 | |
| 	    COMPILE(ret, "defined/colon2#nd_head", node->nd_head);
 | |
| 	    ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_METHOD),
 | |
| 		      ID2SYM(node->nd_mid), needstr);
 | |
| 	}
 | |
| 	return 1;
 | |
|       case NODE_COLON3:
 | |
| 	ADD_INSN1(ret, nd_line(node), putobject, rb_cObject);
 | |
| 	ADD_INSN3(ret, nd_line(node), defined,
 | |
| 		  INT2FIX(DEFINED_CONST), ID2SYM(node->nd_mid), needstr);
 | |
| 	return 1;
 | |
| 
 | |
| 	/* method dispatch */
 | |
|       case NODE_CALL:
 | |
|       case NODE_VCALL:
 | |
|       case NODE_FCALL:
 | |
|       case NODE_ATTRASGN:{
 | |
| 	int self = TRUE;
 | |
| 
 | |
| 	switch (type) {
 | |
| 	  case NODE_ATTRASGN:
 | |
| 	    if (node->nd_recv == (NODE *)1) break;
 | |
| 	  case NODE_CALL:
 | |
| 	    self = FALSE;
 | |
| 	    break;
 | |
| 	  default:
 | |
| 	    /* through */;
 | |
| 	}
 | |
| 	if (!lfinish[1]) {
 | |
| 	    lfinish[1] = NEW_LABEL(nd_line(node));
 | |
| 	}
 | |
| 	if (node->nd_args) {
 | |
| 	    defined_expr(iseq, ret, node->nd_args, lfinish, Qfalse);
 | |
| 	    ADD_INSNL(ret, nd_line(node), branchunless, lfinish[1]);
 | |
| 	}
 | |
| 	if (!self) {
 | |
| 	    defined_expr(iseq, ret, node->nd_recv, lfinish, Qfalse);
 | |
| 	    ADD_INSNL(ret, nd_line(node), branchunless, lfinish[1]);
 | |
| 	    COMPILE(ret, "defined/recv", node->nd_recv);
 | |
| 	    ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_METHOD),
 | |
| 		      ID2SYM(node->nd_mid), needstr);
 | |
| 	}
 | |
| 	else {
 | |
| 	    ADD_INSN(ret, nd_line(node), putself);
 | |
| 	    ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_FUNC),
 | |
| 		      ID2SYM(node->nd_mid), needstr);
 | |
| 	}
 | |
| 	return 1;
 | |
|       }
 | |
| 
 | |
|       case NODE_YIELD:
 | |
| 	ADD_INSN(ret, nd_line(node), putnil);
 | |
| 	ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_YIELD), 0,
 | |
| 		  needstr);
 | |
| 	return 1;
 | |
| 
 | |
|       case NODE_BACK_REF:
 | |
|       case NODE_NTH_REF:
 | |
| 	ADD_INSN(ret, nd_line(node), putnil);
 | |
| 	ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_REF),
 | |
| 		  INT2FIX((node->nd_nth << 1) | (type == NODE_BACK_REF)),
 | |
| 		  needstr);
 | |
| 	return 1;
 | |
| 
 | |
|       case NODE_SUPER:
 | |
|       case NODE_ZSUPER:
 | |
| 	ADD_INSN(ret, nd_line(node), putnil);
 | |
| 	ADD_INSN3(ret, nd_line(node), defined, INT2FIX(DEFINED_ZSUPER), 0,
 | |
| 		  needstr);
 | |
| 	return 1;
 | |
| 
 | |
|       case NODE_OP_ASGN1:
 | |
|       case NODE_OP_ASGN2:
 | |
|       case NODE_OP_ASGN_OR:
 | |
|       case NODE_OP_ASGN_AND:
 | |
|       case NODE_MASGN:
 | |
|       case NODE_LASGN:
 | |
|       case NODE_DASGN:
 | |
|       case NODE_DASGN_CURR:
 | |
|       case NODE_GASGN:
 | |
|       case NODE_IASGN:
 | |
|       case NODE_CDECL:
 | |
|       case NODE_CVDECL:
 | |
|       case NODE_CVASGN:
 | |
| 	expr_type = DEFINED_ASGN;
 | |
| 	break;
 | |
|     }
 | |
| 
 | |
|     if (expr_type) {
 | |
| 	if (needstr != Qfalse) {
 | |
| 	    VALUE str = rb_iseq_defined_string(expr_type);
 | |
| 	    ADD_INSN1(ret, nd_line(node), putobject, str);
 | |
| 	}
 | |
| 	else {
 | |
| 	    ADD_INSN1(ret, nd_line(node), putobject, Qtrue);
 | |
| 	}
 | |
| 	return 1;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| #undef defined_expr
 | |
| 
 | |
| static int
 | |
| defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *ret,
 | |
| 	     NODE *node, LABEL **lfinish, VALUE needstr)
 | |
| {
 | |
|     LINK_ELEMENT *lcur = ret->last;
 | |
|     int done = defined_expr0(iseq, ret, node, lfinish, needstr);
 | |
|     if (lfinish[1]) {
 | |
| 	int line = nd_line(node);
 | |
| 	LABEL *lstart = NEW_LABEL(line);
 | |
| 	LABEL *lend = NEW_LABEL(line);
 | |
| 	VALUE rescue = NEW_CHILD_ISEQVAL(NEW_NIL(),
 | |
| 					 rb_str_concat(rb_str_new2
 | |
| 						       ("defined guard in "),
 | |
| 						       iseq->location.label),
 | |
| 					 ISEQ_TYPE_DEFINED_GUARD, 0);
 | |
| 	APPEND_LABEL(ret, lcur, lstart);
 | |
| 	ADD_LABEL(ret, lend);
 | |
| 	ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]);
 | |
|     }
 | |
|     return done;
 | |
| }
 | |
| 
 | |
| #define BUFSIZE 0x100
 | |
| 
 | |
| static VALUE
 | |
| make_name_for_block(rb_iseq_t *iseq)
 | |
| {
 | |
|     int level = 1;
 | |
|     rb_iseq_t *ip = iseq;
 | |
| 
 | |
|     if (iseq->parent_iseq != 0) {
 | |
| 	while (ip->local_iseq != ip) {
 | |
| 	    if (ip->type == ISEQ_TYPE_BLOCK) {
 | |
| 		level++;
 | |
| 	    }
 | |
| 	    ip = ip->parent_iseq;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     if (level == 1) {
 | |
| 	return rb_sprintf("block in %"PRIsVALUE, ip->location.label);
 | |
|     }
 | |
|     else {
 | |
| 	return rb_sprintf("block (%d levels) in %"PRIsVALUE, level, ip->location.label);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void
 | |
| push_ensure_entry(rb_iseq_t *iseq,
 | |
| 		  struct iseq_compile_data_ensure_node_stack *enl,
 | |
| 		  struct ensure_range *er, NODE *node)
 | |
| {
 | |
|     enl->ensure_node = node;
 | |
|     enl->prev = iseq->compile_data->ensure_node_stack;	/* prev */
 | |
|     enl->erange = er;
 | |
|     iseq->compile_data->ensure_node_stack = enl;
 | |
| }
 | |
| 
 | |
| static void
 | |
| add_ensure_range(rb_iseq_t *iseq, struct ensure_range *erange,
 | |
| 		 LABEL *lstart, LABEL *lend)
 | |
| {
 | |
|     struct ensure_range *ne =
 | |
| 	compile_data_alloc(iseq, sizeof(struct ensure_range));
 | |
| 
 | |
|     while (erange->next != 0) {
 | |
| 	erange = erange->next;
 | |
|     }
 | |
|     ne->next = 0;
 | |
|     ne->begin = lend;
 | |
|     ne->end = erange->end;
 | |
|     erange->end = lstart;
 | |
| 
 | |
|     erange->next = ne;
 | |
| }
 | |
| 
 | |
| static void
 | |
| add_ensure_iseq(LINK_ANCHOR *ret, rb_iseq_t *iseq, int is_return)
 | |
| {
 | |
|     struct iseq_compile_data_ensure_node_stack *enlp =
 | |
| 	iseq->compile_data->ensure_node_stack;
 | |
|     struct iseq_compile_data_ensure_node_stack *prev_enlp = enlp;
 | |
|     DECL_ANCHOR(ensure);
 | |
| 
 | |
|     INIT_ANCHOR(ensure);
 | |
|     while (enlp) {
 | |
| 	if (enlp->erange != 0) {
 | |
| 	    DECL_ANCHOR(ensure_part);
 | |
| 	    LABEL *lstart = NEW_LABEL(0);
 | |
| 	    LABEL *lend = NEW_LABEL(0);
 | |
| 	    INIT_ANCHOR(ensure_part);
 | |
| 
 | |
| 	    add_ensure_range(iseq, enlp->erange, lstart, lend);
 | |
| 
 | |
| 	    iseq->compile_data->ensure_node_stack = enlp->prev;
 | |
| 	    ADD_LABEL(ensure_part, lstart);
 | |
| 	    COMPILE_POPED(ensure_part, "ensure part", enlp->ensure_node);
 | |
| 	    ADD_LABEL(ensure_part, lend);
 | |
| 	    ADD_SEQ(ensure, ensure_part);
 | |
| 	}
 | |
| 	else {
 | |
| 	    if (!is_return) {
 | |
| 		break;
 | |
| 	    }
 | |
| 	}
 | |
| 	enlp = enlp->prev;
 | |
|     }
 | |
|     iseq->compile_data->ensure_node_stack = prev_enlp;
 | |
|     ADD_SEQ(ret, ensure);
 | |
| }
 | |
| 
 | |
| static VALUE
 | |
| setup_args(rb_iseq_t *iseq, LINK_ANCHOR *args, NODE *argn, VALUE *flag)
 | |
| {
 | |
|     VALUE argc = INT2FIX(0);
 | |
|     int nsplat = 0;
 | |
|     DECL_ANCHOR(arg_block);
 | |
|     DECL_ANCHOR(args_splat);
 | |
| 
 | |
|     INIT_ANCHOR(arg_block);
 | |
|     INIT_ANCHOR(args_splat);
 | |
|     if (argn && nd_type(argn) == NODE_BLOCK_PASS) {
 | |
| 	COMPILE(arg_block, "block", argn->nd_body);
 | |
| 	*flag |= VM_CALL_ARGS_BLOCKARG;
 | |
| 	argn = argn->nd_head;
 | |
|     }
 | |
| 
 | |
|   setup_argn:
 | |
|     if (argn) {
 | |
| 	switch (nd_type(argn)) {
 | |
| 	  case NODE_SPLAT: {
 | |
| 	    COMPILE(args, "args (splat)", argn->nd_head);
 | |
| 	    argc = INT2FIX(1);
 | |
| 	    nsplat++;
 | |
| 	    *flag |= VM_CALL_ARGS_SPLAT;
 | |
| 	    break;
 | |
| 	  }
 | |
| 	  case NODE_ARGSCAT:
 | |
| 	  case NODE_ARGSPUSH: {
 | |
| 	    int next_is_array = (nd_type(argn->nd_head) == NODE_ARRAY);
 | |
| 	    DECL_ANCHOR(tmp);
 | |
| 
 | |
| 	    INIT_ANCHOR(tmp);
 | |
| 	    COMPILE(tmp, "args (cat: splat)", argn->nd_body);
 | |
| 	    if (next_is_array && nsplat == 0) {
 | |
| 		/* none */
 | |
| 	    }
 | |
| 	    else {
 | |
| 		if (nd_type(argn) == NODE_ARGSCAT) {
 | |
| 		    ADD_INSN1(tmp, nd_line(argn), splatarray, Qfalse);
 | |
| 		}
 | |
| 		else {
 | |
| 		    ADD_INSN1(tmp, nd_line(argn), newarray, INT2FIX(1));
 | |
| 		}
 | |
| 	    }
 | |
| 	    INSERT_LIST(args_splat, tmp);
 | |
| 	    nsplat++;
 | |
| 	    *flag |= VM_CALL_ARGS_SPLAT;
 | |
| 
 | |
| 	    if (next_is_array) {
 | |
| 		argc = INT2FIX(compile_array(iseq, args, argn->nd_head, COMPILE_ARRAY_TYPE_ARGS) + 1);
 | |
| 	    }
 | |
| 	    else {
 | |
| 		argn = argn->nd_head;
 | |
| 		goto setup_argn;
 | |
| 	    }
 | |
| 	    break;
 | |
| 	  }
 | |
| 	  case NODE_ARRAY: {
 | |
| 	    argc = INT2FIX(compile_array(iseq, args, argn, COMPILE_ARRAY_TYPE_ARGS));
 | |
| 	    break;
 | |
| 	  }
 | |
| 	  default: {
 | |
| 	    rb_bug("setup_arg: unknown node: %s\n", ruby_node_name(nd_type(argn)));
 | |
| 	  }
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     if (nsplat > 1) {
 | |
| 	int i;
 | |
| 	for (i=1; i<nsplat; i++) {
 | |
| 	    ADD_INSN(args_splat, nd_line(args), concatarray);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|     if (!LIST_SIZE_ZERO(args_splat)) {
 | |
| 	ADD_SEQ(args, args_splat);
 | |
|     }
 | |
| 
 | |
|     if (*flag & VM_CALL_ARGS_BLOCKARG) {
 | |
| 	ADD_SEQ(args, arg_block);
 | |
|     }
 | |
|     return argc;
 | |
| }
 | |
| 
 | |
| static VALUE
 | |
| build_postexe_iseq(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *body)
 | |
| {
 | |
|     int line = nd_line(body);
 | |
|     VALUE argc = INT2FIX(0);
 | |
|     VALUE block = NEW_CHILD_ISEQVAL(body, make_name_for_block(iseq->parent_iseq), ISEQ_TYPE_BLOCK, line);
 | |
|     ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | |
|     ADD_CALL_WITH_BLOCK(ret, line, ID2SYM(id_core_set_postexe), argc, block);
 | |
|     iseq_set_local_table(iseq, 0);
 | |
|     return Qnil;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   compile each node
 | |
| 
 | |
|   self:  InstructionSequence
 | |
|   node:  Ruby compiled node
 | |
|   poped: This node will be poped
 | |
|  */
 | |
| static int
 | |
| iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
 | |
| {
 | |
|     enum node_type type;
 | |
|     LINK_ELEMENT *saved_last_element = 0;
 | |
|     int line;
 | |
| 
 | |
|     if (node == 0) {
 | |
| 	if (!poped) {
 | |
| 	    debugs("node: NODE_NIL(implicit)\n");
 | |
| 	    ADD_INSN(ret, iseq->compile_data->last_line, putnil);
 | |
| 	}
 | |
| 	return COMPILE_OK;
 | |
|     }
 | |
| 
 | |
|     iseq->compile_data->last_line = line = (int)nd_line(node);
 | |
|     debug_node_start(node);
 | |
| 
 | |
|     type = nd_type(node);
 | |
| 
 | |
|     if (node->flags & NODE_FL_NEWLINE) {
 | |
| 	ADD_TRACE(ret, line, RUBY_EVENT_LINE);
 | |
| 	saved_last_element = ret->last;
 | |
|     }
 | |
| 
 | |
|     switch (type) {
 | |
|       case NODE_BLOCK:{
 | |
| 	while (node && nd_type(node) == NODE_BLOCK) {
 | |
| 	    COMPILE_(ret, "BLOCK body", node->nd_head,
 | |
| 		     (node->nd_next == 0 && poped == 0) ? 0 : 1);
 | |
| 	    node = node->nd_next;
 | |
| 	}
 | |
| 	if (node) {
 | |
| 	    COMPILE_(ret, "BLOCK next", node->nd_next, poped);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_IF:{
 | |
| 	DECL_ANCHOR(cond_seq);
 | |
| 	DECL_ANCHOR(then_seq);
 | |
| 	DECL_ANCHOR(else_seq);
 | |
| 	LABEL *then_label, *else_label, *end_label;
 | |
| 
 | |
| 	INIT_ANCHOR(cond_seq);
 | |
| 	INIT_ANCHOR(then_seq);
 | |
| 	INIT_ANCHOR(else_seq);
 | |
| 	then_label = NEW_LABEL(line);
 | |
| 	else_label = NEW_LABEL(line);
 | |
| 	end_label = NEW_LABEL(line);
 | |
| 
 | |
| 	compile_branch_condition(iseq, cond_seq, node->nd_cond,
 | |
| 				 then_label, else_label);
 | |
| 	COMPILE_(then_seq, "then", node->nd_body, poped);
 | |
| 	COMPILE_(else_seq, "else", node->nd_else, poped);
 | |
| 
 | |
| 	ADD_SEQ(ret, cond_seq);
 | |
| 
 | |
| 	ADD_LABEL(ret, then_label);
 | |
| 	ADD_SEQ(ret, then_seq);
 | |
| 	ADD_INSNL(ret, line, jump, end_label);
 | |
| 
 | |
| 	ADD_LABEL(ret, else_label);
 | |
| 	ADD_SEQ(ret, else_seq);
 | |
| 
 | |
| 	ADD_LABEL(ret, end_label);
 | |
| 
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_CASE:{
 | |
| 	NODE *vals;
 | |
| 	NODE *tempnode = node;
 | |
| 	LABEL *endlabel, *elselabel;
 | |
| 	DECL_ANCHOR(head);
 | |
| 	DECL_ANCHOR(body_seq);
 | |
| 	DECL_ANCHOR(cond_seq);
 | |
| 	int only_special_literals = 1;
 | |
| 	VALUE literals = rb_hash_new();
 | |
| 
 | |
| 	INIT_ANCHOR(head);
 | |
| 	INIT_ANCHOR(body_seq);
 | |
| 	INIT_ANCHOR(cond_seq);
 | |
| 
 | |
| 	rb_hash_tbl_raw(literals)->type = &cdhash_type;
 | |
| 
 | |
| 	if (node->nd_head == 0) {
 | |
| 	    COMPILE_(ret, "when", node->nd_body, poped);
 | |
| 	    break;
 | |
| 	}
 | |
| 	COMPILE(head, "case base", node->nd_head);
 | |
| 
 | |
| 	node = node->nd_body;
 | |
| 	type = nd_type(node);
 | |
| 	line = nd_line(node);
 | |
| 
 | |
| 	if (type != NODE_WHEN) {
 | |
| 	    COMPILE_ERROR((ERROR_ARGS "NODE_CASE: unexpected node. must be NODE_WHEN, but %s", ruby_node_name(type)));
 | |
| 	}
 | |
| 
 | |
| 	endlabel = NEW_LABEL(line);
 | |
| 	elselabel = NEW_LABEL(line);
 | |
| 
 | |
| 	ADD_SEQ(ret, head);	/* case VAL */
 | |
| 
 | |
| 	while (type == NODE_WHEN) {
 | |
| 	    LABEL *l1;
 | |
| 
 | |
| 	    l1 = NEW_LABEL(line);
 | |
| 	    ADD_LABEL(body_seq, l1);
 | |
| 	    ADD_INSN(body_seq, line, pop);
 | |
| 	    COMPILE_(body_seq, "when body", node->nd_body, poped);
 | |
| 	    ADD_INSNL(body_seq, line, jump, endlabel);
 | |
| 
 | |
| 	    vals = node->nd_head;
 | |
| 	    if (vals) {
 | |
| 		switch (nd_type(vals)) {
 | |
| 		  case NODE_ARRAY:
 | |
| 		    only_special_literals = when_vals(iseq, cond_seq, vals, l1, only_special_literals, literals);
 | |
| 		    break;
 | |
| 		  case NODE_SPLAT:
 | |
| 		  case NODE_ARGSCAT:
 | |
| 		  case NODE_ARGSPUSH:
 | |
| 		    only_special_literals = 0;
 | |
| 		    ADD_INSN (cond_seq, nd_line(vals), dup);
 | |
| 		    COMPILE(cond_seq, "when/cond splat", vals);
 | |
| 		    ADD_INSN1(cond_seq, nd_line(vals), checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_CASE | VM_CHECKMATCH_ARRAY));
 | |
| 		    ADD_INSNL(cond_seq, nd_line(vals), branchif, l1);
 | |
| 		    break;
 | |
| 		  default:
 | |
| 		    rb_bug("NODE_CASE: unknown node (%s)",
 | |
| 			   ruby_node_name(nd_type(vals)));
 | |
| 		}
 | |
| 	    }
 | |
| 	    else {
 | |
| 		rb_bug("NODE_CASE: must be NODE_ARRAY, but 0");
 | |
| 	    }
 | |
| 
 | |
| 	    node = node->nd_next;
 | |
| 	    if (!node) {
 | |
| 		break;
 | |
| 	    }
 | |
| 	    type = nd_type(node);
 | |
| 	    line = nd_line(node);
 | |
| 	}
 | |
| 	/* else */
 | |
| 	if (node) {
 | |
| 	    ADD_LABEL(cond_seq, elselabel);
 | |
| 	    ADD_INSN(cond_seq, line, pop);
 | |
| 	    COMPILE_(cond_seq, "else", node, poped);
 | |
| 	    ADD_INSNL(cond_seq, line, jump, endlabel);
 | |
| 	}
 | |
| 	else {
 | |
| 	    debugs("== else (implicit)\n");
 | |
| 	    ADD_LABEL(cond_seq, elselabel);
 | |
| 	    ADD_INSN(cond_seq, nd_line(tempnode), pop);
 | |
| 	    if (!poped) {
 | |
| 		ADD_INSN(cond_seq, nd_line(tempnode), putnil);
 | |
| 	    }
 | |
| 	    ADD_INSNL(cond_seq, nd_line(tempnode), jump, endlabel);
 | |
| 	}
 | |
| 
 | |
| 	if (only_special_literals) {
 | |
| 	    iseq_add_mark_object(iseq, literals);
 | |
| 
 | |
| 	    ADD_INSN(ret, nd_line(tempnode), dup);
 | |
| 	    ADD_INSN2(ret, nd_line(tempnode), opt_case_dispatch, literals, elselabel);
 | |
| 	}
 | |
| 
 | |
| 	ADD_SEQ(ret, cond_seq);
 | |
| 	ADD_SEQ(ret, body_seq);
 | |
| 	ADD_LABEL(ret, endlabel);
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_WHEN:{
 | |
| 	NODE *vals;
 | |
| 	NODE *val;
 | |
| 	NODE *orig_node = node;
 | |
| 	LABEL *endlabel;
 | |
| 	DECL_ANCHOR(body_seq);
 | |
| 
 | |
| 	INIT_ANCHOR(body_seq);
 | |
| 	endlabel = NEW_LABEL(line);
 | |
| 
 | |
| 	while (node && nd_type(node) == NODE_WHEN) {
 | |
| 	    LABEL *l1 = NEW_LABEL(line = nd_line(node));
 | |
| 	    ADD_LABEL(body_seq, l1);
 | |
| 	    COMPILE_(body_seq, "when", node->nd_body, poped);
 | |
| 	    ADD_INSNL(body_seq, line, jump, endlabel);
 | |
| 
 | |
| 	    vals = node->nd_head;
 | |
| 	    if (!vals) {
 | |
| 		rb_bug("NODE_WHEN: must be NODE_ARRAY, but 0");
 | |
| 	    }
 | |
| 	    switch (nd_type(vals)) {
 | |
| 	      case NODE_ARRAY:
 | |
| 		while (vals) {
 | |
| 		    val = vals->nd_head;
 | |
| 		    COMPILE(ret, "when2", val);
 | |
| 		    ADD_INSNL(ret, nd_line(val), branchif, l1);
 | |
| 		    vals = vals->nd_next;
 | |
| 		}
 | |
| 		break;
 | |
| 	      case NODE_SPLAT:
 | |
| 	      case NODE_ARGSCAT:
 | |
| 	      case NODE_ARGSPUSH:
 | |
| 		ADD_INSN(ret, nd_line(vals), putnil);
 | |
| 		COMPILE(ret, "when2/cond splat", vals);
 | |
| 		ADD_INSN1(ret, nd_line(vals), checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_WHEN | VM_CHECKMATCH_ARRAY));
 | |
| 		ADD_INSNL(ret, nd_line(vals), branchif, l1);
 | |
| 		break;
 | |
| 	      default:
 | |
| 		rb_bug("NODE_WHEN: unknown node (%s)",
 | |
| 		       ruby_node_name(nd_type(vals)));
 | |
| 	    }
 | |
| 	    node = node->nd_next;
 | |
| 	}
 | |
| 	/* else */
 | |
| 	COMPILE_(ret, "else", node, poped);
 | |
| 	ADD_INSNL(ret, nd_line(orig_node), jump, endlabel);
 | |
| 
 | |
| 	ADD_SEQ(ret, body_seq);
 | |
| 	ADD_LABEL(ret, endlabel);
 | |
| 
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_OPT_N:
 | |
|       case NODE_WHILE:
 | |
|       case NODE_UNTIL:{
 | |
| 	LABEL *prev_start_label = iseq->compile_data->start_label;
 | |
| 	LABEL *prev_end_label = iseq->compile_data->end_label;
 | |
| 	LABEL *prev_redo_label = iseq->compile_data->redo_label;
 | |
| 	int prev_loopval_popped = iseq->compile_data->loopval_popped;
 | |
| 
 | |
| 	struct iseq_compile_data_ensure_node_stack enl;
 | |
| 
 | |
| 	LABEL *next_label = iseq->compile_data->start_label = NEW_LABEL(line);	/* next  */
 | |
| 	LABEL *redo_label = iseq->compile_data->redo_label = NEW_LABEL(line);	/* redo  */
 | |
| 	LABEL *break_label = iseq->compile_data->end_label = NEW_LABEL(line);	/* break */
 | |
| 	LABEL *end_label = NEW_LABEL(line);
 | |
| 
 | |
| 	LABEL *next_catch_label = NEW_LABEL(line);
 | |
| 	LABEL *tmp_label = NULL;
 | |
| 
 | |
| 	iseq->compile_data->loopval_popped = 0;
 | |
| 	push_ensure_entry(iseq, &enl, 0, 0);
 | |
| 
 | |
| 	if (type == NODE_OPT_N || node->nd_state == 1) {
 | |
| 	    ADD_INSNL(ret, line, jump, next_label);
 | |
| 	}
 | |
| 	else {
 | |
| 	    tmp_label = NEW_LABEL(line);
 | |
| 	    ADD_INSNL(ret, line, jump, tmp_label);
 | |
| 	}
 | |
| 	ADD_INSN(ret, line, putnil);
 | |
| 	ADD_LABEL(ret, next_catch_label);
 | |
| 	ADD_INSN(ret, line, pop);
 | |
| 	ADD_INSNL(ret, line, jump, next_label);
 | |
| 	if (tmp_label) ADD_LABEL(ret, tmp_label);
 | |
| 
 | |
| 	ADD_LABEL(ret, redo_label);
 | |
| 	COMPILE_POPED(ret, "while body", node->nd_body);
 | |
| 	ADD_LABEL(ret, next_label);	/* next */
 | |
| 
 | |
| 	if (type == NODE_WHILE) {
 | |
| 	    compile_branch_condition(iseq, ret, node->nd_cond,
 | |
| 				     redo_label, end_label);
 | |
| 	}
 | |
| 	else if (type == NODE_UNTIL) {
 | |
| 	    /* until */
 | |
| 	    compile_branch_condition(iseq, ret, node->nd_cond,
 | |
| 				     end_label, redo_label);
 | |
| 	}
 | |
| 	else {
 | |
| 	    ADD_CALL_RECEIVER(ret, line);
 | |
| 	    ADD_CALL(ret, line, ID2SYM(idGets), INT2FIX(0));
 | |
| 	    ADD_INSNL(ret, line, branchif, redo_label);
 | |
| 	    /* opt_n */
 | |
| 	}
 | |
| 
 | |
| 	ADD_LABEL(ret, end_label);
 | |
| 
 | |
| 	if (node->nd_state == Qundef) {
 | |
| 	    /* ADD_INSN(ret, line, putundef); */
 | |
| 	    rb_bug("unsupported: putundef");
 | |
| 	}
 | |
| 	else {
 | |
| 	    ADD_INSN(ret, line, putnil);
 | |
| 	}
 | |
| 
 | |
| 	ADD_LABEL(ret, break_label);	/* break */
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 
 | |
| 	ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label,
 | |
| 			0, break_label);
 | |
| 	ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, 0,
 | |
| 			next_catch_label);
 | |
| 	ADD_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, 0,
 | |
| 			iseq->compile_data->redo_label);
 | |
| 
 | |
| 	iseq->compile_data->start_label = prev_start_label;
 | |
| 	iseq->compile_data->end_label = prev_end_label;
 | |
| 	iseq->compile_data->redo_label = prev_redo_label;
 | |
| 	iseq->compile_data->loopval_popped = prev_loopval_popped;
 | |
| 	iseq->compile_data->ensure_node_stack = iseq->compile_data->ensure_node_stack->prev;
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_ITER:
 | |
|       case NODE_FOR:{
 | |
| 	VALUE prevblock = iseq->compile_data->current_block;
 | |
| 	LABEL *retry_label = NEW_LABEL(line);
 | |
| 	LABEL *retry_end_l = NEW_LABEL(line);
 | |
| 
 | |
| 	ADD_LABEL(ret, retry_label);
 | |
| 	if (nd_type(node) == NODE_FOR) {
 | |
| 	    COMPILE(ret, "iter caller (for)", node->nd_iter);
 | |
| 
 | |
| 	    iseq->compile_data->current_block =
 | |
| 		NEW_CHILD_ISEQVAL(node->nd_body, make_name_for_block(iseq),
 | |
| 				  ISEQ_TYPE_BLOCK, line);
 | |
| 
 | |
| 	    ADD_SEND_R(ret, line, ID2SYM(idEach), INT2FIX(0),
 | |
| 		       iseq->compile_data->current_block, INT2FIX(0));
 | |
| 	}
 | |
| 	else {
 | |
| 	    iseq->compile_data->current_block =
 | |
| 		NEW_CHILD_ISEQVAL(node->nd_body, make_name_for_block(iseq),
 | |
| 				  ISEQ_TYPE_BLOCK, line);
 | |
| 	    COMPILE(ret, "iter caller", node->nd_iter);
 | |
| 	}
 | |
| 	ADD_LABEL(ret, retry_end_l);
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 
 | |
| 	iseq->compile_data->current_block = prevblock;
 | |
| 
 | |
| 	ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, 0, retry_end_l);
 | |
| 
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_BREAK:{
 | |
| 	unsigned long level = 0;
 | |
| 
 | |
| 	if (iseq->compile_data->redo_label != 0) {
 | |
| 	    /* while/until */
 | |
| 	    LABEL *splabel = NEW_LABEL(0);
 | |
| 	    ADD_LABEL(ret, splabel);
 | |
| 	    ADD_ADJUST(ret, line, iseq->compile_data->redo_label);
 | |
| 	    COMPILE_(ret, "break val (while/until)", node->nd_stts, iseq->compile_data->loopval_popped);
 | |
| 	    add_ensure_iseq(ret, iseq, 0);
 | |
| 	    ADD_INSNL(ret, line, jump, iseq->compile_data->end_label);
 | |
| 	    ADD_ADJUST_RESTORE(ret, splabel);
 | |
| 
 | |
| 	    if (!poped) {
 | |
| 		ADD_INSN(ret, line, putnil);
 | |
| 	    }
 | |
| 	}
 | |
| 	else if (iseq->type == ISEQ_TYPE_BLOCK) {
 | |
| 	  break_by_insn:
 | |
| 	    /* escape from block */
 | |
| 	    COMPILE(ret, "break val (block)", node->nd_stts);
 | |
| 	    ADD_INSN1(ret, line, throw, INT2FIX(level | 0x02) /* TAG_BREAK */ );
 | |
| 	    if (poped) {
 | |
| 		ADD_INSN(ret, line, pop);
 | |
| 	    }
 | |
| 	}
 | |
| 	else if (iseq->type == ISEQ_TYPE_EVAL) {
 | |
| 	  break_in_eval:
 | |
| 	    COMPILE_ERROR((ERROR_ARGS "Can't escape from eval with break"));
 | |
| 	}
 | |
| 	else {
 | |
| 	    rb_iseq_t *ip = iseq->parent_iseq;
 | |
| 	    while (ip) {
 | |
| 		if (!ip->compile_data) {
 | |
| 		    ip = 0;
 | |
| 		    break;
 | |
| 		}
 | |
| 
 | |
| 		level++;
 | |
| 		if (ip->compile_data->redo_label != 0) {
 | |
| 		    level = 0x8000;
 | |
| 		    if (ip->compile_data->loopval_popped == 0) {
 | |
| 			/* need value */
 | |
| 			level |= 0x4000;
 | |
| 		    }
 | |
| 		    goto break_by_insn;
 | |
| 		}
 | |
| 		else if (ip->type == ISEQ_TYPE_BLOCK) {
 | |
| 		    level <<= 16;
 | |
| 		    goto break_by_insn;
 | |
| 		}
 | |
| 		else if (ip->type == ISEQ_TYPE_EVAL) {
 | |
| 		    goto break_in_eval;
 | |
| 		}
 | |
| 
 | |
| 		ip = ip->parent_iseq;
 | |
| 	    }
 | |
| 	    COMPILE_ERROR((ERROR_ARGS "Invalid break"));
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_NEXT:{
 | |
| 	unsigned long level = 0;
 | |
| 
 | |
| 	if (iseq->compile_data->redo_label != 0) {
 | |
| 	    LABEL *splabel = NEW_LABEL(0);
 | |
| 	    debugs("next in while loop\n");
 | |
| 	    ADD_LABEL(ret, splabel);
 | |
| 	    COMPILE(ret, "next val/valid syntax?", node->nd_stts);
 | |
| 	    add_ensure_iseq(ret, iseq, 0);
 | |
| 	    ADD_ADJUST(ret, line, iseq->compile_data->redo_label);
 | |
| 	    ADD_INSNL(ret, line, jump, iseq->compile_data->start_label);
 | |
| 	    ADD_ADJUST_RESTORE(ret, splabel);
 | |
| 	    if (!poped) {
 | |
| 		ADD_INSN(ret, line, putnil);
 | |
| 	    }
 | |
| 	}
 | |
| 	else if (iseq->compile_data->end_label) {
 | |
| 	    LABEL *splabel = NEW_LABEL(0);
 | |
| 	    debugs("next in block\n");
 | |
| 	    ADD_LABEL(ret, splabel);
 | |
| 	    ADD_ADJUST(ret, line, iseq->compile_data->start_label);
 | |
| 	    COMPILE(ret, "next val", node->nd_stts);
 | |
| 	    add_ensure_iseq(ret, iseq, 0);
 | |
| 	    ADD_INSNL(ret, line, jump, iseq->compile_data->end_label);
 | |
| 	    ADD_ADJUST_RESTORE(ret, splabel);
 | |
| 
 | |
| 	    if (!poped) {
 | |
| 		ADD_INSN(ret, line, putnil);
 | |
| 	    }
 | |
| 	}
 | |
| 	else if (iseq->type == ISEQ_TYPE_EVAL) {
 | |
| 	  next_in_eval:
 | |
| 	    COMPILE_ERROR((ERROR_ARGS "Can't escape from eval with next"));
 | |
| 	}
 | |
| 	else {
 | |
| 	    rb_iseq_t *ip;
 | |
| 	    ip = iseq;
 | |
| 	    while (ip) {
 | |
| 		if (!ip->compile_data) {
 | |
| 		    ip = 0;
 | |
| 		    break;
 | |
| 		}
 | |
| 
 | |
| 		level = 0x8000 | 0x4000;
 | |
| 		if (ip->compile_data->redo_label != 0) {
 | |
| 		    /* while loop */
 | |
| 		    break;
 | |
| 		}
 | |
| 		else if (ip->type == ISEQ_TYPE_BLOCK) {
 | |
| 		    break;
 | |
| 		}
 | |
| 		else if (ip->type == ISEQ_TYPE_EVAL) {
 | |
| 		    goto next_in_eval;
 | |
| 		}
 | |
| 
 | |
| 		ip = ip->parent_iseq;
 | |
| 	    }
 | |
| 	    if (ip != 0) {
 | |
| 		COMPILE(ret, "next val", node->nd_stts);
 | |
| 		ADD_INSN1(ret, line, throw, INT2FIX(level | 0x03) /* TAG_NEXT */ );
 | |
| 
 | |
| 		if (poped) {
 | |
| 		    ADD_INSN(ret, line, pop);
 | |
| 		}
 | |
| 	    }
 | |
| 	    else {
 | |
| 		COMPILE_ERROR((ERROR_ARGS "Invalid next"));
 | |
| 	    }
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_REDO:{
 | |
| 	if (iseq->compile_data->redo_label) {
 | |
| 	    LABEL *splabel = NEW_LABEL(0);
 | |
| 	    debugs("redo in while");
 | |
| 	    ADD_LABEL(ret, splabel);
 | |
| 	    ADD_ADJUST(ret, line, iseq->compile_data->redo_label);
 | |
| 	    add_ensure_iseq(ret, iseq, 0);
 | |
| 	    ADD_INSNL(ret, line, jump, iseq->compile_data->redo_label);
 | |
| 	    ADD_ADJUST_RESTORE(ret, splabel);
 | |
| 	    if (!poped) {
 | |
| 		ADD_INSN(ret, line, putnil);
 | |
| 	    }
 | |
| 	}
 | |
| 	else if (iseq->type == ISEQ_TYPE_EVAL) {
 | |
| 	  redo_in_eval:
 | |
| 	    COMPILE_ERROR((ERROR_ARGS "Can't escape from eval with redo"));
 | |
| 	}
 | |
| 	else if (iseq->compile_data->start_label) {
 | |
| 	    LABEL *splabel = NEW_LABEL(0);
 | |
| 
 | |
| 	    debugs("redo in block");
 | |
| 	    ADD_LABEL(ret, splabel);
 | |
| 	    add_ensure_iseq(ret, iseq, 0);
 | |
| 	    ADD_ADJUST(ret, line, iseq->compile_data->start_label);
 | |
| 	    ADD_INSNL(ret, line, jump, iseq->compile_data->start_label);
 | |
| 	    ADD_ADJUST_RESTORE(ret, splabel);
 | |
| 
 | |
| 	    if (!poped) {
 | |
| 		ADD_INSN(ret, line, putnil);
 | |
| 	    }
 | |
| 	}
 | |
| 	else {
 | |
| 	    rb_iseq_t *ip;
 | |
| 	    unsigned long level;
 | |
| 	    level = 0x8000 | 0x4000;
 | |
| 	    ip = iseq;
 | |
| 	    while (ip) {
 | |
| 		if (!ip->compile_data) {
 | |
| 		    ip = 0;
 | |
| 		    break;
 | |
| 		}
 | |
| 
 | |
| 		if (ip->compile_data->redo_label != 0) {
 | |
| 		    break;
 | |
| 		}
 | |
| 		else if (ip->type == ISEQ_TYPE_BLOCK) {
 | |
| 		    break;
 | |
| 		}
 | |
| 		else if (ip->type == ISEQ_TYPE_EVAL) {
 | |
| 		    goto redo_in_eval;
 | |
| 		}
 | |
| 
 | |
| 		ip = ip->parent_iseq;
 | |
| 	    }
 | |
| 	    if (ip != 0) {
 | |
| 		ADD_INSN(ret, line, putnil);
 | |
| 		ADD_INSN1(ret, line, throw, INT2FIX(level | 0x05) /* TAG_REDO */ );
 | |
| 
 | |
| 		if (poped) {
 | |
| 		    ADD_INSN(ret, line, pop);
 | |
| 		}
 | |
| 	    }
 | |
| 	    else {
 | |
| 		COMPILE_ERROR((ERROR_ARGS "Invalid redo"));
 | |
| 	    }
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_RETRY:{
 | |
| 	if (iseq->type == ISEQ_TYPE_RESCUE) {
 | |
| 	    ADD_INSN(ret, line, putnil);
 | |
| 	    ADD_INSN1(ret, line, throw, INT2FIX(0x04) /* TAG_RETRY */ );
 | |
| 
 | |
| 	    if (poped) {
 | |
| 		ADD_INSN(ret, line, pop);
 | |
| 	    }
 | |
| 	}
 | |
| 	else {
 | |
| 	    COMPILE_ERROR((ERROR_ARGS "Invalid retry"));
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_BEGIN:{
 | |
| 	COMPILE_(ret, "NODE_BEGIN", node->nd_body, poped);
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_RESCUE:{
 | |
| 	LABEL *lstart = NEW_LABEL(line);
 | |
| 	LABEL *lend = NEW_LABEL(line);
 | |
| 	LABEL *lcont = NEW_LABEL(line);
 | |
| 	VALUE rescue = NEW_CHILD_ISEQVAL(
 | |
| 	    node->nd_resq,
 | |
| 	    rb_str_concat(rb_str_new2("rescue in "), iseq->location.label),
 | |
| 	    ISEQ_TYPE_RESCUE, line);
 | |
| 
 | |
| 	ADD_LABEL(ret, lstart);
 | |
| 	COMPILE(ret, "rescue head", node->nd_head);
 | |
| 	ADD_LABEL(ret, lend);
 | |
| 	if (node->nd_else) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	    COMPILE(ret, "rescue else", node->nd_else);
 | |
| 	}
 | |
| 	ADD_INSN(ret, line, nop);
 | |
| 	ADD_LABEL(ret, lcont);
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 
 | |
| 	/* register catch entry */
 | |
| 	ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lcont);
 | |
| 	ADD_CATCH_ENTRY(CATCH_TYPE_RETRY, lend, lcont, 0, lstart);
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_RESBODY:{
 | |
| 	NODE *resq = node;
 | |
| 	NODE *narg;
 | |
| 	LABEL *label_miss, *label_hit;
 | |
| 
 | |
| 	while (resq) {
 | |
| 	    label_miss = NEW_LABEL(line);
 | |
| 	    label_hit = NEW_LABEL(line);
 | |
| 
 | |
| 	    narg = resq->nd_args;
 | |
| 	    if (narg) {
 | |
| 		switch (nd_type(narg)) {
 | |
| 		  case NODE_ARRAY:
 | |
| 		    while (narg) {
 | |
| 			ADD_INSN2(ret, line, getlocal, INT2FIX(2), INT2FIX(0));
 | |
| 			COMPILE(ret, "rescue arg", narg->nd_head);
 | |
| 			ADD_INSN1(ret, line, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
 | |
| 			ADD_INSNL(ret, line, branchif, label_hit);
 | |
| 			narg = narg->nd_next;
 | |
| 		    }
 | |
| 		    break;
 | |
| 		  case NODE_SPLAT:
 | |
| 		  case NODE_ARGSCAT:
 | |
| 		  case NODE_ARGSPUSH:
 | |
| 		    ADD_INSN2(ret, line, getlocal, INT2FIX(2), INT2FIX(0));
 | |
| 		    COMPILE(ret, "rescue/cond splat", narg);
 | |
| 		    ADD_INSN1(ret, line, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE | VM_CHECKMATCH_ARRAY));
 | |
| 		    ADD_INSNL(ret, line, branchif, label_hit);
 | |
| 		    break;
 | |
| 		  default:
 | |
| 		    rb_bug("NODE_RESBODY: unknown node (%s)",
 | |
| 			   ruby_node_name(nd_type(narg)));
 | |
| 		}
 | |
| 	    }
 | |
| 	    else {
 | |
| 		ADD_INSN2(ret, line, getlocal, INT2FIX(2), INT2FIX(0));
 | |
| 		ADD_INSN1(ret, line, putobject, rb_eStandardError);
 | |
| 		ADD_INSN1(ret, line, checkmatch, INT2FIX(VM_CHECKMATCH_TYPE_RESCUE));
 | |
| 		ADD_INSNL(ret, line, branchif, label_hit);
 | |
| 	    }
 | |
| 	    ADD_INSNL(ret, line, jump, label_miss);
 | |
| 	    ADD_LABEL(ret, label_hit);
 | |
| 	    COMPILE(ret, "resbody body", resq->nd_body);
 | |
| 	    if (iseq->compile_data->option->tailcall_optimization) {
 | |
| 		ADD_INSN(ret, line, nop);
 | |
| 	    }
 | |
| 	    ADD_INSN(ret, line, leave);
 | |
| 	    ADD_LABEL(ret, label_miss);
 | |
| 	    resq = resq->nd_head;
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_ENSURE:{
 | |
| 	DECL_ANCHOR(ensr);
 | |
| 	VALUE ensure = NEW_CHILD_ISEQVAL(node->nd_ensr,
 | |
| 					 rb_str_concat(rb_str_new2
 | |
| 						       ("ensure in "),
 | |
| 						       iseq->location.label),
 | |
| 					 ISEQ_TYPE_ENSURE, line);
 | |
| 	LABEL *lstart = NEW_LABEL(line);
 | |
| 	LABEL *lend = NEW_LABEL(line);
 | |
| 	LABEL *lcont = NEW_LABEL(line);
 | |
| 	struct ensure_range er;
 | |
| 	struct iseq_compile_data_ensure_node_stack enl;
 | |
| 	struct ensure_range *erange;
 | |
| 
 | |
| 	INIT_ANCHOR(ensr);
 | |
| 	COMPILE_POPED(ensr, "ensure ensr", node->nd_ensr);
 | |
| 
 | |
| 	er.begin = lstart;
 | |
| 	er.end = lend;
 | |
| 	er.next = 0;
 | |
| 	push_ensure_entry(iseq, &enl, &er, node->nd_ensr);
 | |
| 
 | |
| 	ADD_LABEL(ret, lstart);
 | |
| 	COMPILE_(ret, "ensure head", node->nd_head, poped);
 | |
| 	ADD_LABEL(ret, lend);
 | |
| 	if (ensr->anchor.next == 0) {
 | |
| 	    ADD_INSN(ret, line, nop);
 | |
| 	}
 | |
| 	else {
 | |
| 	    ADD_SEQ(ret, ensr);
 | |
| 	}
 | |
| 	ADD_LABEL(ret, lcont);
 | |
| 
 | |
| 	erange = iseq->compile_data->ensure_node_stack->erange;
 | |
| 	while (erange) {
 | |
| 	    ADD_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end,
 | |
| 			    ensure, lcont);
 | |
| 	    erange = erange->next;
 | |
| 	}
 | |
| 
 | |
| 	iseq->compile_data->ensure_node_stack = enl.prev;
 | |
| 	break;
 | |
|       }
 | |
| 
 | |
|       case NODE_AND:
 | |
|       case NODE_OR:{
 | |
| 	LABEL *end_label = NEW_LABEL(line);
 | |
| 	COMPILE(ret, "nd_1st", node->nd_1st);
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN(ret, line, dup);
 | |
| 	}
 | |
| 	if (type == NODE_AND) {
 | |
| 	    ADD_INSNL(ret, line, branchunless, end_label);
 | |
| 	}
 | |
| 	else {
 | |
| 	    ADD_INSNL(ret, line, branchif, end_label);
 | |
| 	}
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	COMPILE_(ret, "nd_2nd", node->nd_2nd, poped);
 | |
| 	ADD_LABEL(ret, end_label);
 | |
| 	break;
 | |
|       }
 | |
| 
 | |
|       case NODE_MASGN:{
 | |
| 	compile_massign(iseq, ret, node, poped);
 | |
| 	break;
 | |
|       }
 | |
| 
 | |
|       case NODE_LASGN:{
 | |
| 	ID id = node->nd_vid;
 | |
| 	int idx = iseq->local_iseq->local_size - get_local_var_idx(iseq, id);
 | |
| 
 | |
| 	debugs("lvar: %s idx: %d\n", rb_id2name(id), idx);
 | |
| 	COMPILE(ret, "rvalue", node->nd_value);
 | |
| 
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN(ret, line, dup);
 | |
| 	}
 | |
| 	ADD_INSN2(ret, line, setlocal, INT2FIX(idx), INT2FIX(get_lvar_level(iseq)));
 | |
| 
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_DASGN:
 | |
|       case NODE_DASGN_CURR:{
 | |
| 	int idx, lv, ls;
 | |
| 	COMPILE(ret, "dvalue", node->nd_value);
 | |
| 	debugp_param("dassn id", rb_str_new2(rb_id2name(node->nd_vid) ? rb_id2name(node->nd_vid) : "*"));
 | |
| 
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN(ret, line, dup);
 | |
| 	}
 | |
| 
 | |
| 	idx = get_dyna_var_idx(iseq, node->nd_vid, &lv, &ls);
 | |
| 
 | |
| 	if (idx < 0) {
 | |
| 	    rb_bug("NODE_DASGN(_CURR): unknown id (%s)", rb_id2name(node->nd_vid));
 | |
| 	}
 | |
| 
 | |
| 	ADD_INSN2(ret, line, setlocal, INT2FIX(ls - idx), INT2FIX(lv));
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_GASGN:{
 | |
| 	COMPILE(ret, "lvalue", node->nd_value);
 | |
| 
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN(ret, line, dup);
 | |
| 	}
 | |
| 	ADD_INSN1(ret, line, setglobal,
 | |
| 		  ((VALUE)node->nd_entry | 1));
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_IASGN:
 | |
|       case NODE_IASGN2:{
 | |
| 	COMPILE(ret, "lvalue", node->nd_value);
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN(ret, line, dup);
 | |
| 	}
 | |
| 	ADD_INSN2(ret, line, setinstancevariable,
 | |
| 		  ID2SYM(node->nd_vid), INT2FIX(iseq->is_size++));
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_CDECL:{
 | |
| 	COMPILE(ret, "lvalue", node->nd_value);
 | |
| 
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN(ret, line, dup);
 | |
| 	}
 | |
| 
 | |
| 	if (node->nd_vid) {
 | |
| 	    ADD_INSN1(ret, line, putspecialobject,
 | |
| 		      INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
 | |
| 	    ADD_INSN1(ret, line, setconstant, ID2SYM(node->nd_vid));
 | |
| 	}
 | |
| 	else {
 | |
| 	    compile_cpath(ret, iseq, node->nd_else);
 | |
| 	    ADD_INSN1(ret, line, setconstant, ID2SYM(node->nd_else->nd_mid));
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_CVASGN:{
 | |
| 	COMPILE(ret, "cvasgn val", node->nd_value);
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN(ret, line, dup);
 | |
| 	}
 | |
| 	ADD_INSN1(ret, line, setclassvariable,
 | |
| 		  ID2SYM(node->nd_vid));
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_OP_ASGN1: {
 | |
| 	DECL_ANCHOR(args);
 | |
| 	VALUE argc;
 | |
| 	VALUE flag = 0;
 | |
| 	ID id = node->nd_mid;
 | |
| 	int boff = 0;
 | |
| 
 | |
| 	/*
 | |
| 	 * a[x] (op)= y
 | |
| 	 *
 | |
| 	 * nil       # nil
 | |
| 	 * eval a    # nil a
 | |
| 	 * eval x    # nil a x
 | |
| 	 * dupn 2    # nil a x a x
 | |
| 	 * send :[]  # nil a x a[x]
 | |
| 	 * eval y    # nil a x a[x] y
 | |
| 	 * send op   # nil a x ret
 | |
| 	 * setn 3    # ret a x ret
 | |
| 	 * send []=  # ret ?
 | |
| 	 * pop       # ret
 | |
| 	 */
 | |
| 
 | |
| 	/*
 | |
| 	 * nd_recv[nd_args->nd_body] (nd_mid)= nd_args->nd_head;
 | |
| 	 * NODE_OP_ASGN nd_recv
 | |
| 	 *              nd_args->nd_head
 | |
| 	 *              nd_args->nd_body
 | |
| 	 *              nd_mid
 | |
| 	 */
 | |
| 
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN(ret, line, putnil);
 | |
| 	}
 | |
| 	COMPILE(ret, "NODE_OP_ASGN1 recv", node->nd_recv);
 | |
| 	switch (nd_type(node->nd_args->nd_head)) {
 | |
| 	  case NODE_ZARRAY:
 | |
| 	    argc = INT2FIX(0);
 | |
| 	    break;
 | |
| 	  case NODE_BLOCK_PASS:
 | |
| 	    boff = 1;
 | |
| 	  default:
 | |
| 	    INIT_ANCHOR(args);
 | |
| 	    argc = setup_args(iseq, args, node->nd_args->nd_head, &flag);
 | |
| 	    ADD_SEQ(ret, args);
 | |
| 	}
 | |
| 	ADD_INSN1(ret, line, dupn, FIXNUM_INC(argc, 1 + boff));
 | |
| 	ADD_SEND_R(ret, line, ID2SYM(idAREF), argc, Qfalse, LONG2FIX(flag));
 | |
| 
 | |
| 	if (id == 0 || id == 1) {
 | |
| 	    /* 0: or, 1: and
 | |
| 	       a[x] ||= y
 | |
| 
 | |
| 	       unless/if a[x]
 | |
| 	       a[x]= y
 | |
| 	       else
 | |
| 	       nil
 | |
| 	       end
 | |
| 	    */
 | |
| 	    LABEL *label = NEW_LABEL(line);
 | |
| 	    LABEL *lfin = NEW_LABEL(line);
 | |
| 
 | |
| 	    ADD_INSN(ret, line, dup);
 | |
| 	    if (id == 0) {
 | |
| 		/* or */
 | |
| 		ADD_INSNL(ret, line, branchif, label);
 | |
| 	    }
 | |
| 	    else {
 | |
| 		/* and */
 | |
| 		ADD_INSNL(ret, line, branchunless, label);
 | |
| 	    }
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 
 | |
| 	    COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body);
 | |
| 	    if (!poped) {
 | |
| 		ADD_INSN1(ret, line, setn, FIXNUM_INC(argc, 2+boff));
 | |
| 	    }
 | |
| 	    if (flag & VM_CALL_ARGS_SPLAT) {
 | |
| 		ADD_INSN1(ret, line, newarray, INT2FIX(1));
 | |
| 		if (boff > 0) {
 | |
| 		    ADD_INSN1(ret, line, dupn, INT2FIX(3));
 | |
| 		    ADD_INSN(ret, line, swap);
 | |
| 		    ADD_INSN(ret, line, pop);
 | |
| 		}
 | |
| 		ADD_INSN(ret, line, concatarray);
 | |
| 		if (boff > 0) {
 | |
| 		    ADD_INSN1(ret, line, setn, INT2FIX(3));
 | |
| 		    ADD_INSN(ret, line, pop);
 | |
| 		    ADD_INSN(ret, line, pop);
 | |
| 		}
 | |
| 		ADD_SEND_R(ret, line, ID2SYM(idASET),
 | |
| 			   argc, Qfalse, LONG2FIX(flag));
 | |
| 	    }
 | |
| 	    else {
 | |
| 		if (boff > 0)
 | |
| 		    ADD_INSN(ret, line, swap);
 | |
| 		ADD_SEND_R(ret, line, ID2SYM(idASET),
 | |
| 			   FIXNUM_INC(argc, 1), Qfalse, LONG2FIX(flag));
 | |
| 	    }
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	    ADD_INSNL(ret, line, jump, lfin);
 | |
| 	    ADD_LABEL(ret, label);
 | |
| 	    if (!poped) {
 | |
| 		ADD_INSN1(ret, line, setn, FIXNUM_INC(argc, 2+boff));
 | |
| 	    }
 | |
| 	    ADD_INSN1(ret, line, adjuststack, FIXNUM_INC(argc, 2+boff));
 | |
| 	    ADD_LABEL(ret, lfin);
 | |
| 	}
 | |
| 	else {
 | |
| 	    COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body);
 | |
| 	    ADD_SEND(ret, line, ID2SYM(id), INT2FIX(1));
 | |
| 	    if (!poped) {
 | |
| 		ADD_INSN1(ret, line, setn, FIXNUM_INC(argc, 2+boff));
 | |
| 	    }
 | |
| 	    if (flag & VM_CALL_ARGS_SPLAT) {
 | |
| 		ADD_INSN1(ret, line, newarray, INT2FIX(1));
 | |
| 		if (boff > 0) {
 | |
| 		    ADD_INSN1(ret, line, dupn, INT2FIX(3));
 | |
| 		    ADD_INSN(ret, line, swap);
 | |
| 		    ADD_INSN(ret, line, pop);
 | |
| 		}
 | |
| 		ADD_INSN(ret, line, concatarray);
 | |
| 		if (boff > 0) {
 | |
| 		    ADD_INSN1(ret, line, setn, INT2FIX(3));
 | |
| 		    ADD_INSN(ret, line, pop);
 | |
| 		    ADD_INSN(ret, line, pop);
 | |
| 		}
 | |
| 		ADD_SEND_R(ret, line, ID2SYM(idASET),
 | |
| 			   argc, Qfalse, LONG2FIX(flag));
 | |
| 	    }
 | |
| 	    else {
 | |
| 		if (boff > 0)
 | |
| 		    ADD_INSN(ret, line, swap);
 | |
| 		ADD_SEND_R(ret, line, ID2SYM(idASET),
 | |
| 			   FIXNUM_INC(argc, 1), Qfalse, LONG2FIX(flag));
 | |
| 	    }
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_OP_ASGN2:{
 | |
| 	ID atype = node->nd_next->nd_mid;
 | |
| 	LABEL *lfin = NEW_LABEL(line);
 | |
| 	LABEL *lcfin = NEW_LABEL(line);
 | |
| 	/*
 | |
| 	  class C; attr_accessor :c; end
 | |
| 	  r = C.new
 | |
| 	  r.a &&= v # asgn2
 | |
| 
 | |
| 	  eval r    # r
 | |
| 	  dup       # r r
 | |
| 	  eval r.a  # r o
 | |
| 
 | |
| 	  # or
 | |
| 	  dup       # r o o
 | |
| 	  if lcfin  # r o
 | |
| 	  pop       # r
 | |
| 	  eval v    # r v
 | |
| 	  swap      # v r
 | |
| 	  topn 1    # v r v
 | |
| 	  send a=   # v ?
 | |
| 	  jump lfin # v ?
 | |
| 
 | |
| 	  lcfin:      # r o
 | |
| 	  swap      # o r
 | |
| 
 | |
| 	  lfin:       # o ?
 | |
| 	  pop       # o
 | |
| 
 | |
| 	  # and
 | |
| 	  dup       # r o o
 | |
| 	  unless lcfin
 | |
| 	  pop       # r
 | |
| 	  eval v    # r v
 | |
| 	  swap      # v r
 | |
| 	  topn 1    # v r v
 | |
| 	  send a=   # v ?
 | |
| 	  jump lfin # v ?
 | |
| 
 | |
| 	  # others
 | |
| 	  eval v    # r o v
 | |
| 	  send ??   # r w
 | |
| 	  send a=   # w
 | |
| 
 | |
| 	*/
 | |
| 
 | |
| 	COMPILE(ret, "NODE_OP_ASGN2#recv", node->nd_recv);
 | |
| 	ADD_INSN(ret, line, dup);
 | |
| 	ADD_SEND(ret, line, ID2SYM(node->nd_next->nd_vid),
 | |
| 		 INT2FIX(0));
 | |
| 
 | |
| 	if (atype == 0 || atype == 1) {	/* 0: OR or 1: AND */
 | |
| 	    ADD_INSN(ret, line, dup);
 | |
| 	    if (atype == 0) {
 | |
| 		ADD_INSNL(ret, line, branchif, lcfin);
 | |
| 	    }
 | |
| 	    else {
 | |
| 		ADD_INSNL(ret, line, branchunless, lcfin);
 | |
| 	    }
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	    COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value);
 | |
| 	    ADD_INSN(ret, line, swap);
 | |
| 	    ADD_INSN1(ret, line, topn, INT2FIX(1));
 | |
| 	    ADD_SEND(ret, line, ID2SYM(node->nd_next->nd_aid),
 | |
| 		     INT2FIX(1));
 | |
| 	    ADD_INSNL(ret, line, jump, lfin);
 | |
| 
 | |
| 	    ADD_LABEL(ret, lcfin);
 | |
| 	    ADD_INSN(ret, line, swap);
 | |
| 
 | |
| 	    ADD_LABEL(ret, lfin);
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	    if (poped) {
 | |
| 		/* we can apply more optimize */
 | |
| 		ADD_INSN(ret, line, pop);
 | |
| 	    }
 | |
| 	}
 | |
| 	else {
 | |
| 	    COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value);
 | |
| 	    ADD_SEND(ret, line, ID2SYM(node->nd_next->nd_mid),
 | |
| 		     INT2FIX(1));
 | |
| 	    if (!poped) {
 | |
| 		ADD_INSN(ret, line, swap);
 | |
| 		ADD_INSN1(ret, line, topn, INT2FIX(1));
 | |
| 	    }
 | |
| 	    ADD_SEND(ret, line, ID2SYM(node->nd_next->nd_aid),
 | |
| 		     INT2FIX(1));
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_OP_CDECL: {
 | |
| 	LABEL *lfin = 0;
 | |
| 	LABEL *lassign = 0;
 | |
| 	ID mid;
 | |
| 
 | |
| 	switch (nd_type(node->nd_head)) {
 | |
| 	  case NODE_COLON3:
 | |
| 	    ADD_INSN1(ret, line, putobject, rb_cObject);
 | |
| 	    break;
 | |
| 	  case NODE_COLON2:
 | |
| 	    COMPILE(ret, "NODE_OP_CDECL/colon2#nd_head", node->nd_head->nd_head);
 | |
| 	    break;
 | |
| 	  default:
 | |
| 	    do {
 | |
| 		COMPILE_ERROR((ERROR_ARGS "%s: invalid node in NODE_OP_CDECL",
 | |
| 			       ruby_node_name(nd_type(node->nd_head))));
 | |
| 	    } while (0);
 | |
| 	    return COMPILE_NG;
 | |
| 	}
 | |
| 	mid = node->nd_head->nd_mid;
 | |
| 	/* cref */
 | |
| 	if (node->nd_aid == 0) {
 | |
| 	    lassign = NEW_LABEL(line);
 | |
| 	    ADD_INSN(ret, line, dup); /* cref cref */
 | |
| 	    ADD_INSN3(ret, line, defined, INT2FIX(DEFINED_CONST),
 | |
| 		      ID2SYM(mid), Qfalse); /* cref bool */
 | |
| 	    ADD_INSNL(ret, line, branchunless, lassign); /* cref */
 | |
| 	}
 | |
| 	ADD_INSN(ret, line, dup); /* cref cref */
 | |
| 	ADD_INSN1(ret, line, getconstant, ID2SYM(mid)); /* cref obj */
 | |
| 
 | |
| 	if (node->nd_aid == 0 || node->nd_aid == 1) {
 | |
| 	    lfin = NEW_LABEL(line);
 | |
| 	    if (!poped) ADD_INSN(ret, line, dup); /* cref [obj] obj */
 | |
| 	    if (node->nd_aid == 0)
 | |
| 		ADD_INSNL(ret, line, branchif, lfin);
 | |
| 	    else
 | |
| 		ADD_INSNL(ret, line, branchunless, lfin);
 | |
| 	    /* cref [obj] */
 | |
| 	    if (!poped) ADD_INSN(ret, line, pop); /* cref */
 | |
| 	    if (lassign) ADD_LABEL(ret, lassign);
 | |
| 	    COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value);
 | |
| 	    /* cref value */
 | |
| 	    if (poped)
 | |
| 		ADD_INSN1(ret, line, topn, INT2FIX(1)); /* cref value cref */
 | |
| 	    else {
 | |
| 		ADD_INSN1(ret, line, dupn, INT2FIX(2)); /* cref value cref value */
 | |
| 		ADD_INSN(ret, line, swap); /* cref value value cref */
 | |
| 	    }
 | |
| 	    ADD_INSN1(ret, line, setconstant, ID2SYM(mid)); /* cref [value] */
 | |
| 	    ADD_LABEL(ret, lfin);			    /* cref [value] */
 | |
| 	    if (!poped) ADD_INSN(ret, line, swap); /* [value] cref */
 | |
| 	    ADD_INSN(ret, line, pop); /* [value] */
 | |
| 	}
 | |
| 	else {
 | |
| 	    COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value);
 | |
| 	    /* cref obj value */
 | |
| 	    ADD_CALL(ret, line, ID2SYM(node->nd_aid), INT2FIX(1));
 | |
| 	    /* cref value */
 | |
| 	    ADD_INSN(ret, line, swap); /* value cref */
 | |
| 	    if (!poped) {
 | |
| 		ADD_INSN1(ret, line, topn, INT2FIX(1)); /* value cref value */
 | |
| 		ADD_INSN(ret, line, swap); /* value value cref */
 | |
| 	    }
 | |
| 	    ADD_INSN1(ret, line, setconstant, ID2SYM(mid));
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_OP_ASGN_AND:
 | |
|       case NODE_OP_ASGN_OR:{
 | |
| 	LABEL *lfin = NEW_LABEL(line);
 | |
| 	LABEL *lassign;
 | |
| 
 | |
| 	if (nd_type(node) == NODE_OP_ASGN_OR) {
 | |
| 	    LABEL *lfinish[2];
 | |
| 	    lfinish[0] = lfin;
 | |
| 	    lfinish[1] = 0;
 | |
| 	    defined_expr(iseq, ret, node->nd_head, lfinish, Qfalse);
 | |
| 	    lassign = lfinish[1];
 | |
| 	    if (!lassign) {
 | |
| 		lassign = NEW_LABEL(line);
 | |
| 	    }
 | |
| 	    ADD_INSNL(ret, line, branchunless, lassign);
 | |
| 	}
 | |
| 	else {
 | |
| 	    lassign = NEW_LABEL(line);
 | |
| 	}
 | |
| 
 | |
| 	COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_head", node->nd_head);
 | |
| 	ADD_INSN(ret, line, dup);
 | |
| 
 | |
| 	if (nd_type(node) == NODE_OP_ASGN_AND) {
 | |
| 	    ADD_INSNL(ret, line, branchunless, lfin);
 | |
| 	}
 | |
| 	else {
 | |
| 	    ADD_INSNL(ret, line, branchif, lfin);
 | |
| 	}
 | |
| 
 | |
| 	ADD_INSN(ret, line, pop);
 | |
| 	ADD_LABEL(ret, lassign);
 | |
| 	COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_value", node->nd_value);
 | |
| 	ADD_LABEL(ret, lfin);
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    /* we can apply more optimize */
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_CALL:
 | |
|       case NODE_FCALL:
 | |
|       case NODE_VCALL:{		/* VCALL: variable or call */
 | |
| 	/*
 | |
| 	  call:  obj.method(...)
 | |
| 	  fcall: func(...)
 | |
| 	  vcall: func
 | |
| 	*/
 | |
| 	DECL_ANCHOR(recv);
 | |
| 	DECL_ANCHOR(args);
 | |
| 	ID mid = node->nd_mid;
 | |
| 	VALUE argc;
 | |
| 	VALUE flag = 0;
 | |
| 	VALUE parent_block = iseq->compile_data->current_block;
 | |
| 	iseq->compile_data->current_block = Qfalse;
 | |
| 
 | |
| 	INIT_ANCHOR(recv);
 | |
| 	INIT_ANCHOR(args);
 | |
| #if SUPPORT_JOKE
 | |
| 	if (nd_type(node) == NODE_VCALL) {
 | |
| 	    ID id_bitblt;
 | |
| 	    ID id_answer;
 | |
| 
 | |
| 	    CONST_ID(id_bitblt, "bitblt");
 | |
| 	    CONST_ID(id_answer, "the_answer_to_life_the_universe_and_everything");
 | |
| 
 | |
| 	    if (mid == id_bitblt) {
 | |
| 		ADD_INSN(ret, line, bitblt);
 | |
| 		break;
 | |
| 	    }
 | |
| 	    else if (mid == id_answer) {
 | |
| 		ADD_INSN(ret, line, answer);
 | |
| 		break;
 | |
| 	    }
 | |
| 	}
 | |
| 	/* only joke */
 | |
| 	{
 | |
| 	    ID goto_id;
 | |
| 	    ID label_id;
 | |
| 
 | |
| 	    CONST_ID(goto_id, "__goto__");
 | |
| 	    CONST_ID(label_id, "__label__");
 | |
| 
 | |
| 	    if (nd_type(node) == NODE_FCALL &&
 | |
| 		(mid == goto_id || mid == label_id)) {
 | |
| 		LABEL *label;
 | |
| 		st_data_t data;
 | |
| 		st_table *labels_table = iseq->compile_data->labels_table;
 | |
| 		ID label_name;
 | |
| 
 | |
| 		if (!labels_table) {
 | |
| 		    labels_table = st_init_numtable();
 | |
| 		    iseq->compile_data->labels_table = labels_table;
 | |
| 		}
 | |
| 		if (nd_type(node->nd_args->nd_head) == NODE_LIT &&
 | |
| 		    SYMBOL_P(node->nd_args->nd_head->nd_lit)) {
 | |
| 
 | |
| 		    label_name = SYM2ID(node->nd_args->nd_head->nd_lit);
 | |
| 		    if (!st_lookup(labels_table, (st_data_t)label_name, &data)) {
 | |
| 			label = NEW_LABEL(line);
 | |
| 			label->position = line;
 | |
| 			st_insert(labels_table, (st_data_t)label_name, (st_data_t)label);
 | |
| 		    }
 | |
| 		    else {
 | |
| 			label = (LABEL *)data;
 | |
| 		    }
 | |
| 		}
 | |
| 		else {
 | |
| 		    COMPILE_ERROR((ERROR_ARGS "invalid goto/label format"));
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		if (mid == goto_id) {
 | |
| 		    ADD_INSNL(ret, line, jump, label);
 | |
| 		}
 | |
| 		else {
 | |
| 		    ADD_LABEL(ret, label);
 | |
| 		}
 | |
| 		break;
 | |
| 	    }
 | |
| 	}
 | |
| #endif
 | |
| 	/* receiver */
 | |
| 	if (type == NODE_CALL) {
 | |
| 	    COMPILE(recv, "recv", node->nd_recv);
 | |
| 	}
 | |
| 	else if (type == NODE_FCALL || type == NODE_VCALL) {
 | |
| 	    ADD_CALL_RECEIVER(recv, line);
 | |
| 	}
 | |
| 
 | |
| 	/* args */
 | |
| 	if (nd_type(node) != NODE_VCALL) {
 | |
| 	    argc = setup_args(iseq, args, node->nd_args, &flag);
 | |
| 	}
 | |
| 	else {
 | |
| 	    argc = INT2FIX(0);
 | |
| 	}
 | |
| 
 | |
| 	ADD_SEQ(ret, recv);
 | |
| 	ADD_SEQ(ret, args);
 | |
| 
 | |
| 	debugp_param("call args argc", argc);
 | |
| 	debugp_param("call method", ID2SYM(mid));
 | |
| 
 | |
| 	switch (nd_type(node)) {
 | |
| 	  case NODE_VCALL:
 | |
| 	    flag |= VM_CALL_VCALL;
 | |
| 	    /* VCALL is funcall, so fall through */
 | |
| 	  case NODE_FCALL:
 | |
| 	    flag |= VM_CALL_FCALL;
 | |
| 	}
 | |
| 
 | |
| 	ADD_SEND_R(ret, line, ID2SYM(mid),
 | |
| 		   argc, parent_block, LONG2FIX(flag));
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_SUPER:
 | |
|       case NODE_ZSUPER:{
 | |
| 	DECL_ANCHOR(args);
 | |
| 	int argc;
 | |
| 	VALUE flag = 0;
 | |
| 	VALUE parent_block = iseq->compile_data->current_block;
 | |
| 
 | |
| 	INIT_ANCHOR(args);
 | |
| 	iseq->compile_data->current_block = Qfalse;
 | |
| 	if (nd_type(node) == NODE_SUPER) {
 | |
| 	    VALUE vargc = setup_args(iseq, args, node->nd_args, &flag);
 | |
| 	    argc = FIX2INT(vargc);
 | |
| 	}
 | |
| 	else {
 | |
| 	    /* NODE_ZSUPER */
 | |
| 	    int i;
 | |
| 	    rb_iseq_t *liseq = iseq->local_iseq;
 | |
| 	    int lvar_level = get_lvar_level(iseq);
 | |
| 
 | |
| 	    argc = liseq->argc;
 | |
| 
 | |
| 	    /* normal arguments */
 | |
| 	    for (i = 0; i < liseq->argc; i++) {
 | |
| 		int idx = liseq->local_size - i;
 | |
| 		ADD_INSN2(args, line, getlocal, INT2FIX(idx), INT2FIX(lvar_level));
 | |
| 	    }
 | |
| 
 | |
| 	    if (!liseq->arg_simple) {
 | |
| 		if (liseq->arg_opts) {
 | |
| 		    /* optional arguments */
 | |
| 		    int j;
 | |
| 		    for (j = 0; j < liseq->arg_opts - 1; j++) {
 | |
| 			int idx = liseq->local_size - (i + j);
 | |
| 			ADD_INSN2(args, line, getlocal, INT2FIX(idx), INT2FIX(lvar_level));
 | |
| 		    }
 | |
| 		    i += j;
 | |
| 		    argc = i;
 | |
| 		}
 | |
| 
 | |
| 		if (liseq->arg_rest != -1) {
 | |
| 		    /* rest argument */
 | |
| 		    int idx = liseq->local_size - liseq->arg_rest;
 | |
| 		    ADD_INSN2(args, line, getlocal, INT2FIX(idx), INT2FIX(lvar_level));
 | |
| 		    argc = liseq->arg_rest + 1;
 | |
| 		    flag |= VM_CALL_ARGS_SPLAT;
 | |
| 		}
 | |
| 
 | |
| 		if (liseq->arg_post_len) {
 | |
| 		    /* post arguments */
 | |
| 		    int post_len = liseq->arg_post_len;
 | |
| 		    int post_start = liseq->arg_post_start;
 | |
| 
 | |
| 		    if (liseq->arg_rest != -1) {
 | |
| 			int j;
 | |
| 			for (j=0; j<post_len; j++) {
 | |
| 			    int idx = liseq->local_size - (post_start + j);
 | |
| 			    ADD_INSN2(args, line, getlocal, INT2FIX(idx), INT2FIX(lvar_level));
 | |
| 			}
 | |
| 			ADD_INSN1(args, line, newarray, INT2FIX(j));
 | |
| 			ADD_INSN (args, line, concatarray);
 | |
| 			/* argc is settled at above */
 | |
| 		    }
 | |
| 		    else {
 | |
| 			int j;
 | |
| 			for (j=0; j<post_len; j++) {
 | |
| 			    int idx = liseq->local_size - (post_start + j);
 | |
| 			    ADD_INSN2(args, line, getlocal, INT2FIX(idx), INT2FIX(lvar_level));
 | |
| 			}
 | |
| 			argc = post_len + post_start;
 | |
| 		    }
 | |
| 		}
 | |
| 
 | |
| 		if (liseq->arg_keyword >= 0) {
 | |
| 		    int local_size = liseq->local_size;
 | |
| 		    int idx = local_size - liseq->arg_keyword;
 | |
| 		    argc++;
 | |
| 		    ADD_INSN1(args, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | |
| 		    ADD_INSN2(args, line, getlocal, INT2FIX(idx), INT2FIX(lvar_level));
 | |
| 		    ADD_SEND (args, line, ID2SYM(rb_intern("dup")), INT2FIX(0));
 | |
| 		    for (i = 0; i < liseq->arg_keywords; ++i) {
 | |
| 			ID id = liseq->arg_keyword_table[i];
 | |
| 			idx = local_size - get_local_var_idx(liseq, id);
 | |
| 			ADD_INSN1(args, line, putobject, ID2SYM(id));
 | |
| 			ADD_INSN2(args, line, getlocal, INT2FIX(idx), INT2FIX(lvar_level));
 | |
| 		    }
 | |
| 		    ADD_SEND(args, line, ID2SYM(id_core_hash_merge_ptr), INT2FIX(i * 2 + 1));
 | |
| 		    if (liseq->arg_rest != -1) {
 | |
| 			ADD_INSN1(args, line, newarray, INT2FIX(1));
 | |
| 			ADD_INSN (args, line, concatarray);
 | |
| 			--argc;
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
| 	/* dummy receiver */
 | |
| 	ADD_INSN1(ret, line, putobject, nd_type(node) == NODE_ZSUPER ? Qfalse : Qtrue);
 | |
| 	ADD_SEQ(ret, args);
 | |
| 	ADD_INSN1(ret, line, invokesuper, new_callinfo(iseq, 0, argc, parent_block,
 | |
| 								flag | VM_CALL_SUPER | VM_CALL_FCALL));
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_ARRAY:{
 | |
| 	compile_array_(iseq, ret, node, COMPILE_ARRAY_TYPE_ARRAY, poped);
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_ZARRAY:{
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN1(ret, line, newarray, INT2FIX(0));
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_VALUES:{
 | |
| 	NODE *n = node;
 | |
| 	while (n) {
 | |
| 	    COMPILE(ret, "values item", n->nd_head);
 | |
| 	    n = n->nd_next;
 | |
| 	}
 | |
| 	ADD_INSN1(ret, line, newarray, INT2FIX(node->nd_alen));
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_HASH:{
 | |
| 	DECL_ANCHOR(list);
 | |
| 	int type = node->nd_head ? nd_type(node->nd_head) : NODE_ZARRAY;
 | |
| 
 | |
| 	INIT_ANCHOR(list);
 | |
| 	switch (type) {
 | |
| 	  case NODE_ARRAY:
 | |
| 	    compile_array(iseq, list, node->nd_head, COMPILE_ARRAY_TYPE_HASH);
 | |
| 	    ADD_SEQ(ret, list);
 | |
| 	    break;
 | |
| 
 | |
| 	  case NODE_ZARRAY:
 | |
| 	    ADD_INSN1(ret, line, newhash, INT2FIX(0));
 | |
| 	    break;
 | |
| 
 | |
| 	  default:
 | |
| 	    rb_bug("can't make hash with this node: %s", ruby_node_name(type));
 | |
| 	}
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_RETURN:{
 | |
| 	rb_iseq_t *is = iseq;
 | |
| 
 | |
| 	if (is) {
 | |
| 	    if (is->type == ISEQ_TYPE_TOP) {
 | |
| 		COMPILE_ERROR((ERROR_ARGS "Invalid return"));
 | |
| 	    }
 | |
| 	    else {
 | |
| 		LABEL *splabel = 0;
 | |
| 
 | |
| 		if (is->type == ISEQ_TYPE_METHOD) {
 | |
| 		    splabel = NEW_LABEL(0);
 | |
| 		    ADD_LABEL(ret, splabel);
 | |
| 		    ADD_ADJUST(ret, line, 0);
 | |
| 		}
 | |
| 
 | |
| 		COMPILE(ret, "return nd_stts (return val)", node->nd_stts);
 | |
| 
 | |
| 		if (is->type == ISEQ_TYPE_METHOD) {
 | |
| 		    add_ensure_iseq(ret, iseq, 1);
 | |
| 		    ADD_TRACE(ret, line, RUBY_EVENT_RETURN);
 | |
| 		    ADD_INSN(ret, line, leave);
 | |
| 		    ADD_ADJUST_RESTORE(ret, splabel);
 | |
| 
 | |
| 		    if (!poped) {
 | |
| 			ADD_INSN(ret, line, putnil);
 | |
| 		    }
 | |
| 		}
 | |
| 		else {
 | |
| 		    ADD_INSN1(ret, line, throw, INT2FIX(0x01) /* TAG_RETURN */ );
 | |
| 		    if (poped) {
 | |
| 			ADD_INSN(ret, line, pop);
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_YIELD:{
 | |
| 	DECL_ANCHOR(args);
 | |
| 	VALUE argc;
 | |
| 	VALUE flag = 0;
 | |
| 
 | |
| 	INIT_ANCHOR(args);
 | |
| 	if (iseq->type == ISEQ_TYPE_TOP) {
 | |
| 	    COMPILE_ERROR((ERROR_ARGS "Invalid yield"));
 | |
| 	}
 | |
| 
 | |
| 	if (node->nd_head) {
 | |
| 	    argc = setup_args(iseq, args, node->nd_head, &flag);
 | |
| 	}
 | |
| 	else {
 | |
| 	    argc = INT2FIX(0);
 | |
| 	}
 | |
| 
 | |
| 	ADD_SEQ(ret, args);
 | |
| 	ADD_INSN1(ret, line, invokeblock, new_callinfo(iseq, 0, FIX2INT(argc), 0, flag));
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_LVAR:{
 | |
| 	if (!poped) {
 | |
| 	    ID id = node->nd_vid;
 | |
| 	    int idx = iseq->local_iseq->local_size - get_local_var_idx(iseq, id);
 | |
| 
 | |
| 	    debugs("id: %s idx: %d\n", rb_id2name(id), idx);
 | |
| 	    ADD_INSN2(ret, line, getlocal, INT2FIX(idx), INT2FIX(get_lvar_level(iseq)));
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_DVAR:{
 | |
| 	int lv, idx, ls;
 | |
| 	debugi("nd_vid", node->nd_vid);
 | |
| 	if (!poped) {
 | |
| 	    idx = get_dyna_var_idx(iseq, node->nd_vid, &lv, &ls);
 | |
| 	    if (idx < 0) {
 | |
| 		rb_bug("unknown dvar (%s)", rb_id2name(node->nd_vid));
 | |
| 	    }
 | |
| 	    ADD_INSN2(ret, line, getlocal, INT2FIX(ls - idx), INT2FIX(lv));
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_GVAR:{
 | |
| 	ADD_INSN1(ret, line, getglobal,
 | |
| 		  ((VALUE)node->nd_entry | 1));
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_IVAR:{
 | |
| 	debugi("nd_vid", node->nd_vid);
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN2(ret, line, getinstancevariable,
 | |
| 		      ID2SYM(node->nd_vid), INT2FIX(iseq->is_size++));
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_CONST:{
 | |
| 	debugi("nd_vid", node->nd_vid);
 | |
| 
 | |
| 	if (iseq->compile_data->option->inline_const_cache) {
 | |
| 	    LABEL *lend = NEW_LABEL(line);
 | |
| 	    int ic_index = iseq->is_size++;
 | |
| 
 | |
| 	    ADD_INSN2(ret, line, getinlinecache, lend, INT2FIX(ic_index));
 | |
| 	    ADD_INSN1(ret, line, getconstant, ID2SYM(node->nd_vid));
 | |
| 	    ADD_INSN1(ret, line, setinlinecache, INT2FIX(ic_index));
 | |
| 	    ADD_LABEL(ret, lend);
 | |
| 	}
 | |
| 	else {
 | |
| 	    ADD_INSN(ret, line, putnil);
 | |
| 	    ADD_INSN1(ret, line, getconstant, ID2SYM(node->nd_vid));
 | |
| 	}
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_CVAR:{
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN1(ret, line, getclassvariable,
 | |
| 		      ID2SYM(node->nd_vid));
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_NTH_REF:{
 | |
|         if (!poped) {
 | |
| 	    ADD_INSN2(ret, line, getspecial, INT2FIX(1) /* '~'  */,
 | |
| 		      INT2FIX(node->nd_nth << 1));
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_BACK_REF:{
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN2(ret, line, getspecial, INT2FIX(1) /* '~' */,
 | |
| 		      INT2FIX(0x01 | (node->nd_nth << 1)));
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_MATCH:
 | |
|       case NODE_MATCH2:
 | |
|       case NODE_MATCH3:{
 | |
| 	DECL_ANCHOR(recv);
 | |
| 	DECL_ANCHOR(val);
 | |
| 
 | |
| 	INIT_ANCHOR(recv);
 | |
| 	INIT_ANCHOR(val);
 | |
| 	switch (nd_type(node)) {
 | |
| 	  case NODE_MATCH:
 | |
| 	    ADD_INSN1(recv, line, putobject, node->nd_lit);
 | |
| 	    ADD_INSN2(val, line, getspecial, INT2FIX(0),
 | |
| 		      INT2FIX(0));
 | |
| 	    break;
 | |
| 	  case NODE_MATCH2:
 | |
| 	    COMPILE(recv, "receiver", node->nd_recv);
 | |
| 	    COMPILE(val, "value", node->nd_value);
 | |
| 	    break;
 | |
| 	  case NODE_MATCH3:
 | |
| 	    COMPILE(recv, "receiver", node->nd_value);
 | |
| 	    COMPILE(val, "value", node->nd_recv);
 | |
| 	    break;
 | |
| 	}
 | |
| 
 | |
| 	if (iseq->compile_data->option->specialized_instruction) {
 | |
| 	    /* TODO: detect by node */
 | |
| 	    if (recv->last == recv->anchor.next &&
 | |
| 		INSN_OF(recv->last) == BIN(putobject) &&
 | |
| 		nd_type(node) == NODE_MATCH2) {
 | |
| 		ADD_SEQ(ret, val);
 | |
| 		ADD_INSN1(ret, line, opt_regexpmatch1,
 | |
| 			  OPERAND_AT(recv->last, 0));
 | |
| 	    }
 | |
| 	    else {
 | |
| 		ADD_SEQ(ret, recv);
 | |
| 		ADD_SEQ(ret, val);
 | |
| 		ADD_INSN1(ret, line, opt_regexpmatch2, new_callinfo(iseq, idEqTilde, 1, 0, 0));
 | |
| 	    }
 | |
| 	}
 | |
| 	else {
 | |
| 	    ADD_SEQ(ret, recv);
 | |
| 	    ADD_SEQ(ret, val);
 | |
| 	    ADD_SEND(ret, line, ID2SYM(idEqTilde), INT2FIX(1));
 | |
| 	}
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_LIT:{
 | |
| 	debugp_param("lit", node->nd_lit);
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN1(ret, line, putobject, node->nd_lit);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_STR:{
 | |
| 	debugp_param("nd_lit", node->nd_lit);
 | |
| 	if (!poped) {
 | |
| 	    OBJ_FREEZE(node->nd_lit);
 | |
| 	    ADD_INSN1(ret, line, putstring, node->nd_lit);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_DSTR:{
 | |
| 	compile_dstr(iseq, ret, node);
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_XSTR:{
 | |
| 	OBJ_FREEZE(node->nd_lit);
 | |
| 	ADD_CALL_RECEIVER(ret, line);
 | |
| 	ADD_INSN1(ret, line, putobject, node->nd_lit);
 | |
| 	ADD_CALL(ret, line, ID2SYM(idBackquote), INT2FIX(1));
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_DXSTR:{
 | |
| 	ADD_CALL_RECEIVER(ret, line);
 | |
| 	compile_dstr(iseq, ret, node);
 | |
| 	ADD_CALL(ret, line, ID2SYM(idBackquote), INT2FIX(1));
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_EVSTR:{
 | |
| 	COMPILE(ret, "nd_body", node->nd_body);
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	else {
 | |
| 	    ADD_INSN(ret, line, tostring);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_DREGX:{
 | |
| 	compile_dregx(iseq, ret, node);
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_DREGX_ONCE:{
 | |
| 	int ic_index = iseq->is_size++;
 | |
| 	NODE *dregx_node = NEW_NODE(NODE_DREGX, node->u1.value, node->u2.value, node->u3.value);
 | |
| 	NODE *block_node = NEW_NODE(NODE_SCOPE, 0, dregx_node, 0);
 | |
| 	VALUE block_iseq = NEW_CHILD_ISEQVAL(block_node, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line);
 | |
| 
 | |
| 	ADD_INSN2(ret, line, once, block_iseq, INT2FIX(ic_index));
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_ARGSCAT:{
 | |
| 	if (poped) {
 | |
| 	    COMPILE(ret, "argscat head", node->nd_head);
 | |
| 	    ADD_INSN1(ret, line, splatarray, Qfalse);
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	    COMPILE(ret, "argscat body", node->nd_body);
 | |
| 	    ADD_INSN1(ret, line, splatarray, Qfalse);
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	else {
 | |
| 	    COMPILE(ret, "argscat head", node->nd_head);
 | |
| 	    COMPILE(ret, "argscat body", node->nd_body);
 | |
| 	    ADD_INSN(ret, line, concatarray);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_ARGSPUSH:{
 | |
| 	if (poped) {
 | |
| 	    COMPILE(ret, "arsgpush head", node->nd_head);
 | |
| 	    ADD_INSN1(ret, line, splatarray, Qfalse);
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	    COMPILE_(ret, "argspush body", node->nd_body, poped);
 | |
| 	}
 | |
| 	else {
 | |
| 	    COMPILE(ret, "arsgpush head", node->nd_head);
 | |
| 	    COMPILE_(ret, "argspush body", node->nd_body, poped);
 | |
| 	    ADD_INSN1(ret, line, newarray, INT2FIX(1));
 | |
| 	    ADD_INSN(ret, line, concatarray);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_SPLAT:{
 | |
| 	COMPILE(ret, "splat", node->nd_head);
 | |
| 	ADD_INSN1(ret, line, splatarray, Qtrue);
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_DEFN:{
 | |
| 	VALUE iseqval = NEW_ISEQVAL(node->nd_defn,
 | |
| 				    rb_str_dup(rb_id2str(node->nd_mid)),
 | |
| 				    ISEQ_TYPE_METHOD, line);
 | |
| 
 | |
| 	debugp_param("defn/iseq", iseqval);
 | |
| 
 | |
| 	ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | |
| 	ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
 | |
| 	ADD_INSN1(ret, line, putobject, ID2SYM(node->nd_mid));
 | |
| 	ADD_INSN1(ret, line, putiseq, iseqval);
 | |
| 	ADD_SEND (ret, line, ID2SYM(id_core_define_method), INT2FIX(3));
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 
 | |
| 	debugp_param("defn", iseqval);
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_DEFS:{
 | |
| 	VALUE iseqval = NEW_ISEQVAL(node->nd_defn,
 | |
| 				    rb_str_dup(rb_id2str(node->nd_mid)),
 | |
| 				    ISEQ_TYPE_METHOD, line);
 | |
| 
 | |
| 	debugp_param("defs/iseq", iseqval);
 | |
| 
 | |
| 	ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | |
| 	COMPILE(ret, "defs: recv", node->nd_recv);
 | |
| 	ADD_INSN1(ret, line, putobject, ID2SYM(node->nd_mid));
 | |
| 	ADD_INSN1(ret, line, putiseq, iseqval);
 | |
| 	ADD_SEND (ret, line, ID2SYM(id_core_define_singleton_method), INT2FIX(3));
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_ALIAS:{
 | |
| 	ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | |
| 	ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
 | |
| 	COMPILE(ret, "alias arg1", node->u1.node);
 | |
| 	COMPILE(ret, "alias arg2", node->u2.node);
 | |
| 	ADD_SEND(ret, line, ID2SYM(id_core_set_method_alias), INT2FIX(3));
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_VALIAS:{
 | |
| 	ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | |
| 	ADD_INSN1(ret, line, putobject, ID2SYM(node->u1.id));
 | |
| 	ADD_INSN1(ret, line, putobject, ID2SYM(node->u2.id));
 | |
| 	ADD_SEND(ret, line, ID2SYM(id_core_set_variable_alias), INT2FIX(2));
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_UNDEF:{
 | |
| 	ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | |
| 	ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_CBASE));
 | |
| 	COMPILE(ret, "undef arg", node->u2.node);
 | |
| 	ADD_SEND(ret, line, ID2SYM(id_core_undef_method), INT2FIX(2));
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_CLASS:{
 | |
| 	VALUE iseqval =
 | |
| 	    NEW_CHILD_ISEQVAL(
 | |
| 		node->nd_body,
 | |
| 		rb_sprintf("<class:%s>", rb_id2name(node->nd_cpath->nd_mid)),
 | |
| 		ISEQ_TYPE_CLASS, line);
 | |
| 	VALUE noscope = compile_cpath(ret, iseq, node->nd_cpath);
 | |
| 	int flags = VM_DEFINECLASS_TYPE_CLASS;
 | |
| 	if (!noscope) flags |= VM_DEFINECLASS_FLAG_SCOPED;
 | |
| 	if (node->nd_super) flags |= VM_DEFINECLASS_FLAG_HAS_SUPERCLASS;
 | |
| 	COMPILE(ret, "super", node->nd_super);
 | |
| 	ADD_INSN3(ret, line, defineclass,
 | |
| 		  ID2SYM(node->nd_cpath->nd_mid), iseqval, INT2FIX(flags));
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_MODULE:{
 | |
| 	VALUE iseqval = NEW_CHILD_ISEQVAL(
 | |
| 	    node->nd_body,
 | |
| 	    rb_sprintf("<module:%s>", rb_id2name(node->nd_cpath->nd_mid)),
 | |
| 	    ISEQ_TYPE_CLASS, line);
 | |
| 
 | |
| 	VALUE noscope = compile_cpath(ret, iseq, node->nd_cpath);
 | |
| 	int flags = VM_DEFINECLASS_TYPE_MODULE;
 | |
| 	if (!noscope) flags |= VM_DEFINECLASS_FLAG_SCOPED;
 | |
| 	ADD_INSN (ret, line, putnil); /* dummy */
 | |
| 	ADD_INSN3(ret, line, defineclass,
 | |
| 		  ID2SYM(node->nd_cpath->nd_mid), iseqval, INT2FIX(flags));
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_SCLASS:{
 | |
| 	ID singletonclass;
 | |
| 	VALUE iseqval =
 | |
| 	    NEW_ISEQVAL(node->nd_body, rb_str_new2("singleton class"),
 | |
| 			ISEQ_TYPE_CLASS, line);
 | |
| 
 | |
| 	COMPILE(ret, "sclass#recv", node->nd_recv);
 | |
| 	ADD_INSN (ret, line, putnil);
 | |
| 	CONST_ID(singletonclass, "singletonclass");
 | |
| 	ADD_INSN3(ret, line, defineclass,
 | |
| 		  ID2SYM(singletonclass), iseqval,
 | |
| 		  INT2FIX(VM_DEFINECLASS_TYPE_SINGLETON_CLASS));
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_COLON2:{
 | |
| 	if (rb_is_const_id(node->nd_mid)) {
 | |
| 	    /* constant */
 | |
| 	    LABEL *lend = NEW_LABEL(line);
 | |
| 	    int ic_index = iseq->is_size++;
 | |
| 
 | |
| 	    DECL_ANCHOR(pref);
 | |
| 	    DECL_ANCHOR(body);
 | |
| 
 | |
| 	    INIT_ANCHOR(pref);
 | |
| 	    INIT_ANCHOR(body);
 | |
| 	    compile_colon2(iseq, node, pref, body);
 | |
| 	    if (LIST_SIZE_ZERO(pref)) {
 | |
| 		if (iseq->compile_data->option->inline_const_cache) {
 | |
| 		    ADD_INSN2(ret, line, getinlinecache, lend, INT2FIX(ic_index));
 | |
| 		}
 | |
| 		else {
 | |
| 		    ADD_INSN(ret, line, putnil);
 | |
| 		}
 | |
| 
 | |
| 		ADD_SEQ(ret, body);
 | |
| 
 | |
| 		if (iseq->compile_data->option->inline_const_cache) {
 | |
| 		    ADD_INSN1(ret, line, setinlinecache, INT2FIX(ic_index));
 | |
| 		    ADD_LABEL(ret, lend);
 | |
| 		}
 | |
| 	    }
 | |
| 	    else {
 | |
| 		ADD_SEQ(ret, pref);
 | |
| 		ADD_SEQ(ret, body);
 | |
| 	    }
 | |
| 	}
 | |
| 	else {
 | |
| 	    /* function call */
 | |
| 	    ADD_CALL_RECEIVER(ret, line);
 | |
| 	    COMPILE(ret, "colon2#nd_head", node->nd_head);
 | |
| 	    ADD_CALL(ret, line, ID2SYM(node->nd_mid),
 | |
| 		     INT2FIX(1));
 | |
| 	}
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_COLON3:{
 | |
| 	LABEL *lend = NEW_LABEL(line);
 | |
| 	int ic_index = iseq->is_size++;
 | |
| 
 | |
| 	debugi("colon3#nd_mid", node->nd_mid);
 | |
| 
 | |
| 	/* add cache insn */
 | |
| 	if (iseq->compile_data->option->inline_const_cache) {
 | |
| 	    ADD_INSN2(ret, line, getinlinecache, lend, INT2FIX(ic_index));
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 
 | |
| 	ADD_INSN1(ret, line, putobject, rb_cObject);
 | |
| 	ADD_INSN1(ret, line, getconstant, ID2SYM(node->nd_mid));
 | |
| 
 | |
| 	if (iseq->compile_data->option->inline_const_cache) {
 | |
| 	    ADD_INSN1(ret, line, setinlinecache, INT2FIX(ic_index));
 | |
| 	    ADD_LABEL(ret, lend);
 | |
| 	}
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_DOT2:
 | |
|       case NODE_DOT3:{
 | |
| 	VALUE flag = type == NODE_DOT2 ? INT2FIX(0) : INT2FIX(1);
 | |
| 	COMPILE(ret, "min", (NODE *) node->nd_beg);
 | |
| 	COMPILE(ret, "max", (NODE *) node->nd_end);
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	else {
 | |
| 	    ADD_INSN1(ret, line, newrange, flag);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_FLIP2:
 | |
|       case NODE_FLIP3:{
 | |
| 	LABEL *lend = NEW_LABEL(line);
 | |
| 	LABEL *lfin = NEW_LABEL(line);
 | |
| 	LABEL *ltrue = NEW_LABEL(line);
 | |
| 	rb_iseq_t *local_iseq = iseq->local_iseq;
 | |
| 	rb_num_t cnt;
 | |
| 	VALUE key;
 | |
| 
 | |
| 	cnt = local_iseq->flip_cnt++ + DEFAULT_SPECIAL_VAR_COUNT;
 | |
| 	key = INT2FIX(cnt);
 | |
| 
 | |
| 	ADD_INSN2(ret, line, getspecial, key, INT2FIX(0));
 | |
| 	ADD_INSNL(ret, line, branchif, lend);
 | |
| 
 | |
| 	/* *flip == 0 */
 | |
| 	COMPILE(ret, "flip2 beg", node->nd_beg);
 | |
| 	ADD_INSN(ret, line, dup);
 | |
| 	ADD_INSNL(ret, line, branchunless, lfin);
 | |
| 	if (nd_type(node) == NODE_FLIP3) {
 | |
| 	    ADD_INSN(ret, line, dup);
 | |
| 	    ADD_INSN1(ret, line, setspecial, key);
 | |
| 	    ADD_INSNL(ret, line, jump, lfin);
 | |
| 	}
 | |
| 	else {
 | |
| 	    ADD_INSN1(ret, line, setspecial, key);
 | |
| 	}
 | |
| 
 | |
| 	/* *flip == 1 */
 | |
| 	ADD_LABEL(ret, lend);
 | |
| 	COMPILE(ret, "flip2 end", node->nd_end);
 | |
| 	ADD_INSNL(ret, line, branchunless, ltrue);
 | |
| 	ADD_INSN1(ret, line, putobject, Qfalse);
 | |
| 	ADD_INSN1(ret, line, setspecial, key);
 | |
| 
 | |
| 	ADD_LABEL(ret, ltrue);
 | |
| 	ADD_INSN1(ret, line, putobject, Qtrue);
 | |
| 
 | |
| 	ADD_LABEL(ret, lfin);
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_SELF:{
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN(ret, line, putself);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_NIL:{
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN(ret, line, putnil);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_TRUE:{
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN1(ret, line, putobject, Qtrue);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_FALSE:{
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN1(ret, line, putobject, Qfalse);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_ERRINFO:{
 | |
| 	if (!poped) {
 | |
| 	    if (iseq->type == ISEQ_TYPE_RESCUE) {
 | |
| 		ADD_INSN2(ret, line, getlocal, INT2FIX(2), INT2FIX(0));
 | |
| 	    }
 | |
| 	    else {
 | |
| 		rb_iseq_t *ip = iseq;
 | |
| 		int level = 0;
 | |
| 		while (ip) {
 | |
| 		    if (ip->type == ISEQ_TYPE_RESCUE) {
 | |
| 			break;
 | |
| 		    }
 | |
| 		    ip = ip->parent_iseq;
 | |
| 		    level++;
 | |
| 		}
 | |
| 		if (ip) {
 | |
| 		    ADD_INSN2(ret, line, getlocal, INT2FIX(2), INT2FIX(level));
 | |
| 		}
 | |
| 		else {
 | |
| 		    ADD_INSN(ret, line, putnil);
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_DEFINED:{
 | |
| 	if (poped) break;
 | |
| 	if (!node->nd_head) {
 | |
| 	    VALUE str = rb_iseq_defined_string(DEFINED_NIL);
 | |
| 	    ADD_INSN1(ret, nd_line(node), putobject, str);
 | |
| 	}
 | |
| 	else {
 | |
| 	    LABEL *lfinish[2];
 | |
| 	    lfinish[0] = NEW_LABEL(line);
 | |
| 	    lfinish[1] = 0;
 | |
| 	    ADD_INSN(ret, line, putnil);
 | |
| 	    defined_expr(iseq, ret, node->nd_head, lfinish, Qtrue);
 | |
| 	    ADD_INSN(ret, line, swap);
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	    if (lfinish[1]) {
 | |
| 		ADD_LABEL(ret, lfinish[1]);
 | |
| 	    }
 | |
| 	    ADD_LABEL(ret, lfinish[0]);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_POSTEXE:{
 | |
| 	/* compiled to:
 | |
| 	 *   ONCE{ rb_mRubyVMFrozenCore::core#set_postexe{ ... } }
 | |
| 	 */
 | |
| 	int is_index = iseq->is_size++;
 | |
| 	VALUE once_iseq = NEW_CHILD_ISEQVAL(
 | |
| 	    NEW_IFUNC(build_postexe_iseq, node->nd_body),
 | |
| 	    make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line);
 | |
| 
 | |
| 	ADD_INSN2(ret, line, once, once_iseq, INT2FIX(is_index));
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_KW_ARG:{
 | |
| 	LABEL *default_label = NEW_LABEL(line);
 | |
| 	LABEL *end_label = 0;
 | |
| 	int idx, lv, ls;
 | |
| 	ID id = node->nd_body->nd_vid;
 | |
| 
 | |
| 	ADD_INSN(ret, line, dup);
 | |
| 	ADD_INSN1(ret, line, putobject, ID2SYM(id));
 | |
| 	ADD_SEND(ret, line, ID2SYM(rb_intern("key?")), INT2FIX(1));
 | |
| 	ADD_INSNL(ret, line, branchunless, default_label);
 | |
| 	ADD_INSN(ret, line, dup);
 | |
| 	ADD_INSN1(ret, line, putobject, ID2SYM(id));
 | |
| 	ADD_SEND(ret, line, ID2SYM(rb_intern("delete")), INT2FIX(1));
 | |
| 	switch (nd_type(node->nd_body)) {
 | |
| 	  case NODE_LASGN:
 | |
| 	    idx = iseq->local_iseq->local_size - get_local_var_idx(iseq, id);
 | |
| 	    ADD_INSN2(ret, line, setlocal, INT2FIX(idx), INT2FIX(get_lvar_level(iseq)));
 | |
| 	    break;
 | |
| 	  case NODE_DASGN:
 | |
| 	  case NODE_DASGN_CURR:
 | |
| 	    idx = get_dyna_var_idx(iseq, id, &lv, &ls);
 | |
| 	    ADD_INSN2(ret, line, setlocal, INT2FIX(ls - idx), INT2FIX(lv));
 | |
| 	    break;
 | |
| 	  default:
 | |
| 	    rb_bug("iseq_compile_each (NODE_KW_ARG): unknown node: %s", ruby_node_name(nd_type(node->nd_body)));
 | |
| 	}
 | |
| 	if (node->nd_body->nd_value != (NODE *)-1) {
 | |
| 	    end_label = NEW_LABEL(nd_line(node));
 | |
| 	    ADD_INSNL(ret, nd_line(node), jump, end_label);
 | |
| 	}
 | |
| 	ADD_LABEL(ret, default_label);
 | |
| 	if (node->nd_body->nd_value != (NODE *)-1) {
 | |
| 	    COMPILE_POPED(ret, "keyword default argument", node->nd_body);
 | |
| 	    ADD_LABEL(ret, end_label);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_DSYM:{
 | |
| 	compile_dstr(iseq, ret, node);
 | |
| 	if (!poped) {
 | |
| 	    ADD_SEND(ret, line, ID2SYM(idIntern), INT2FIX(0));
 | |
| 	}
 | |
| 	else {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_ATTRASGN:{
 | |
| 	DECL_ANCHOR(recv);
 | |
| 	DECL_ANCHOR(args);
 | |
| 	VALUE flag = 0;
 | |
| 	VALUE argc;
 | |
| 
 | |
| 	INIT_ANCHOR(recv);
 | |
| 	INIT_ANCHOR(args);
 | |
| 	argc = setup_args(iseq, args, node->nd_args, &flag);
 | |
| 
 | |
| 	if (node->nd_recv == (NODE *) 1) {
 | |
| 	    flag |= VM_CALL_FCALL;
 | |
| 	    ADD_INSN(recv, line, putself);
 | |
| 	}
 | |
| 	else {
 | |
| 	    COMPILE(recv, "recv", node->nd_recv);
 | |
| 	}
 | |
| 
 | |
| 	debugp_param("argc", argc);
 | |
| 	debugp_param("nd_mid", ID2SYM(node->nd_mid));
 | |
| 
 | |
| 	if (!poped) {
 | |
| 	    ADD_INSN(ret, line, putnil);
 | |
| 	    ADD_SEQ(ret, recv);
 | |
| 	    ADD_SEQ(ret, args);
 | |
| 
 | |
| 	    if (flag & VM_CALL_ARGS_BLOCKARG) {
 | |
| 		ADD_INSN1(ret, line, topn, INT2FIX(1));
 | |
| 		if (flag & VM_CALL_ARGS_SPLAT) {
 | |
| 		    ADD_INSN1(ret, line, putobject, INT2FIX(-1));
 | |
| 		    ADD_SEND(ret, line, ID2SYM(idAREF), INT2FIX(1));
 | |
| 		}
 | |
| 		ADD_INSN1(ret, line, setn, FIXNUM_INC(argc, 3));
 | |
| 		ADD_INSN (ret, line, pop);
 | |
| 	    }
 | |
| 	    else if (flag & VM_CALL_ARGS_SPLAT) {
 | |
| 		ADD_INSN(ret, line, dup);
 | |
| 		ADD_INSN1(ret, line, putobject, INT2FIX(-1));
 | |
| 		ADD_SEND(ret, line, ID2SYM(idAREF), INT2FIX(1));
 | |
| 		ADD_INSN1(ret, line, setn, FIXNUM_INC(argc, 2));
 | |
| 		ADD_INSN (ret, line, pop);
 | |
| 	    }
 | |
| 	    else {
 | |
| 		ADD_INSN1(ret, line, setn, FIXNUM_INC(argc, 1));
 | |
| 	    }
 | |
| 	}
 | |
| 	else {
 | |
| 	    ADD_SEQ(ret, recv);
 | |
| 	    ADD_SEQ(ret, args);
 | |
| 	}
 | |
| 	ADD_SEND_R(ret, line, ID2SYM(node->nd_mid), argc, 0, LONG2FIX(flag));
 | |
| 	ADD_INSN(ret, line, pop);
 | |
| 
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_PRELUDE:{
 | |
| 	COMPILE_POPED(ret, "prelude", node->nd_head);
 | |
| 	COMPILE_(ret, "body", node->nd_body, poped);
 | |
| 	break;
 | |
|       }
 | |
|       case NODE_LAMBDA:{
 | |
| 	/* compile same as lambda{...} */
 | |
| 	VALUE block = NEW_CHILD_ISEQVAL(node->nd_body, make_name_for_block(iseq), ISEQ_TYPE_BLOCK, line);
 | |
| 	VALUE argc = INT2FIX(0);
 | |
| 	ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | |
| 	ADD_CALL_WITH_BLOCK(ret, line, ID2SYM(idLambda), argc, block);
 | |
| 
 | |
| 	if (poped) {
 | |
| 	    ADD_INSN(ret, line, pop);
 | |
| 	}
 | |
| 	break;
 | |
|       }
 | |
|       default:
 | |
| 	rb_bug("iseq_compile_each: unknown node: %s", ruby_node_name(type));
 | |
| 	return COMPILE_NG;
 | |
|     }
 | |
| 
 | |
|     /* check & remove redundant trace(line) */
 | |
|     if (saved_last_element && ret /* ret can be 0 when error */ &&
 | |
| 	ret->last == saved_last_element &&
 | |
| 	((INSN *)saved_last_element)->insn_id == BIN(trace)) {
 | |
| 	POP_ELEMENT(ret);
 | |
|     }
 | |
| 
 | |
|     debug_node_end();
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| /***************************/
 | |
| /* instruction information */
 | |
| /***************************/
 | |
| 
 | |
| static int
 | |
| insn_data_length(INSN *iobj)
 | |
| {
 | |
|     return insn_len(iobj->insn_id);
 | |
| }
 | |
| 
 | |
| static int
 | |
| calc_sp_depth(int depth, INSN *insn)
 | |
| {
 | |
|     return insn_stack_increase(depth, insn->insn_id, insn->operands);
 | |
| }
 | |
| 
 | |
| static int
 | |
| insn_data_line_no(INSN *iobj)
 | |
| {
 | |
|     return insn_len(iobj->line_no);
 | |
| }
 | |
| 
 | |
| static VALUE
 | |
| insn_data_to_s_detail(INSN *iobj)
 | |
| {
 | |
|     VALUE str = rb_sprintf("%-16s", insn_name(iobj->insn_id));
 | |
| 
 | |
|     if (iobj->operands) {
 | |
| 	const char *types = insn_op_types(iobj->insn_id);
 | |
| 	int j;
 | |
| 
 | |
| 	for (j = 0; types[j]; j++) {
 | |
| 	    char type = types[j];
 | |
| 	    printf("str: %"PRIxVALUE", type: %c\n", str, type);
 | |
| 
 | |
| 	    switch (type) {
 | |
| 	      case TS_OFFSET:	/* label(destination position) */
 | |
| 		{
 | |
| 		    LABEL *lobj = (LABEL *)OPERAND_AT(iobj, j);
 | |
| 		    rb_str_catf(str, "<L%03d>", lobj->label_no);
 | |
| 		    break;
 | |
| 		}
 | |
| 		break;
 | |
| 	      case TS_ISEQ:	/* iseq */
 | |
| 		{
 | |
| 		    rb_iseq_t *iseq = (rb_iseq_t *)OPERAND_AT(iobj, j);
 | |
| 		    VALUE val = Qnil;
 | |
| 		    if (0 && iseq) { /* TODO: invalidate now */
 | |
| 			val = iseq->self;
 | |
| 		    }
 | |
| 		    rb_str_concat(str, rb_inspect(val));
 | |
| 		}
 | |
| 		break;
 | |
| 	      case TS_LINDEX:
 | |
| 	      case TS_NUM:	/* ulong */
 | |
| 	      case TS_VALUE:	/* VALUE */
 | |
| 		{
 | |
| 		    VALUE v = OPERAND_AT(iobj, j);
 | |
| 		    rb_str_concat(str, rb_inspect(v));
 | |
| 		    break;
 | |
| 		}
 | |
| 	      case TS_ID:	/* ID */
 | |
| 		rb_str_concat(str, rb_inspect(OPERAND_AT(iobj, j)));
 | |
| 		break;
 | |
| 	      case TS_GENTRY:
 | |
| 		{
 | |
| 		    struct rb_global_entry *entry = (struct rb_global_entry *)
 | |
| 		      (OPERAND_AT(iobj, j) & (~1));
 | |
| 		    rb_str_cat2(str, rb_id2name(entry->id));
 | |
| 		    break;
 | |
| 		}
 | |
| 	      case TS_IC:	/* inline cache */
 | |
| 		rb_str_catf(str, "<ic:%d>", FIX2INT(OPERAND_AT(iobj, j)));
 | |
| 		break;
 | |
| 	      case TS_CALLINFO: /* call info */
 | |
| 		{
 | |
| 		    rb_call_info_t *ci = (rb_call_info_t *)OPERAND_AT(iobj, j);
 | |
| 		    rb_str_catf(str, "<callinfo:%s, %d>", ci->mid ? rb_id2name(ci->mid) : "", ci->orig_argc);
 | |
| 		    break;
 | |
| 		}
 | |
| 	      case TS_CDHASH:	/* case/when condition cache */
 | |
| 		rb_str_cat2(str, "<ch>");
 | |
| 		break;
 | |
| 	      default:{
 | |
| 		rb_raise(rb_eSyntaxError, "unknown operand type: %c", type);
 | |
| 	      }
 | |
| 	    }
 | |
| 	    if (types[j + 1]) {
 | |
| 		rb_str_cat2(str, ", ");
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     return str;
 | |
| }
 | |
| 
 | |
| static void
 | |
| dump_disasm_list(struct iseq_link_element *link)
 | |
| {
 | |
|     int pos = 0;
 | |
|     INSN *iobj;
 | |
|     LABEL *lobj;
 | |
|     VALUE str;
 | |
| 
 | |
|     printf("-- raw disasm--------\n");
 | |
| 
 | |
|     while (link) {
 | |
| 	switch (link->type) {
 | |
| 	  case ISEQ_ELEMENT_INSN:
 | |
| 	    {
 | |
| 		iobj = (INSN *)link;
 | |
| 		str = insn_data_to_s_detail(iobj);
 | |
| 		printf("%04d %-65s(%4d)\n", pos, StringValueCStr(str), insn_data_line_no(iobj));
 | |
| 		pos += insn_data_length(iobj);
 | |
| 		break;
 | |
| 	    }
 | |
| 	  case ISEQ_ELEMENT_LABEL:
 | |
| 	    {
 | |
| 		lobj = (LABEL *)link;
 | |
| 		printf("<L%03d>\n", lobj->label_no);
 | |
| 		break;
 | |
| 	    }
 | |
| 	  case ISEQ_ELEMENT_NONE:
 | |
| 	    {
 | |
| 		printf("[none]\n");
 | |
| 		break;
 | |
| 	    }
 | |
| 	  case ISEQ_ELEMENT_ADJUST:
 | |
| 	    {
 | |
| 		ADJUST *adjust = (ADJUST *)link;
 | |
| 		printf("adjust: [label: %d]\n", adjust->label->label_no);
 | |
| 		break;
 | |
| 	    }
 | |
| 	  default:
 | |
| 	    /* ignore */
 | |
| 	    rb_raise(rb_eSyntaxError, "dump_disasm_list error: %ld\n", FIX2LONG(link->type));
 | |
| 	}
 | |
| 	link = link->next;
 | |
|     }
 | |
|     printf("---------------------\n");
 | |
| }
 | |
| 
 | |
| const char *
 | |
| rb_insns_name(int i)
 | |
| {
 | |
|     return insn_name_info[i];
 | |
| }
 | |
| 
 | |
| VALUE
 | |
| rb_insns_name_array(void)
 | |
| {
 | |
|     VALUE ary = rb_ary_new();
 | |
|     int i;
 | |
|     for (i = 0; i < numberof(insn_name_info); i++) {
 | |
| 	rb_ary_push(ary, rb_obj_freeze(rb_str_new2(insn_name_info[i])));
 | |
|     }
 | |
|     return rb_obj_freeze(ary);
 | |
| }
 | |
| 
 | |
| static LABEL *
 | |
| register_label(rb_iseq_t *iseq, struct st_table *labels_table, VALUE obj)
 | |
| {
 | |
|     LABEL *label = 0;
 | |
|     st_data_t tmp;
 | |
|     obj = rb_convert_type(obj, T_SYMBOL, "Symbol", "to_sym");
 | |
| 
 | |
|     if (st_lookup(labels_table, obj, &tmp) == 0) {
 | |
| 	label = NEW_LABEL(0);
 | |
| 	st_insert(labels_table, obj, (st_data_t)label);
 | |
|     }
 | |
|     else {
 | |
| 	label = (LABEL *)tmp;
 | |
|     }
 | |
|     return label;
 | |
| }
 | |
| 
 | |
| static VALUE
 | |
| get_exception_sym2type(VALUE sym)
 | |
| {
 | |
| #undef rb_intern
 | |
| #define rb_intern(str) rb_intern_const(str)
 | |
|     VALUE sym_inspect;
 | |
|     static VALUE symRescue, symEnsure, symRetry;
 | |
|     static VALUE symBreak, symRedo, symNext;
 | |
| 
 | |
|     if (symRescue == 0) {
 | |
| 	symRescue = ID2SYM(rb_intern("rescue"));
 | |
| 	symEnsure = ID2SYM(rb_intern("ensure"));
 | |
| 	symRetry  = ID2SYM(rb_intern("retry"));
 | |
| 	symBreak  = ID2SYM(rb_intern("break"));
 | |
| 	symRedo   = ID2SYM(rb_intern("redo"));
 | |
| 	symNext   = ID2SYM(rb_intern("next"));
 | |
|     }
 | |
| 
 | |
|     if (sym == symRescue) return CATCH_TYPE_RESCUE;
 | |
|     if (sym == symEnsure) return CATCH_TYPE_ENSURE;
 | |
|     if (sym == symRetry)  return CATCH_TYPE_RETRY;
 | |
|     if (sym == symBreak)  return CATCH_TYPE_BREAK;
 | |
|     if (sym == symRedo)   return CATCH_TYPE_REDO;
 | |
|     if (sym == symNext)   return CATCH_TYPE_NEXT;
 | |
|     sym_inspect = rb_inspect(sym);
 | |
|     rb_raise(rb_eSyntaxError, "invalid exception symbol: %s",
 | |
| 	     StringValuePtr(sym_inspect));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| iseq_build_from_ary_exception(rb_iseq_t *iseq, struct st_table *labels_table,
 | |
| 		     VALUE exception)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     for (i=0; i<RARRAY_LEN(exception); i++) {
 | |
| 	VALUE v, type, *ptr, eiseqval;
 | |
| 	LABEL *lstart, *lend, *lcont;
 | |
| 	int sp;
 | |
| 
 | |
| 	RB_GC_GUARD(v) = rb_convert_type(RARRAY_AREF(exception, i), T_ARRAY,
 | |
| 					 "Array", "to_ary");
 | |
| 	if (RARRAY_LEN(v) != 6) {
 | |
| 	    rb_raise(rb_eSyntaxError, "wrong exception entry");
 | |
| 	}
 | |
| 	ptr  = RARRAY_PTR(v);
 | |
| 	type = get_exception_sym2type(ptr[0]);
 | |
| 	if (ptr[1] == Qnil) {
 | |
| 	    eiseqval = 0;
 | |
| 	}
 | |
| 	else {
 | |
| 	    eiseqval = rb_iseq_load(ptr[1], iseq->self, Qnil);
 | |
| 	}
 | |
| 
 | |
| 	lstart = register_label(iseq, labels_table, ptr[2]);
 | |
| 	lend   = register_label(iseq, labels_table, ptr[3]);
 | |
| 	lcont  = register_label(iseq, labels_table, ptr[4]);
 | |
| 	sp     = NUM2INT(ptr[5]);
 | |
| 
 | |
| 	(void)sp;
 | |
| 
 | |
| 	ADD_CATCH_ENTRY(type, lstart, lend, eiseqval, lcont);
 | |
|     }
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| static struct st_table *
 | |
| insn_make_insn_table(void)
 | |
| {
 | |
|     struct st_table *table;
 | |
|     int i;
 | |
|     table = st_init_numtable();
 | |
| 
 | |
|     for (i=0; i<VM_INSTRUCTION_SIZE; i++) {
 | |
| 	st_insert(table, ID2SYM(rb_intern(insn_name(i))), i);
 | |
|     }
 | |
| 
 | |
|     return table;
 | |
| }
 | |
| 
 | |
| static VALUE
 | |
| iseq_build_load_iseq(rb_iseq_t *iseq, VALUE op)
 | |
| {
 | |
|     VALUE iseqval;
 | |
|     if (RB_TYPE_P(op, T_ARRAY)) {
 | |
| 	iseqval = rb_iseq_load(op, iseq->self, Qnil);
 | |
|     }
 | |
|     else if (CLASS_OF(op) == rb_cISeq) {
 | |
| 	iseqval = op;
 | |
|     }
 | |
|     else {
 | |
| 	rb_raise(rb_eSyntaxError, "ISEQ is required");
 | |
|     }
 | |
|     iseq_add_mark_object(iseq, iseqval);
 | |
|     return iseqval;
 | |
| }
 | |
| 
 | |
| static int
 | |
| iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *anchor,
 | |
| 		VALUE body, struct st_table *labels_table)
 | |
| {
 | |
|     /* TODO: body should be frozen */
 | |
|     VALUE *ptr = RARRAY_PTR(body);
 | |
|     long i, len = RARRAY_LEN(body);
 | |
|     int j;
 | |
|     int line_no = 0;
 | |
| 
 | |
|     /*
 | |
|      * index -> LABEL *label
 | |
|      */
 | |
|     static struct st_table *insn_table;
 | |
| 
 | |
|     if (insn_table == 0) {
 | |
| 	insn_table = insn_make_insn_table();
 | |
|     }
 | |
| 
 | |
|     for (i=0; i<len; i++) {
 | |
| 	VALUE obj = ptr[i];
 | |
| 
 | |
| 	if (SYMBOL_P(obj)) {
 | |
| 	    LABEL *label = register_label(iseq, labels_table, obj);
 | |
| 	    ADD_LABEL(anchor, label);
 | |
| 	}
 | |
| 	else if (FIXNUM_P(obj)) {
 | |
| 	    line_no = NUM2INT(obj);
 | |
| 	}
 | |
| 	else if (RB_TYPE_P(obj, T_ARRAY)) {
 | |
| 	    VALUE *argv = 0;
 | |
| 	    int argc = RARRAY_LENINT(obj) - 1;
 | |
| 	    st_data_t insn_id;
 | |
| 	    VALUE insn;
 | |
| 
 | |
| 	    insn = (argc < 0) ? Qnil : RARRAY_AREF(obj, 0);
 | |
| 	    if (st_lookup(insn_table, (st_data_t)insn, &insn_id) == 0) {
 | |
| 		/* TODO: exception */
 | |
| 		RB_GC_GUARD(insn) = rb_inspect(insn);
 | |
| 		rb_compile_error(RSTRING_PTR(iseq->location.path), line_no,
 | |
| 				 "unknown instruction: %s", RSTRING_PTR(insn));
 | |
| 	    }
 | |
| 
 | |
| 	    if (argc != insn_len((VALUE)insn_id)-1) {
 | |
| 		rb_compile_error(RSTRING_PTR(iseq->location.path), line_no,
 | |
| 				 "operand size mismatch");
 | |
| 	    }
 | |
| 
 | |
| 	    if (argc > 0) {
 | |
| 		argv = compile_data_alloc(iseq, sizeof(VALUE) * argc);
 | |
| 		for (j=0; j<argc; j++) {
 | |
| 		    VALUE op = rb_ary_entry(obj, j+1);
 | |
| 		    switch (insn_op_type((VALUE)insn_id, j)) {
 | |
| 		      case TS_OFFSET: {
 | |
| 			LABEL *label = register_label(iseq, labels_table, op);
 | |
| 			argv[j] = (VALUE)label;
 | |
| 			break;
 | |
| 		      }
 | |
| 		      case TS_LINDEX:
 | |
| 		      case TS_NUM:
 | |
| 			(void)NUM2INT(op);
 | |
| 			argv[j] = op;
 | |
| 			break;
 | |
| 		      case TS_VALUE:
 | |
| 			argv[j] = op;
 | |
| 			iseq_add_mark_object(iseq, op);
 | |
| 			break;
 | |
| 		      case TS_ISEQ:
 | |
| 			{
 | |
| 			    if (op != Qnil) {
 | |
| 				argv[j] = iseq_build_load_iseq(iseq, op);
 | |
| 			    }
 | |
| 			    else {
 | |
| 				argv[j] = 0;
 | |
| 			    }
 | |
| 			}
 | |
| 			break;
 | |
| 		      case TS_GENTRY:
 | |
| 			op = rb_convert_type(op, T_SYMBOL, "Symbol", "to_sym");
 | |
| 			argv[j] = (VALUE)rb_global_entry(SYM2ID(op));
 | |
| 			break;
 | |
| 		      case TS_IC:
 | |
| 			argv[j] = op;
 | |
| 			if (NUM2INT(op) >= iseq->is_size) {
 | |
| 			    iseq->is_size = NUM2INT(op) + 1;
 | |
| 			}
 | |
| 			break;
 | |
| 		      case TS_CALLINFO:
 | |
| 			{
 | |
| 			    ID mid = 0;
 | |
| 			    int orig_argc = 0;
 | |
| 			    VALUE block = 0;
 | |
| 			    unsigned long flag = 0;
 | |
| 
 | |
| 			    if (!NIL_P(op)) {
 | |
| 				VALUE vmid = rb_hash_aref(op, ID2SYM(rb_intern("mid")));
 | |
| 				VALUE vflag = rb_hash_aref(op, ID2SYM(rb_intern("flag")));
 | |
| 				VALUE vorig_argc = rb_hash_aref(op, ID2SYM(rb_intern("orig_argc")));
 | |
| 				VALUE vblock = rb_hash_aref(op, ID2SYM(rb_intern("block")));
 | |
| 
 | |
| 				if (!NIL_P(vmid)) mid = SYM2ID(vmid);
 | |
| 				if (!NIL_P(vflag)) flag = NUM2ULONG(vflag);
 | |
| 				if (!NIL_P(vorig_argc)) orig_argc = FIX2INT(vorig_argc);
 | |
| 				if (!NIL_P(vblock)) block = iseq_build_load_iseq(iseq, vblock);
 | |
| 			    }
 | |
| 			    argv[j] = (VALUE)new_callinfo(iseq, mid, orig_argc, block, flag);
 | |
| 			}
 | |
| 			break;
 | |
| 		      case TS_ID:
 | |
| 			argv[j] = rb_convert_type(op, T_SYMBOL,
 | |
| 						  "Symbol", "to_sym");
 | |
| 			break;
 | |
| 		      case TS_CDHASH:
 | |
| 			{
 | |
| 			    int i;
 | |
| 			    op = rb_convert_type(op, T_ARRAY, "Array", "to_ary");
 | |
| 			    op = rb_ary_dup(op);
 | |
| 			    for (i=0; i<RARRAY_LEN(op); i+=2) {
 | |
| 				VALUE sym = rb_ary_entry(op, i+1);
 | |
| 				LABEL *label =
 | |
| 				  register_label(iseq, labels_table, sym);
 | |
| 				rb_ary_store(op, i+1, (VALUE)label | 1);
 | |
| 			    }
 | |
| 			    argv[j] = op;
 | |
| 			    iseq_add_mark_object_compile_time(iseq, op);
 | |
| 			}
 | |
| 			break;
 | |
| 		      default:
 | |
| 			rb_raise(rb_eSyntaxError, "unknown operand: %c", insn_op_type((VALUE)insn_id, j));
 | |
| 		    }
 | |
| 		}
 | |
| 	    }
 | |
| 	    ADD_ELEM(anchor,
 | |
| 		     (LINK_ELEMENT*)new_insn_core(iseq, line_no,
 | |
| 						  (enum ruby_vminsn_type)insn_id, argc, argv));
 | |
| 	}
 | |
| 	else {
 | |
| 	    rb_raise(rb_eTypeError, "unexpected object for instruction");
 | |
| 	}
 | |
|     }
 | |
|     validate_labels(iseq, labels_table);
 | |
|     st_free_table(labels_table);
 | |
|     iseq_setup(iseq, anchor);
 | |
|     return COMPILE_OK;
 | |
| }
 | |
| 
 | |
| #define CHECK_ARRAY(v)   rb_convert_type((v), T_ARRAY, "Array", "to_ary")
 | |
| #define CHECK_STRING(v)  rb_convert_type((v), T_STRING, "String", "to_str")
 | |
| #define CHECK_SYMBOL(v)  rb_convert_type((v), T_SYMBOL, "Symbol", "to_sym")
 | |
| static inline VALUE CHECK_INTEGER(VALUE v) {(void)NUM2LONG(v); return v;}
 | |
| 
 | |
| VALUE
 | |
| rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE locals, VALUE args,
 | |
| 			 VALUE exception, VALUE body)
 | |
| {
 | |
|     int i;
 | |
|     ID *tbl;
 | |
|     struct st_table *labels_table = st_init_numtable();
 | |
|     DECL_ANCHOR(anchor);
 | |
|     INIT_ANCHOR(anchor);
 | |
| 
 | |
|     iseq->local_table_size = RARRAY_LENINT(locals);
 | |
|     iseq->local_table = tbl = (ID *)ALLOC_N(ID, iseq->local_table_size);
 | |
|     iseq->local_size = iseq->local_table_size + 1;
 | |
| 
 | |
|     for (i=0; i<RARRAY_LEN(locals); i++) {
 | |
| 	VALUE lv = RARRAY_AREF(locals, i);
 | |
| 	tbl[i] = FIXNUM_P(lv) ? (ID)FIX2LONG(lv) : SYM2ID(CHECK_SYMBOL(lv));
 | |
|     }
 | |
| 
 | |
|     /* args */
 | |
|     if (FIXNUM_P(args)) {
 | |
| 	iseq->arg_size = iseq->argc = FIX2INT(args);
 | |
| 	iseq->arg_simple = 1;
 | |
|     }
 | |
|     else {
 | |
| 	int i = 0;
 | |
| 	VALUE argc = CHECK_INTEGER(rb_ary_entry(args, i++));
 | |
| 	VALUE arg_opt_labels = CHECK_ARRAY(rb_ary_entry(args, i++));
 | |
| 	VALUE arg_post_len = CHECK_INTEGER(rb_ary_entry(args, i++));
 | |
| 	VALUE arg_post_start = CHECK_INTEGER(rb_ary_entry(args, i++));
 | |
| 	VALUE arg_rest = CHECK_INTEGER(rb_ary_entry(args, i++));
 | |
| 	VALUE arg_block = CHECK_INTEGER(rb_ary_entry(args, i++));
 | |
| 	VALUE arg_simple = CHECK_INTEGER(rb_ary_entry(args, i++));
 | |
| 
 | |
| 	iseq->argc = FIX2INT(argc);
 | |
| 	iseq->arg_rest = FIX2INT(arg_rest);
 | |
| 	iseq->arg_post_len = FIX2INT(arg_post_len);
 | |
| 	iseq->arg_post_start = FIX2INT(arg_post_start);
 | |
| 	iseq->arg_block = FIX2INT(arg_block);
 | |
| 	iseq->arg_opts = RARRAY_LENINT(arg_opt_labels);
 | |
| 	iseq->arg_opt_table = (VALUE *)ALLOC_N(VALUE, iseq->arg_opts);
 | |
| 
 | |
| 	if (iseq->arg_block != -1) {
 | |
| 	    iseq->arg_size = iseq->arg_block + 1;
 | |
| 	}
 | |
| 	else if (iseq->arg_post_len) {
 | |
| 	    iseq->arg_size = iseq->arg_post_start + iseq->arg_post_len;
 | |
| 	}
 | |
| 	else if (iseq->arg_rest != -1) {
 | |
| 	    iseq->arg_size = iseq->arg_rest + 1;
 | |
| 	}
 | |
| 	else {
 | |
| 	    iseq->arg_size = iseq->argc + (iseq->arg_opts ? iseq->arg_opts - 1 : 0);
 | |
| 	}
 | |
| 
 | |
| 	for (i=0; i<RARRAY_LEN(arg_opt_labels); i++) {
 | |
| 	    iseq->arg_opt_table[i] =
 | |
| 	      (VALUE)register_label(iseq, labels_table,
 | |
| 				    rb_ary_entry(arg_opt_labels, i));
 | |
| 	}
 | |
| 
 | |
| 	iseq->arg_simple = NUM2INT(arg_simple);
 | |
|     }
 | |
| 
 | |
|     /* exception */
 | |
|     iseq_build_from_ary_exception(iseq, labels_table, exception);
 | |
| 
 | |
|     /* body */
 | |
|     iseq_build_from_ary_body(iseq, anchor, body, labels_table);
 | |
|     return iseq->self;
 | |
| }
 | |
| 
 | |
| /* for parser */
 | |
| 
 | |
| int
 | |
| rb_dvar_defined(ID id)
 | |
| {
 | |
|     rb_thread_t *th = GET_THREAD();
 | |
|     rb_iseq_t *iseq;
 | |
|     if (th->base_block && (iseq = th->base_block->iseq)) {
 | |
| 	while (iseq->type == ISEQ_TYPE_BLOCK ||
 | |
| 	       iseq->type == ISEQ_TYPE_RESCUE ||
 | |
| 	       iseq->type == ISEQ_TYPE_ENSURE ||
 | |
| 	       iseq->type == ISEQ_TYPE_EVAL ||
 | |
| 	       iseq->type == ISEQ_TYPE_MAIN
 | |
| 	       ) {
 | |
| 	    int i;
 | |
| 
 | |
| 	    for (i = 0; i < iseq->local_table_size; i++) {
 | |
| 		if (iseq->local_table[i] == id) {
 | |
| 		    return 1;
 | |
| 		}
 | |
| 	    }
 | |
| 	    iseq = iseq->parent_iseq;
 | |
| 	}
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| rb_local_defined(ID id)
 | |
| {
 | |
|     rb_thread_t *th = GET_THREAD();
 | |
|     rb_iseq_t *iseq;
 | |
| 
 | |
|     if (th->base_block && th->base_block->iseq) {
 | |
| 	int i;
 | |
| 	iseq = th->base_block->iseq->local_iseq;
 | |
| 
 | |
| 	for (i=0; i<iseq->local_table_size; i++) {
 | |
| 	    if (iseq->local_table[i] == id) {
 | |
| 		return 1;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| rb_parse_in_eval(void)
 | |
| {
 | |
|     return GET_THREAD()->parse_in_eval > 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| rb_parse_in_main(void)
 | |
| {
 | |
|     return GET_THREAD()->parse_in_eval < 0;
 | |
| }
 |