mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	introduce new enum for it. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61946 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
		
			
				
	
	
		
			9573 lines
		
	
	
	
		
			252 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			9573 lines
		
	
	
	
		
			252 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/encoding.h"
 | 
						|
#include "ruby/re.h"
 | 
						|
#include "internal.h"
 | 
						|
#include "encindex.h"
 | 
						|
#include <math.h>
 | 
						|
 | 
						|
#include "vm_core.h"
 | 
						|
#include "vm_debug.h"
 | 
						|
#include "iseq.h"
 | 
						|
#include "insns.inc"
 | 
						|
#include "insns_info.inc"
 | 
						|
#include "id_table.h"
 | 
						|
#include "gc.h"
 | 
						|
 | 
						|
#ifdef HAVE_DLADDR
 | 
						|
# include <dlfcn.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#undef RUBY_UNTYPED_DATA_WARNING
 | 
						|
#define RUBY_UNTYPED_DATA_WARNING 0
 | 
						|
 | 
						|
#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_ANCHOR,
 | 
						|
	ISEQ_ELEMENT_LABEL,
 | 
						|
	ISEQ_ELEMENT_INSN,
 | 
						|
	ISEQ_ELEMENT_ADJUST,
 | 
						|
	ISEQ_ELEMENT_TRACE
 | 
						|
    } 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 enum {
 | 
						|
    LABEL_RESCUE_NONE,
 | 
						|
    LABEL_RESCUE_BEG,
 | 
						|
    LABEL_RESCUE_END,
 | 
						|
    LABEL_RESCUE_TYPE_MAX
 | 
						|
} LABEL_RESCUE_TYPE;
 | 
						|
 | 
						|
typedef struct iseq_label_data {
 | 
						|
    LINK_ELEMENT link;
 | 
						|
    int label_no;
 | 
						|
    int position;
 | 
						|
    int sc_state;
 | 
						|
    int sp;
 | 
						|
    int refcnt;
 | 
						|
    unsigned int set: 1;
 | 
						|
    unsigned int rescued: 2;
 | 
						|
    unsigned int unremovable: 1;
 | 
						|
} LABEL;
 | 
						|
 | 
						|
typedef struct iseq_insn_data {
 | 
						|
    LINK_ELEMENT link;
 | 
						|
    enum ruby_vminsn_type insn_id;
 | 
						|
    int operand_size;
 | 
						|
    int sc_state;
 | 
						|
    VALUE *operands;
 | 
						|
    struct {
 | 
						|
	int line_no;
 | 
						|
	rb_event_flag_t events;
 | 
						|
    } insn_info;
 | 
						|
} INSN;
 | 
						|
 | 
						|
typedef struct iseq_adjust_data {
 | 
						|
    LINK_ELEMENT link;
 | 
						|
    LABEL *label;
 | 
						|
    int line_no;
 | 
						|
} ADJUST;
 | 
						|
 | 
						|
typedef struct iseq_trace_data {
 | 
						|
    LINK_ELEMENT link;
 | 
						|
    rb_event_flag_t event;
 | 
						|
} TRACE;
 | 
						|
 | 
						|
struct ensure_range {
 | 
						|
    LABEL *begin;
 | 
						|
    LABEL *end;
 | 
						|
    struct ensure_range *next;
 | 
						|
};
 | 
						|
 | 
						|
struct iseq_compile_data_ensure_node_stack {
 | 
						|
    const 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(iseq)->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, "", (const NODE *)(node)), gl_node_level)), \
 | 
						|
   gl_node_level++)
 | 
						|
 | 
						|
#define debug_node_end()  gl_node_level --
 | 
						|
 | 
						|
#else
 | 
						|
 | 
						|
#define debugi(header, id)                 ((void)0)
 | 
						|
#define debugp(header, value)              ((void)0)
 | 
						|
#define debugp_verbose(header, value)      ((void)0)
 | 
						|
#define debugp_verbose_node(header, value) ((void)0)
 | 
						|
#define debugp_param(header, value)        ((void)0)
 | 
						|
#define debug_node_start(node)             ((void)0)
 | 
						|
#define debug_node_end()                   ((void)0)
 | 
						|
#endif
 | 
						|
 | 
						|
#if CPDEBUG > 1 || CPDEBUG < 0
 | 
						|
#define printf ruby_debug_printf
 | 
						|
#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
 | 
						|
 | 
						|
#define LVAR_ERRINFO (1)
 | 
						|
 | 
						|
/* create new label */
 | 
						|
#define NEW_LABEL(l) new_label_body(iseq, (l))
 | 
						|
#define LABEL_FORMAT "<L%03d>"
 | 
						|
 | 
						|
#define NEW_ISEQ(node, name, type, line_no) \
 | 
						|
  new_child_iseq(iseq, (node), rb_fstring(name), 0, (type), (line_no))
 | 
						|
 | 
						|
#define NEW_CHILD_ISEQ(node, name, type, line_no) \
 | 
						|
  new_child_iseq(iseq, (node), rb_fstring(name), iseq, (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))
 | 
						|
 | 
						|
/* insert an instruction before prev */
 | 
						|
#define INSERT_BEFORE_INSN(prev, line, insn) \
 | 
						|
  ELEM_INSERT_PREV(&(prev)->link, (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)))
 | 
						|
 | 
						|
/* insert an instruction with some operands (1, 2, 3, 5) before prev */
 | 
						|
#define INSERT_BEFORE_INSN1(prev, line, insn, op1) \
 | 
						|
  ELEM_INSERT_PREV(&(prev)->link, (LINK_ELEMENT *) \
 | 
						|
           new_insn_body(iseq, (line), BIN(insn), 1, (VALUE)(op1)))
 | 
						|
 | 
						|
#define LABEL_REF(label) ((label)->refcnt++)
 | 
						|
 | 
						|
/* add an instruction with label operand (alias of ADD_INSN1) */
 | 
						|
#define ADD_INSNL(seq, line, insn, label) (ADD_INSN1(seq, line, insn, label), LABEL_REF(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), NULL, (VALUE)INT2FIX(0), NULL)
 | 
						|
 | 
						|
#define ADD_SEND_WITH_FLAG(seq, line, id, argc, flag) \
 | 
						|
  ADD_SEND_R((seq), (line), (id), (argc), NULL, (VALUE)(flag), NULL)
 | 
						|
 | 
						|
#define ADD_SEND_WITH_BLOCK(seq, line, id, argc, block) \
 | 
						|
  ADD_SEND_R((seq), (line), (id), (argc), (block), (VALUE)INT2FIX(0), NULL)
 | 
						|
 | 
						|
#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), NULL, (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
 | 
						|
 | 
						|
#define ADD_CALL_WITH_BLOCK(seq, line, id, argc, block) \
 | 
						|
  ADD_SEND_R((seq), (line), (id), (argc), (block), (VALUE)INT2FIX(VM_CALL_FCALL), NULL)
 | 
						|
 | 
						|
#define ADD_SEND_R(seq, line, id, argc, block, flag, keywords) \
 | 
						|
  ADD_ELEM((seq), (LINK_ELEMENT *) new_insn_send(iseq, (line), (id), (VALUE)(argc), (block), (VALUE)(flag), (keywords)))
 | 
						|
 | 
						|
#define ADD_TRACE(seq, event) \
 | 
						|
  ADD_ELEM((seq), (LINK_ELEMENT *)new_trace_body(iseq, (event)))
 | 
						|
#define ADD_TRACE_LINE_COVERAGE(seq, line) \
 | 
						|
  do { \
 | 
						|
      if (ISEQ_COVERAGE(iseq) && \
 | 
						|
	  ISEQ_LINE_COVERAGE(iseq) && \
 | 
						|
	  (line) > 0) { \
 | 
						|
	  RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), (line) - 1, INT2FIX(0)); \
 | 
						|
	  ADD_INSN2((seq), (line), tracecoverage, INT2FIX(RUBY_EVENT_COVERAGE_LINE), INT2FIX(line)); \
 | 
						|
      } \
 | 
						|
  } while (0)
 | 
						|
 | 
						|
 | 
						|
#define DECL_BRANCH_BASE(branches, first_line, first_column, last_line, last_column, type) \
 | 
						|
  do { \
 | 
						|
      if (ISEQ_COVERAGE(iseq) && \
 | 
						|
	  ISEQ_BRANCH_COVERAGE(iseq) && \
 | 
						|
	  (first_line) > 0) { \
 | 
						|
	  VALUE structure = RARRAY_AREF(ISEQ_BRANCH_COVERAGE(iseq), 0); \
 | 
						|
	  branches = rb_ary_tmp_new(0); \
 | 
						|
	  rb_ary_push(structure, branches); \
 | 
						|
	  rb_ary_push(branches, ID2SYM(rb_intern(type))); \
 | 
						|
	  rb_ary_push(branches, INT2FIX(first_line)); \
 | 
						|
	  rb_ary_push(branches, INT2FIX(first_column)); \
 | 
						|
	  rb_ary_push(branches, INT2FIX(last_line)); \
 | 
						|
	  rb_ary_push(branches, INT2FIX(last_column)); \
 | 
						|
      } \
 | 
						|
  } while (0)
 | 
						|
#define ADD_TRACE_BRANCH_COVERAGE(seq, first_line, first_column, last_line, last_column, type, branches) \
 | 
						|
  do { \
 | 
						|
      if (ISEQ_COVERAGE(iseq) && \
 | 
						|
	  ISEQ_BRANCH_COVERAGE(iseq) && \
 | 
						|
	  (first_line) > 0) { \
 | 
						|
	  VALUE counters = RARRAY_AREF(ISEQ_BRANCH_COVERAGE(iseq), 1); \
 | 
						|
	  long counter_idx = RARRAY_LEN(counters); \
 | 
						|
	  rb_ary_push(counters, INT2FIX(0)); \
 | 
						|
	  rb_ary_push(branches, ID2SYM(rb_intern(type))); \
 | 
						|
	  rb_ary_push(branches, INT2FIX(first_line)); \
 | 
						|
	  rb_ary_push(branches, INT2FIX(first_column)); \
 | 
						|
	  rb_ary_push(branches, INT2FIX(last_line)); \
 | 
						|
	  rb_ary_push(branches, INT2FIX(last_column)); \
 | 
						|
	  rb_ary_push(branches, INT2FIX(counter_idx)); \
 | 
						|
	  ADD_INSN2((seq), (first_line), tracecoverage, INT2FIX(RUBY_EVENT_COVERAGE_BRANCH), INT2FIX(counter_idx)); \
 | 
						|
      } \
 | 
						|
  } while (0)
 | 
						|
 | 
						|
static void iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, int level);
 | 
						|
static void iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, int level);
 | 
						|
 | 
						|
#define ADD_GETLOCAL(seq, line, idx, level) iseq_add_getlocal(iseq, (seq), (line), (idx), (level))
 | 
						|
#define ADD_SETLOCAL(seq, line, idx, level) iseq_add_setlocal(iseq, (seq), (line), (idx), (level))
 | 
						|
 | 
						|
/* 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 LABEL_UNREMOVABLE(label) \
 | 
						|
    ((label) ? (LABEL_REF(label), (label)->unremovable=1) : 0)
 | 
						|
#define ADD_CATCH_ENTRY(type, ls, le, iseqv, lc) do {				\
 | 
						|
    VALUE _e = rb_ary_new3(5, (type),						\
 | 
						|
			   (VALUE)(ls) | 1, (VALUE)(le) | 1,			\
 | 
						|
			   (VALUE)(iseqv), (VALUE)(lc) | 1);			\
 | 
						|
    LABEL_UNREMOVABLE(ls);							\
 | 
						|
    LABEL_REF(le);								\
 | 
						|
    LABEL_REF(lc);								\
 | 
						|
    rb_ary_push(ISEQ_COMPILE_DATA(iseq)->catch_table_ary, freeze_hide_obj(_e));	\
 | 
						|
} while (0)
 | 
						|
 | 
						|
/* 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_POPPED(anchor, desc, node)    \
 | 
						|
  (debug_compile("== " desc "\n", \
 | 
						|
                 iseq_compile_each(iseq, (anchor), (node), 1)))
 | 
						|
 | 
						|
/* compile node, which is popped when 'popped' is true */
 | 
						|
#define COMPILE_(anchor, desc, node, popped)  \
 | 
						|
  (debug_compile("== " desc "\n", \
 | 
						|
                 iseq_compile_each(iseq, (anchor), (node), (popped))))
 | 
						|
 | 
						|
#define COMPILE_RECV(anchor, desc, node) \
 | 
						|
    (private_recv_p(node) ? \
 | 
						|
     (ADD_INSN(anchor, nd_line(node), putself), VM_CALL_FCALL) : \
 | 
						|
     (COMPILE(anchor, desc, node->nd_recv), 0))
 | 
						|
 | 
						|
#define OPERAND_AT(insn, idx) \
 | 
						|
  (((INSN*)(insn))->operands[(idx)])
 | 
						|
 | 
						|
#define INSN_OF(insn) \
 | 
						|
  (((INSN*)(insn))->insn_id)
 | 
						|
 | 
						|
#define IS_INSN(link) ((link)->type == ISEQ_ELEMENT_INSN)
 | 
						|
#define IS_LABEL(link) ((link)->type == ISEQ_ELEMENT_LABEL)
 | 
						|
#define IS_ADJUST(link) ((link)->type == ISEQ_ELEMENT_ADJUST)
 | 
						|
#define IS_TRACE(link) ((link)->type == ISEQ_ELEMENT_TRACE)
 | 
						|
#define IS_INSN_ID(iobj, insn) (INSN_OF(iobj) == BIN(insn))
 | 
						|
#define IS_NEXT_INSN_ID(link, insn) \
 | 
						|
    ((link)->next && IS_INSN((link)->next) && IS_INSN_ID((link)->next, insn))
 | 
						|
 | 
						|
/* error */
 | 
						|
#if CPDEBUG > 0
 | 
						|
NORETURN(static void append_compile_error(rb_iseq_t *iseq, int line, const char *fmt, ...));
 | 
						|
#endif
 | 
						|
 | 
						|
static void
 | 
						|
append_compile_error(rb_iseq_t *iseq, int line, const char *fmt, ...)
 | 
						|
{
 | 
						|
    VALUE err_info = ISEQ_COMPILE_DATA(iseq)->err_info;
 | 
						|
    VALUE file = rb_iseq_path(iseq);
 | 
						|
    VALUE err = err_info == Qtrue ? Qfalse : err_info;
 | 
						|
    va_list args;
 | 
						|
 | 
						|
    va_start(args, fmt);
 | 
						|
    err = rb_syntax_error_append(err, file, line, -1, NULL, fmt, args);
 | 
						|
    va_end(args);
 | 
						|
    if (NIL_P(err_info)) {
 | 
						|
	RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, err);
 | 
						|
	rb_set_errinfo(err);
 | 
						|
    }
 | 
						|
    else if (!err_info) {
 | 
						|
	RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, Qtrue);
 | 
						|
    }
 | 
						|
    if (compile_debug) rb_exc_fatal(err);
 | 
						|
}
 | 
						|
 | 
						|
#if 0
 | 
						|
static void
 | 
						|
compile_bug(rb_iseq_t *iseq, int line, const char *fmt, ...)
 | 
						|
{
 | 
						|
    va_list args;
 | 
						|
    va_start(args, fmt);
 | 
						|
    rb_report_bug_valist(rb_iseq_path(iseq), line, fmt, args);
 | 
						|
    va_end(args);
 | 
						|
    abort();
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
#define COMPILE_ERROR append_compile_error
 | 
						|
 | 
						|
#define ERROR_ARGS_AT(n) iseq, nd_line(n),
 | 
						|
#define ERROR_ARGS ERROR_ARGS_AT(node)
 | 
						|
 | 
						|
#define EXPECT_NODE(prefix, node, ndtype, errval) \
 | 
						|
do { \
 | 
						|
    const NODE *error_node = (node); \
 | 
						|
    enum node_type error_type = nd_type(error_node); \
 | 
						|
    if (error_type != (ndtype)) { \
 | 
						|
	COMPILE_ERROR(ERROR_ARGS_AT(error_node) \
 | 
						|
		      prefix ": " #ndtype " is expected, but %s", \
 | 
						|
		      ruby_node_name(error_type)); \
 | 
						|
	return errval; \
 | 
						|
    } \
 | 
						|
} while (0)
 | 
						|
 | 
						|
#define EXPECT_NODE_NONULL(prefix, parent, ndtype, errval) \
 | 
						|
do { \
 | 
						|
    COMPILE_ERROR(ERROR_ARGS_AT(parent) \
 | 
						|
		  prefix ": must be " #ndtype ", but 0"); \
 | 
						|
    return errval; \
 | 
						|
} while (0)
 | 
						|
 | 
						|
#define UNKNOWN_NODE(prefix, node, errval) \
 | 
						|
do { \
 | 
						|
    const NODE *error_node = (node); \
 | 
						|
    COMPILE_ERROR(ERROR_ARGS_AT(error_node) prefix ": unknown node (%s)", \
 | 
						|
		  ruby_node_name(nd_type(error_node))); \
 | 
						|
    return errval; \
 | 
						|
} while (0)
 | 
						|
 | 
						|
#define COMPILE_OK 1
 | 
						|
#define COMPILE_NG 0
 | 
						|
 | 
						|
#define CHECK(sub) if (!(sub)) {BEFORE_RETURN;return COMPILE_NG;}
 | 
						|
#define BEFORE_RETURN
 | 
						|
 | 
						|
/* leave name uninitialized so that compiler warn if INIT_ANCHOR is
 | 
						|
 * missing */
 | 
						|
#define DECL_ANCHOR(name) \
 | 
						|
    LINK_ANCHOR name[1] = {{{ISEQ_ELEMENT_ANCHOR,},}}
 | 
						|
#define INIT_ANCHOR(name) \
 | 
						|
    (name->last = &name->anchor)
 | 
						|
 | 
						|
static inline VALUE
 | 
						|
freeze_hide_obj(VALUE obj)
 | 
						|
{
 | 
						|
    OBJ_FREEZE(obj);
 | 
						|
    RBASIC_CLEAR_CLASS(obj);
 | 
						|
    return obj;
 | 
						|
}
 | 
						|
 | 
						|
#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(iseq)->node_level
 | 
						|
#endif
 | 
						|
 | 
						|
static void dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr, const LABEL *dest);
 | 
						|
static void dump_disasm_list(const LINK_ELEMENT *elem);
 | 
						|
 | 
						|
static int insn_data_length(INSN *iobj);
 | 
						|
static int calc_sp_depth(int depth, INSN *iobj);
 | 
						|
 | 
						|
static INSN *new_insn_body(rb_iseq_t *iseq, int line_no, enum ruby_vminsn_type 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 TRACE *new_trace_body(rb_iseq_t *iseq, rb_event_flag_t event);
 | 
						|
 | 
						|
 | 
						|
static int iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *anchor, const NODE *n, int);
 | 
						|
static int iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
 | 
						|
static int iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
 | 
						|
static int iseq_insns_unification(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
 | 
						|
 | 
						|
static int iseq_set_local_table(rb_iseq_t *iseq, const ID *tbl);
 | 
						|
static int iseq_set_exception_local_table(rb_iseq_t *iseq);
 | 
						|
static int iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const anchor, const NODE *const node);
 | 
						|
 | 
						|
static int iseq_set_sequence_stackcaching(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
 | 
						|
static int iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor);
 | 
						|
static int iseq_set_exception_table(rb_iseq_t *iseq);
 | 
						|
static int iseq_set_optargs_table(rb_iseq_t *iseq);
 | 
						|
 | 
						|
static int compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE needstr);
 | 
						|
 | 
						|
/*
 | 
						|
 * To make Array to LinkedList, use link_anchor
 | 
						|
 */
 | 
						|
 | 
						|
static void
 | 
						|
verify_list(ISEQ_ARG_DECLARE const char *info, LINK_ANCHOR *const 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 *const 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 *const 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) APPEND_ELEM(iseq, (anchor), (before), (elem))
 | 
						|
#endif
 | 
						|
 | 
						|
static int
 | 
						|
iseq_add_mark_object(const rb_iseq_t *iseq, VALUE v)
 | 
						|
{
 | 
						|
    if (!SPECIAL_CONST_P(v)) {
 | 
						|
	rb_iseq_add_mark_object(iseq, v);
 | 
						|
    }
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
iseq_add_mark_object_compile_time(const rb_iseq_t *iseq, VALUE v)
 | 
						|
{
 | 
						|
    if (!SPECIAL_CONST_P(v)) {
 | 
						|
	rb_ary_push(ISEQ_COMPILE_DATA(iseq)->mark_ary, v);
 | 
						|
    }
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static inline VALUE
 | 
						|
freeze_literal(rb_iseq_t *iseq, VALUE lit)
 | 
						|
{
 | 
						|
    lit = rb_fstring(lit);
 | 
						|
    rb_ary_push(ISEQ_COMPILE_DATA(iseq)->mark_ary, lit);
 | 
						|
    return lit;
 | 
						|
}
 | 
						|
 | 
						|
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 {
 | 
						|
	    COMPILE_ERROR(iseq, lobj->position,
 | 
						|
			  "%"PRIsVALUE": undefined label",
 | 
						|
			  rb_sym2str((VALUE)name));
 | 
						|
	} 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);
 | 
						|
    st_free_table(labels_table);
 | 
						|
}
 | 
						|
 | 
						|
VALUE
 | 
						|
rb_iseq_compile_ifunc(rb_iseq_t *iseq, const struct vm_ifunc *ifunc)
 | 
						|
{
 | 
						|
    DECL_ANCHOR(ret);
 | 
						|
    INIT_ANCHOR(ret);
 | 
						|
 | 
						|
    (*ifunc->func)(iseq, ret, ifunc->data);
 | 
						|
 | 
						|
    ADD_INSN(ret, ISEQ_COMPILE_DATA(iseq)->last_line, leave);
 | 
						|
 | 
						|
    return iseq_setup(iseq, ret);
 | 
						|
}
 | 
						|
 | 
						|
VALUE
 | 
						|
rb_iseq_compile_node(rb_iseq_t *iseq, const NODE *node)
 | 
						|
{
 | 
						|
    DECL_ANCHOR(ret);
 | 
						|
    INIT_ANCHOR(ret);
 | 
						|
 | 
						|
    VM_ASSERT(!imemo_type_p((VALUE)node, imemo_ifunc));
 | 
						|
 | 
						|
    if (node == 0) {
 | 
						|
	COMPILE(ret, "nil", node);
 | 
						|
	iseq_set_local_table(iseq, 0);
 | 
						|
    }
 | 
						|
    /* assume node is T_NODE */
 | 
						|
    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->body->type) {
 | 
						|
	  case ISEQ_TYPE_BLOCK:
 | 
						|
	    {
 | 
						|
		LABEL *start = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(0);
 | 
						|
		LABEL *end = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(0);
 | 
						|
 | 
						|
		start->rescued = LABEL_RESCUE_BEG;
 | 
						|
		end->rescued = LABEL_RESCUE_END;
 | 
						|
 | 
						|
		ADD_TRACE(ret, RUBY_EVENT_B_CALL);
 | 
						|
		ADD_INSN (ret, FIX2INT(iseq->body->location.first_lineno), nop);
 | 
						|
		ADD_LABEL(ret, start);
 | 
						|
		CHECK(COMPILE(ret, "block body", node->nd_body));
 | 
						|
		ADD_LABEL(ret, end);
 | 
						|
		ADD_TRACE(ret, RUBY_EVENT_B_RETURN);
 | 
						|
		ISEQ_COMPILE_DATA(iseq)->last_line = iseq->body->location.code_location.end_pos.lineno;
 | 
						|
 | 
						|
		/* wide range catch handler must put at last */
 | 
						|
		ADD_CATCH_ENTRY(CATCH_TYPE_REDO, start, end, NULL, start);
 | 
						|
		ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, start, end, NULL, end);
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  case ISEQ_TYPE_CLASS:
 | 
						|
	    {
 | 
						|
		ADD_TRACE(ret, RUBY_EVENT_CLASS);
 | 
						|
		CHECK(COMPILE(ret, "scoped node", node->nd_body));
 | 
						|
		ADD_TRACE(ret, RUBY_EVENT_END);
 | 
						|
		ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node);
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  case ISEQ_TYPE_METHOD:
 | 
						|
	    {
 | 
						|
		ADD_TRACE(ret, RUBY_EVENT_CALL);
 | 
						|
		CHECK(COMPILE(ret, "scoped node", node->nd_body));
 | 
						|
		ADD_TRACE(ret, RUBY_EVENT_RETURN);
 | 
						|
		ISEQ_COMPILE_DATA(iseq)->last_line = nd_line(node);
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  default: {
 | 
						|
	    CHECK(COMPILE(ret, "scoped node", node->nd_body));
 | 
						|
	    break;
 | 
						|
	  }
 | 
						|
	}
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	const char *m;
 | 
						|
#define INVALID_ISEQ_TYPE(type) \
 | 
						|
	ISEQ_TYPE_##type: m = #type; goto invalid_iseq_type
 | 
						|
	switch (iseq->body->type) {
 | 
						|
	  case INVALID_ISEQ_TYPE(METHOD);
 | 
						|
	  case INVALID_ISEQ_TYPE(CLASS);
 | 
						|
	  case INVALID_ISEQ_TYPE(BLOCK);
 | 
						|
	  case INVALID_ISEQ_TYPE(EVAL);
 | 
						|
	  case INVALID_ISEQ_TYPE(MAIN);
 | 
						|
	  case INVALID_ISEQ_TYPE(TOP);
 | 
						|
#undef INVALID_ISEQ_TYPE /* invalid iseq types end */
 | 
						|
	  case ISEQ_TYPE_RESCUE:
 | 
						|
	    iseq_set_exception_local_table(iseq);
 | 
						|
	    CHECK(COMPILE(ret, "rescue", node));
 | 
						|
	    break;
 | 
						|
	  case ISEQ_TYPE_ENSURE:
 | 
						|
	    iseq_set_exception_local_table(iseq);
 | 
						|
	    CHECK(COMPILE_POPPED(ret, "ensure", node));
 | 
						|
	    break;
 | 
						|
	  case ISEQ_TYPE_PLAIN:
 | 
						|
	    CHECK(COMPILE(ret, "ensure", node));
 | 
						|
	    break;
 | 
						|
	  default:
 | 
						|
	    COMPILE_ERROR(ERROR_ARGS "unknown scope: %d", iseq->body->type);
 | 
						|
	    return COMPILE_NG;
 | 
						|
	  invalid_iseq_type:
 | 
						|
	    COMPILE_ERROR(ERROR_ARGS "compile/ISEQ_TYPE_%s should not be reached", m);
 | 
						|
	    return COMPILE_NG;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    if (iseq->body->type == ISEQ_TYPE_RESCUE || iseq->body->type == ISEQ_TYPE_ENSURE) {
 | 
						|
	ADD_GETLOCAL(ret, 0, LVAR_ERRINFO, 0);
 | 
						|
	ADD_INSN1(ret, 0, throw, INT2FIX(0) /* continue throw */ );
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	ADD_INSN(ret, ISEQ_COMPILE_DATA(iseq)->last_line, leave);
 | 
						|
    }
 | 
						|
 | 
						|
#if SUPPORT_JOKE
 | 
						|
    if (ISEQ_COMPILE_DATA(iseq)->labels_table) {
 | 
						|
	st_table *labels_table = ISEQ_COMPILE_DATA(iseq)->labels_table;
 | 
						|
	ISEQ_COMPILE_DATA(iseq)->labels_table = 0;
 | 
						|
	validate_labels(iseq, 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 int i;
 | 
						|
    VALUE *encoded = (VALUE *)iseq->body->iseq_encoded;
 | 
						|
 | 
						|
    for (i = 0; i < iseq->body->iseq_size; /* */ ) {
 | 
						|
	int insn = (int)iseq->body->iseq_encoded[i];
 | 
						|
	int len = insn_len(insn);
 | 
						|
	encoded[i] = (VALUE)table[insn];
 | 
						|
	i += len;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
 | 
						|
static int
 | 
						|
rb_vm_insn_addr2insn(const void *addr) /* cold path */
 | 
						|
{
 | 
						|
    int insn;
 | 
						|
    const void * const *table = rb_vm_get_insns_address_table();
 | 
						|
 | 
						|
    for (insn = 0; insn < VM_INSTRUCTION_SIZE; insn++) {
 | 
						|
	if (table[insn] == addr) {
 | 
						|
	    return insn;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    rb_bug("rb_vm_insn_addr2insn: invalid insn address: %p", addr);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
VALUE *
 | 
						|
rb_iseq_original_iseq(const rb_iseq_t *iseq) /* cold path */
 | 
						|
{
 | 
						|
    VALUE *original_code;
 | 
						|
 | 
						|
    if (ISEQ_ORIGINAL_ISEQ(iseq)) return ISEQ_ORIGINAL_ISEQ(iseq);
 | 
						|
    original_code = ISEQ_ORIGINAL_ISEQ_ALLOC(iseq, iseq->body->iseq_size);
 | 
						|
    MEMCPY(original_code, iseq->body->iseq_encoded, VALUE, iseq->body->iseq_size);
 | 
						|
 | 
						|
#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
 | 
						|
    {
 | 
						|
	unsigned int i;
 | 
						|
 | 
						|
	for (i = 0; i < iseq->body->iseq_size; /* */ ) {
 | 
						|
	    const void *addr = (const void *)original_code[i];
 | 
						|
	    const int insn = rb_vm_insn_addr2insn(addr);
 | 
						|
 | 
						|
	    original_code[i] = insn;
 | 
						|
	    i += insn_len(insn);
 | 
						|
	}
 | 
						|
    }
 | 
						|
#endif
 | 
						|
    return original_code;
 | 
						|
}
 | 
						|
 | 
						|
/*********************************************/
 | 
						|
/* definition of data structure for compiler */
 | 
						|
/*********************************************/
 | 
						|
 | 
						|
/*
 | 
						|
 * On 32-bit SPARC, GCC by default generates SPARC V7 code that may require
 | 
						|
 * 8-byte word alignment. On the other hand, Oracle Solaris Studio seems to
 | 
						|
 * generate SPARCV8PLUS code with unaligned memory access instructions.
 | 
						|
 * That is why the STRICT_ALIGNMENT is defined only with GCC.
 | 
						|
 */
 | 
						|
#if defined(__sparc) && SIZEOF_VOIDP == 4 && defined(__GNUC__)
 | 
						|
  #define STRICT_ALIGNMENT
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef STRICT_ALIGNMENT
 | 
						|
  #if defined(HAVE_TRUE_LONG_LONG) && SIZEOF_LONG_LONG > SIZEOF_VALUE
 | 
						|
    #define ALIGNMENT_SIZE SIZEOF_LONG_LONG
 | 
						|
  #else
 | 
						|
    #define ALIGNMENT_SIZE SIZEOF_VALUE
 | 
						|
  #endif
 | 
						|
  #define PADDING_SIZE_MAX    ((size_t)((ALIGNMENT_SIZE) - 1))
 | 
						|
  #define ALIGNMENT_SIZE_MASK PADDING_SIZE_MAX
 | 
						|
  /* Note: ALIGNMENT_SIZE == (2 ** N) is expected. */
 | 
						|
#else
 | 
						|
  #define PADDING_SIZE_MAX 0
 | 
						|
#endif /* STRICT_ALIGNMENT */
 | 
						|
 | 
						|
#ifdef STRICT_ALIGNMENT
 | 
						|
/* calculate padding size for aligned memory access */
 | 
						|
static size_t
 | 
						|
calc_padding(void *ptr, size_t size)
 | 
						|
{
 | 
						|
    size_t mis;
 | 
						|
    size_t padding = 0;
 | 
						|
 | 
						|
    mis = (size_t)ptr & ALIGNMENT_SIZE_MASK;
 | 
						|
    if (mis > 0) {
 | 
						|
        padding = ALIGNMENT_SIZE - mis;
 | 
						|
    }
 | 
						|
/*
 | 
						|
 * On 32-bit sparc or equivalents, when a single VALUE is requested
 | 
						|
 * and padding == sizeof(VALUE), it is clear that no padding is needed.
 | 
						|
 */
 | 
						|
#if ALIGNMENT_SIZE > SIZEOF_VALUE
 | 
						|
    if (size == sizeof(VALUE) && padding == sizeof(VALUE)) {
 | 
						|
        padding = 0;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    return padding;
 | 
						|
}
 | 
						|
#endif /* STRICT_ALIGNMENT */
 | 
						|
 | 
						|
static void *
 | 
						|
compile_data_alloc(rb_iseq_t *iseq, size_t size)
 | 
						|
{
 | 
						|
    void *ptr = 0;
 | 
						|
    struct iseq_compile_data_storage *storage =
 | 
						|
	ISEQ_COMPILE_DATA(iseq)->storage_current;
 | 
						|
#ifdef STRICT_ALIGNMENT
 | 
						|
    size_t padding = calc_padding((void *)&storage->buff[storage->pos], size);
 | 
						|
#else
 | 
						|
    const size_t padding = 0; /* expected to be optimized by compiler */
 | 
						|
#endif /* STRICT_ALIGNMENT */
 | 
						|
 | 
						|
    if (size >= INT_MAX - padding) rb_memerror();
 | 
						|
    if (storage->pos + size + padding > storage->size) {
 | 
						|
	unsigned int alloc_size = storage->size;
 | 
						|
 | 
						|
	while (alloc_size < size + PADDING_SIZE_MAX) {
 | 
						|
	    if (alloc_size >= INT_MAX / 2) rb_memerror();
 | 
						|
	    alloc_size *= 2;
 | 
						|
	}
 | 
						|
	storage->next = (void *)ALLOC_N(char, alloc_size +
 | 
						|
					offsetof(struct iseq_compile_data_storage, buff));
 | 
						|
	storage = ISEQ_COMPILE_DATA(iseq)->storage_current = storage->next;
 | 
						|
	storage->next = 0;
 | 
						|
	storage->pos = 0;
 | 
						|
	storage->size = alloc_size;
 | 
						|
#ifdef STRICT_ALIGNMENT
 | 
						|
        padding = calc_padding((void *)&storage->buff[storage->pos], size);
 | 
						|
#endif /* STRICT_ALIGNMENT */
 | 
						|
    }
 | 
						|
 | 
						|
#ifdef STRICT_ALIGNMENT
 | 
						|
    storage->pos += (int)padding;
 | 
						|
#endif /* STRICT_ALIGNMENT */
 | 
						|
 | 
						|
    ptr = (void *)&storage->buff[storage->pos];
 | 
						|
    storage->pos += (int)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));
 | 
						|
}
 | 
						|
 | 
						|
static TRACE *
 | 
						|
compile_data_alloc_trace(rb_iseq_t *iseq)
 | 
						|
{
 | 
						|
    return (TRACE *)compile_data_alloc(iseq, sizeof(TRACE));
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * elem1, elemX => elem1, elem2, elemX
 | 
						|
 */
 | 
						|
static void
 | 
						|
ELEM_INSERT_NEXT(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
 | 
						|
{
 | 
						|
    elem2->next = elem1->next;
 | 
						|
    elem2->prev = elem1;
 | 
						|
    elem1->next = elem2;
 | 
						|
    if (elem2->next) {
 | 
						|
	elem2->next->prev = elem2;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * elem1, elemX => elemX, elem2, elem1
 | 
						|
 */
 | 
						|
static void
 | 
						|
ELEM_INSERT_PREV(LINK_ELEMENT *elem1, LINK_ELEMENT *elem2)
 | 
						|
{
 | 
						|
    elem2->prev = elem1->prev;
 | 
						|
    elem2->next = elem1;
 | 
						|
    elem1->prev = elem2;
 | 
						|
    if (elem2->prev) {
 | 
						|
	elem2->prev->next = elem2;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * elemX, elem1, elemY => elemX, elem2, elemY
 | 
						|
 */
 | 
						|
static void
 | 
						|
ELEM_REPLACE(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
 | 
						|
ELEM_REMOVE(LINK_ELEMENT *elem)
 | 
						|
{
 | 
						|
    elem->prev->next = elem->next;
 | 
						|
    if (elem->next) {
 | 
						|
	elem->next->prev = elem->prev;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static LINK_ELEMENT *
 | 
						|
FIRST_ELEMENT(const LINK_ANCHOR *const anchor)
 | 
						|
{
 | 
						|
    return anchor->anchor.next;
 | 
						|
}
 | 
						|
 | 
						|
static LINK_ELEMENT *
 | 
						|
LAST_ELEMENT(LINK_ANCHOR *const anchor)
 | 
						|
{
 | 
						|
    return anchor->last;
 | 
						|
}
 | 
						|
 | 
						|
static LINK_ELEMENT *
 | 
						|
POP_ELEMENT(ISEQ_ARG_DECLARE LINK_ANCHOR *const 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
 | 
						|
 | 
						|
static LINK_ELEMENT *
 | 
						|
ELEM_FIRST_INSN(LINK_ELEMENT *elem)
 | 
						|
{
 | 
						|
    while (elem) {
 | 
						|
	switch (elem->type) {
 | 
						|
	  case ISEQ_ELEMENT_INSN:
 | 
						|
	  case ISEQ_ELEMENT_ADJUST:
 | 
						|
	    return elem;
 | 
						|
	  default:
 | 
						|
	    elem = elem->next;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
LIST_INSN_SIZE_ONE(const LINK_ANCHOR *const anchor)
 | 
						|
{
 | 
						|
    LINK_ELEMENT *first_insn = ELEM_FIRST_INSN(FIRST_ELEMENT(anchor));
 | 
						|
    if (first_insn != NULL &&
 | 
						|
	ELEM_FIRST_INSN(first_insn->next) == NULL) {
 | 
						|
	return TRUE;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	return FALSE;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
LIST_INSN_SIZE_ZERO(const LINK_ANCHOR *const anchor)
 | 
						|
{
 | 
						|
    if (ELEM_FIRST_INSN(FIRST_ELEMENT(anchor)) == NULL) {
 | 
						|
	return TRUE;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	return FALSE;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * 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 *const anc1, LINK_ANCHOR *const 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 *const anc1, LINK_ANCHOR *const 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 CPDEBUG && 0
 | 
						|
static void
 | 
						|
debug_list(ISEQ_ARG_DECLARE LINK_ANCHOR *const 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
 | 
						|
#else
 | 
						|
#define debug_list(anc) ((void)0)
 | 
						|
#endif
 | 
						|
 | 
						|
static TRACE *
 | 
						|
new_trace_body(rb_iseq_t *iseq, rb_event_flag_t event)
 | 
						|
{
 | 
						|
    TRACE *trace = compile_data_alloc_trace(iseq);
 | 
						|
 | 
						|
    trace->link.type = ISEQ_ELEMENT_TRACE;
 | 
						|
    trace->link.next = NULL;
 | 
						|
    trace->event = event;
 | 
						|
 | 
						|
    return trace;
 | 
						|
}
 | 
						|
 | 
						|
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(iseq)->label_no++;
 | 
						|
    labelobj->sc_state = 0;
 | 
						|
    labelobj->sp = -1;
 | 
						|
    labelobj->refcnt = 0;
 | 
						|
    labelobj->set = 0;
 | 
						|
    labelobj->rescued = LABEL_RESCUE_NONE;
 | 
						|
    labelobj->unremovable = 0;
 | 
						|
    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;
 | 
						|
    LABEL_UNREMOVABLE(label);
 | 
						|
    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->insn_info.line_no = line_no;
 | 
						|
    iobj->insn_info.events = 0;
 | 
						|
    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, enum ruby_vminsn_type 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 struct rb_call_info *
 | 
						|
new_callinfo(rb_iseq_t *iseq, ID mid, int argc, unsigned int flag, struct rb_call_info_kw_arg *kw_arg, int has_blockiseq)
 | 
						|
{
 | 
						|
    size_t size = kw_arg != NULL ? sizeof(struct rb_call_info_with_kwarg) : sizeof(struct rb_call_info);
 | 
						|
    struct rb_call_info *ci = (struct rb_call_info *)compile_data_alloc(iseq, size);
 | 
						|
    struct rb_call_info_with_kwarg *ci_kw = (struct rb_call_info_with_kwarg *)ci;
 | 
						|
 | 
						|
    ci->mid = mid;
 | 
						|
    ci->flag = flag;
 | 
						|
    ci->orig_argc = argc;
 | 
						|
 | 
						|
    if (kw_arg) {
 | 
						|
	ci->flag |= VM_CALL_KWARG;
 | 
						|
	ci_kw->kw_arg = kw_arg;
 | 
						|
	ci->orig_argc += kw_arg->keyword_len;
 | 
						|
	iseq->body->ci_kw_size++;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	iseq->body->ci_size++;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!(ci->flag & (VM_CALL_ARGS_SPLAT | VM_CALL_ARGS_BLOCKARG | VM_CALL_KW_SPLAT)) &&
 | 
						|
	kw_arg == NULL && !has_blockiseq) {
 | 
						|
	ci->flag |= VM_CALL_ARGS_SIMPLE;
 | 
						|
    }
 | 
						|
    return ci;
 | 
						|
}
 | 
						|
 | 
						|
static INSN *
 | 
						|
new_insn_send(rb_iseq_t *iseq, int line_no, ID id, VALUE argc, const rb_iseq_t *blockiseq, VALUE flag, struct rb_call_info_kw_arg *keywords)
 | 
						|
{
 | 
						|
    VALUE *operands = (VALUE *)compile_data_alloc(iseq, sizeof(VALUE) * 3);
 | 
						|
    operands[0] = (VALUE)new_callinfo(iseq, id, FIX2INT(argc), FIX2INT(flag), keywords, blockiseq != NULL);
 | 
						|
    operands[1] = Qfalse; /* cache */
 | 
						|
    operands[2] = (VALUE)blockiseq;
 | 
						|
    return new_insn_core(iseq, line_no, BIN(send), 3, operands);
 | 
						|
}
 | 
						|
 | 
						|
static rb_iseq_t *
 | 
						|
new_child_iseq(rb_iseq_t *iseq, const NODE *const node,
 | 
						|
	       VALUE name, const rb_iseq_t *parent, enum iseq_type type, int line_no)
 | 
						|
{
 | 
						|
    rb_iseq_t *ret_iseq;
 | 
						|
    rb_ast_body_t ast;
 | 
						|
 | 
						|
    ast.root = node;
 | 
						|
    ast.compile_option = 0;
 | 
						|
 | 
						|
    debugs("[new_child_iseq]> ---------------------------------------\n");
 | 
						|
    ret_iseq = rb_iseq_new_with_opt(&ast, name,
 | 
						|
				    rb_iseq_path(iseq), rb_iseq_realpath(iseq),
 | 
						|
				    INT2FIX(line_no), parent, type, ISEQ_COMPILE_DATA(iseq)->option);
 | 
						|
    debugs("[new_child_iseq]< ---------------------------------------\n");
 | 
						|
    iseq_add_mark_object(iseq, (VALUE)ret_iseq);
 | 
						|
    return ret_iseq;
 | 
						|
}
 | 
						|
 | 
						|
static rb_iseq_t *
 | 
						|
new_child_iseq_ifunc(rb_iseq_t *iseq, const struct vm_ifunc *ifunc,
 | 
						|
		     VALUE name, const rb_iseq_t *parent, enum iseq_type type, int line_no)
 | 
						|
{
 | 
						|
    rb_iseq_t *ret_iseq;
 | 
						|
 | 
						|
    debugs("[new_child_iseq_ifunc]> ---------------------------------------\n");
 | 
						|
    ret_iseq = rb_iseq_new_ifunc(ifunc, name,
 | 
						|
				 rb_iseq_path(iseq), rb_iseq_realpath(iseq),
 | 
						|
				 INT2FIX(line_no), parent, type, ISEQ_COMPILE_DATA(iseq)->option);
 | 
						|
    debugs("[new_child_iseq_ifunc]< ---------------------------------------\n");
 | 
						|
    iseq_add_mark_object(iseq, (VALUE)ret_iseq);
 | 
						|
    return ret_iseq;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
iseq_setup(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
 | 
						|
{
 | 
						|
    if (RTEST(ISEQ_COMPILE_DATA(iseq)->err_info))
 | 
						|
	return COMPILE_NG;
 | 
						|
 | 
						|
    /* 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(iseq)->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(iseq)->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");
 | 
						|
    if (!iseq_set_sequence(iseq, anchor)) return COMPILE_NG;
 | 
						|
    if (compile_debug > 5)
 | 
						|
	dump_disasm_list(FIRST_ELEMENT(anchor));
 | 
						|
 | 
						|
    debugs("[compile step 4.2 (iseq_set_exception_table)]\n");
 | 
						|
    if (!iseq_set_exception_table(iseq)) return COMPILE_NG;
 | 
						|
 | 
						|
    debugs("[compile step 4.3 (set_optargs_table)] \n");
 | 
						|
    if (!iseq_set_optargs_table(iseq)) return COMPILE_NG;
 | 
						|
 | 
						|
    debugs("[compile step 5 (iseq_translate_threaded_code)] \n");
 | 
						|
    if (!rb_iseq_translate_threaded_code(iseq)) return COMPILE_NG;
 | 
						|
 | 
						|
    if (compile_debug > 1) {
 | 
						|
	VALUE str = rb_iseq_disasm(iseq);
 | 
						|
	printf("%s\n", StringValueCStr(str));
 | 
						|
    }
 | 
						|
    debugs("[compile step: finish]\n");
 | 
						|
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
iseq_set_exception_local_table(rb_iseq_t *iseq)
 | 
						|
{
 | 
						|
    /* TODO: every id table is same -> share it.
 | 
						|
     * Current problem is iseq_free().
 | 
						|
     */
 | 
						|
    ID id_dollar_bang;
 | 
						|
    ID *ids = (ID *)ALLOC_N(ID, 1);
 | 
						|
 | 
						|
    CONST_ID(id_dollar_bang, "#$!");
 | 
						|
    iseq->body->local_table_size = 1;
 | 
						|
    ids[0] = id_dollar_bang;
 | 
						|
    iseq->body->local_table = ids;
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
get_lvar_level(const rb_iseq_t *iseq)
 | 
						|
{
 | 
						|
    int lev = 0;
 | 
						|
    while (iseq != iseq->body->local_iseq) {
 | 
						|
	lev++;
 | 
						|
	iseq = iseq->body->parent_iseq;
 | 
						|
    }
 | 
						|
    return lev;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
get_dyna_var_idx_at_raw(const rb_iseq_t *iseq, ID id)
 | 
						|
{
 | 
						|
    unsigned int i;
 | 
						|
 | 
						|
    for (i = 0; i < iseq->body->local_table_size; i++) {
 | 
						|
	if (iseq->body->local_table[i] == id) {
 | 
						|
	    return (int)i;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
get_local_var_idx(const rb_iseq_t *iseq, ID id)
 | 
						|
{
 | 
						|
    int idx = get_dyna_var_idx_at_raw(iseq->body->local_iseq, id);
 | 
						|
 | 
						|
    if (idx < 0) {
 | 
						|
	rb_bug("get_local_var_idx: %d", idx);
 | 
						|
    }
 | 
						|
 | 
						|
    return idx;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
get_dyna_var_idx(const 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->body->parent_iseq;
 | 
						|
	lv++;
 | 
						|
    }
 | 
						|
 | 
						|
    if (idx < 0) {
 | 
						|
	rb_bug("get_dyna_var_idx: -1");
 | 
						|
    }
 | 
						|
 | 
						|
    *level = lv;
 | 
						|
    *ls = iseq->body->local_table_size;
 | 
						|
    return idx;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
iseq_local_block_param_p(const rb_iseq_t *iseq, unsigned int idx, unsigned int level)
 | 
						|
{
 | 
						|
    while (level > 0) {
 | 
						|
	iseq = iseq->body->parent_iseq;
 | 
						|
	level--;
 | 
						|
    }
 | 
						|
    if (iseq->body->local_iseq == iseq && /* local variables */
 | 
						|
	iseq->body->param.flags.has_block &&
 | 
						|
	iseq->body->local_table_size - iseq->body->param.block_start == idx) {
 | 
						|
	return TRUE;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	return FALSE;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
iseq_block_param_id_p(const rb_iseq_t *iseq, ID id, int *pidx, int *plevel)
 | 
						|
{
 | 
						|
    int level, ls;
 | 
						|
    int idx = get_dyna_var_idx(iseq, id, &level, &ls);
 | 
						|
    if (iseq_local_block_param_p(iseq, ls - idx, level)) {
 | 
						|
	*pidx = ls - idx;
 | 
						|
	*plevel = level;
 | 
						|
	return TRUE;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	return FALSE;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, int level)
 | 
						|
{
 | 
						|
    if (iseq_local_block_param_p(iseq, idx, level)) {
 | 
						|
	ADD_INSN2(seq, line, getblockparam, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	ADD_INSN2(seq, line, getlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, int level)
 | 
						|
{
 | 
						|
    if (iseq_local_block_param_p(iseq, idx, level)) {
 | 
						|
	ADD_INSN2(seq, line, setblockparam, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	ADD_INSN2(seq, line, setlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
iseq_calc_param_size(rb_iseq_t *iseq)
 | 
						|
{
 | 
						|
    if (iseq->body->param.flags.has_opt ||
 | 
						|
	iseq->body->param.flags.has_post ||
 | 
						|
	iseq->body->param.flags.has_rest ||
 | 
						|
	iseq->body->param.flags.has_block ||
 | 
						|
	iseq->body->param.flags.has_kw ||
 | 
						|
	iseq->body->param.flags.has_kwrest) {
 | 
						|
 | 
						|
	if (iseq->body->param.flags.has_block) {
 | 
						|
	    iseq->body->param.size = iseq->body->param.block_start + 1;
 | 
						|
	}
 | 
						|
	else if (iseq->body->param.flags.has_kwrest) {
 | 
						|
	    iseq->body->param.size = iseq->body->param.keyword->rest_start + 1;
 | 
						|
	}
 | 
						|
	else if (iseq->body->param.flags.has_kw) {
 | 
						|
	    iseq->body->param.size = iseq->body->param.keyword->bits_start + 1;
 | 
						|
	}
 | 
						|
	else if (iseq->body->param.flags.has_post) {
 | 
						|
	    iseq->body->param.size = iseq->body->param.post_start + iseq->body->param.post_num;
 | 
						|
	}
 | 
						|
	else if (iseq->body->param.flags.has_rest) {
 | 
						|
	    iseq->body->param.size = iseq->body->param.rest_start + 1;
 | 
						|
	}
 | 
						|
	else if (iseq->body->param.flags.has_opt) {
 | 
						|
	    iseq->body->param.size = iseq->body->param.lead_num + iseq->body->param.opt_num;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    rb_bug("unreachable");
 | 
						|
	}
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	iseq->body->param.size = iseq->body->param.lead_num;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
iseq_set_arguments_keywords(rb_iseq_t *iseq, LINK_ANCHOR *const optargs,
 | 
						|
			    const struct rb_args_info *args)
 | 
						|
{
 | 
						|
    const NODE *node = args->kw_args;
 | 
						|
    struct rb_iseq_param_keyword *keyword;
 | 
						|
    const VALUE default_values = rb_ary_tmp_new(1);
 | 
						|
    const VALUE complex_mark = rb_str_tmp_new(0);
 | 
						|
    int kw = 0, rkw = 0, di = 0, i;
 | 
						|
 | 
						|
    iseq->body->param.flags.has_kw = TRUE;
 | 
						|
    iseq->body->param.keyword = keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
 | 
						|
    keyword->bits_start = get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_cflag);
 | 
						|
 | 
						|
    while (node) {
 | 
						|
	const NODE *val_node = node->nd_body->nd_value;
 | 
						|
	VALUE dv;
 | 
						|
 | 
						|
	if (val_node == (const NODE *)-1) {
 | 
						|
	    ++rkw;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    switch (nd_type(val_node)) {
 | 
						|
	      case NODE_LIT:
 | 
						|
		dv = val_node->nd_lit;
 | 
						|
		iseq_add_mark_object(iseq, dv);
 | 
						|
		break;
 | 
						|
	      case NODE_NIL:
 | 
						|
		dv = Qnil;
 | 
						|
		break;
 | 
						|
	      case NODE_TRUE:
 | 
						|
		dv = Qtrue;
 | 
						|
		break;
 | 
						|
	      case NODE_FALSE:
 | 
						|
		dv = Qfalse;
 | 
						|
		break;
 | 
						|
	      default:
 | 
						|
		COMPILE_POPPED(optargs, "kwarg", node); /* nd_type(node) == NODE_KW_ARG */
 | 
						|
		dv = complex_mark;
 | 
						|
	    }
 | 
						|
 | 
						|
	    keyword->num = ++di;
 | 
						|
	    rb_ary_push(default_values, dv);
 | 
						|
	}
 | 
						|
 | 
						|
	kw++;
 | 
						|
	node = node->nd_next;
 | 
						|
    }
 | 
						|
 | 
						|
    keyword->num = kw;
 | 
						|
 | 
						|
    if (args->kw_rest_arg->nd_vid != 0) {
 | 
						|
	keyword->rest_start =  get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_vid);
 | 
						|
	iseq->body->param.flags.has_kwrest = TRUE;
 | 
						|
    }
 | 
						|
    keyword->required_num = rkw;
 | 
						|
    keyword->table = &iseq->body->local_table[keyword->bits_start - keyword->num];
 | 
						|
 | 
						|
    {
 | 
						|
	VALUE *dvs = ALLOC_N(VALUE, RARRAY_LEN(default_values));
 | 
						|
 | 
						|
	for (i = 0; i < RARRAY_LEN(default_values); i++) {
 | 
						|
	    VALUE dv = RARRAY_AREF(default_values, i);
 | 
						|
	    if (dv == complex_mark) dv = Qundef;
 | 
						|
	    dvs[i] = dv;
 | 
						|
	}
 | 
						|
 | 
						|
	keyword->default_values = dvs;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
iseq_set_arguments(rb_iseq_t *iseq, LINK_ANCHOR *const optargs, const NODE *const 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;
 | 
						|
 | 
						|
	EXPECT_NODE("iseq_set_arguments", node_args, NODE_ARGS, COMPILE_NG);
 | 
						|
 | 
						|
	iseq->body->param.lead_num = (int)args->pre_args_num;
 | 
						|
	if (iseq->body->param.lead_num > 0) iseq->body->param.flags.has_lead = TRUE;
 | 
						|
	debugs("  - argc: %d\n", iseq->body->param.lead_num);
 | 
						|
 | 
						|
	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->body->param.post_start = get_dyna_var_idx_at_raw(iseq, args->first_post_arg);
 | 
						|
	    iseq->body->param.post_num = args->post_args_num;
 | 
						|
	    iseq->body->param.flags.has_post = TRUE;
 | 
						|
	}
 | 
						|
 | 
						|
	if (args->opt_args) {
 | 
						|
	    const NODE *node = args->opt_args;
 | 
						|
	    LABEL *label;
 | 
						|
	    VALUE labels = rb_ary_tmp_new(1);
 | 
						|
	    VALUE *opt_table;
 | 
						|
	    int i = 0, j;
 | 
						|
 | 
						|
	    while (node) {
 | 
						|
		label = NEW_LABEL(nd_line(node));
 | 
						|
		rb_ary_push(labels, (VALUE)label | 1);
 | 
						|
		ADD_LABEL(optargs, label);
 | 
						|
		COMPILE_POPPED(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);
 | 
						|
 | 
						|
	    opt_table = ALLOC_N(VALUE, i+1);
 | 
						|
 | 
						|
	    MEMCPY(opt_table, RARRAY_CONST_PTR(labels), VALUE, i+1);
 | 
						|
	    for (j = 0; j < i+1; j++) {
 | 
						|
		opt_table[j] &= ~1;
 | 
						|
	    }
 | 
						|
	    rb_ary_clear(labels);
 | 
						|
 | 
						|
	    iseq->body->param.flags.has_opt = TRUE;
 | 
						|
	    iseq->body->param.opt_num = i;
 | 
						|
	    iseq->body->param.opt_table = opt_table;
 | 
						|
	}
 | 
						|
 | 
						|
	if (args->kw_args) {
 | 
						|
	    iseq_set_arguments_keywords(iseq, optargs, args);
 | 
						|
	}
 | 
						|
	else if (args->kw_rest_arg) {
 | 
						|
	    struct rb_iseq_param_keyword *keyword = ZALLOC_N(struct rb_iseq_param_keyword, 1);
 | 
						|
	    keyword->rest_start = get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_vid);
 | 
						|
	    iseq->body->param.keyword = keyword;
 | 
						|
	    iseq->body->param.flags.has_kwrest = TRUE;
 | 
						|
	}
 | 
						|
 | 
						|
	if (args->pre_init) { /* m_init */
 | 
						|
	    COMPILE_POPPED(optargs, "init arguments (m)", args->pre_init);
 | 
						|
	}
 | 
						|
	if (args->post_init) { /* p_init */
 | 
						|
	    COMPILE_POPPED(optargs, "init arguments (p)", args->post_init);
 | 
						|
	}
 | 
						|
 | 
						|
	if (rest_id) {
 | 
						|
	    iseq->body->param.rest_start = get_dyna_var_idx_at_raw(iseq, rest_id);
 | 
						|
	    iseq->body->param.flags.has_rest = TRUE;
 | 
						|
	    assert(iseq->body->param.rest_start != -1);
 | 
						|
 | 
						|
	    if (iseq->body->param.post_start == 0) { /* TODO: why that? */
 | 
						|
		iseq->body->param.post_start = iseq->body->param.rest_start + 1;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
	if (block_id) {
 | 
						|
	    iseq->body->param.block_start = get_dyna_var_idx_at_raw(iseq, block_id);
 | 
						|
	    iseq->body->param.flags.has_block = TRUE;
 | 
						|
	}
 | 
						|
 | 
						|
	iseq_calc_param_size(iseq);
 | 
						|
 | 
						|
	if (iseq->body->type == ISEQ_TYPE_BLOCK) {
 | 
						|
	    if (iseq->body->param.flags.has_opt    == FALSE &&
 | 
						|
		iseq->body->param.flags.has_post   == FALSE &&
 | 
						|
		iseq->body->param.flags.has_rest   == FALSE &&
 | 
						|
		iseq->body->param.flags.has_kw     == FALSE &&
 | 
						|
		iseq->body->param.flags.has_kwrest == FALSE) {
 | 
						|
 | 
						|
		if (iseq->body->param.lead_num == 1 && last_comma == 0) {
 | 
						|
		    /* {|a|} */
 | 
						|
		    iseq->body->param.flags.ambiguous_param0 = TRUE;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
iseq_set_local_table(rb_iseq_t *iseq, const ID *tbl)
 | 
						|
{
 | 
						|
    unsigned int size;
 | 
						|
 | 
						|
    if (tbl) {
 | 
						|
	size = (unsigned int)*tbl;
 | 
						|
	tbl++;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	size = 0;
 | 
						|
    }
 | 
						|
 | 
						|
    if (size > 0) {
 | 
						|
	ID *ids = (ID *)ALLOC_N(ID, size);
 | 
						|
	MEMCPY(ids, tbl, ID, size);
 | 
						|
	iseq->body->local_table = ids;
 | 
						|
    }
 | 
						|
    iseq->body->local_table_size = size;
 | 
						|
 | 
						|
    debugs("iseq_set_local_table: %u\n", iseq->body->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;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static inline VALUE
 | 
						|
get_ivar_ic_value(rb_iseq_t *iseq,ID id)
 | 
						|
{
 | 
						|
    VALUE val;
 | 
						|
    struct rb_id_table *tbl = ISEQ_COMPILE_DATA(iseq)->ivar_cache_table;
 | 
						|
    if (tbl) {
 | 
						|
	if (rb_id_table_lookup(tbl,id,&val)) {
 | 
						|
	    return val;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	tbl = rb_id_table_create(1);
 | 
						|
	ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = tbl;
 | 
						|
    }
 | 
						|
    val = INT2FIX(iseq->body->is_size++);
 | 
						|
    rb_id_table_insert(tbl,id,val);
 | 
						|
    return val;
 | 
						|
}
 | 
						|
 | 
						|
#define BADINSN_DUMP(anchor, list, dest) \
 | 
						|
    dump_disasm_list_with_cursor(FIRST_ELEMENT(anchor), list, dest)
 | 
						|
 | 
						|
#define BADINSN_ERROR \
 | 
						|
    (xfree(generated_iseq), \
 | 
						|
     xfree(insns_info), \
 | 
						|
     BADINSN_DUMP(anchor, list, NULL), \
 | 
						|
     COMPILE_ERROR)
 | 
						|
 | 
						|
static int
 | 
						|
fix_sp_depth(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
 | 
						|
{
 | 
						|
    int stack_max = 0, sp = 0, line = 0;
 | 
						|
    LINK_ELEMENT *list;
 | 
						|
 | 
						|
    for (list = FIRST_ELEMENT(anchor); list; list = list->next) {
 | 
						|
	if (list->type == ISEQ_ELEMENT_LABEL) {
 | 
						|
	    LABEL *lobj = (LABEL *)list;
 | 
						|
	    lobj->set = TRUE;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    for (list = FIRST_ELEMENT(anchor); list; list = list->next) {
 | 
						|
	switch (list->type) {
 | 
						|
	  case ISEQ_ELEMENT_INSN:
 | 
						|
	    {
 | 
						|
		int j, len, insn;
 | 
						|
		const char *types;
 | 
						|
		VALUE *operands;
 | 
						|
		INSN *iobj = (INSN *)list;
 | 
						|
 | 
						|
		/* update sp */
 | 
						|
		sp = calc_sp_depth(sp, iobj);
 | 
						|
		if (sp < 0) {
 | 
						|
		    BADINSN_DUMP(anchor, list, NULL);
 | 
						|
		    COMPILE_ERROR(iseq, iobj->insn_info.line_no,
 | 
						|
				  "argument stack underflow (%d)", sp);
 | 
						|
		    return -1;
 | 
						|
		}
 | 
						|
		if (sp > stack_max) {
 | 
						|
		    stack_max = sp;
 | 
						|
		}
 | 
						|
 | 
						|
		line = iobj->insn_info.line_no;
 | 
						|
		/* fprintf(stderr, "insn: %-16s, sp: %d\n", insn_name(iobj->insn_id), sp); */
 | 
						|
		operands = iobj->operands;
 | 
						|
		insn = iobj->insn_id;
 | 
						|
		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); */
 | 
						|
		    BADINSN_DUMP(anchor, list, NULL);
 | 
						|
		    COMPILE_ERROR(iseq, iobj->insn_info.line_no,
 | 
						|
				  "operand size miss! (%d for %d)",
 | 
						|
				  iobj->operand_size, len - 1);
 | 
						|
		    return -1;
 | 
						|
		}
 | 
						|
 | 
						|
		for (j = 0; types[j]; j++) {
 | 
						|
		    if (types[j] == TS_OFFSET) {
 | 
						|
			/* label(destination position) */
 | 
						|
			LABEL *lobj = (LABEL *)operands[j];
 | 
						|
			if (!lobj->set) {
 | 
						|
			    BADINSN_DUMP(anchor, list, NULL);
 | 
						|
			    COMPILE_ERROR(iseq, iobj->insn_info.line_no,
 | 
						|
					  "unknown label: "LABEL_FORMAT, lobj->label_no);
 | 
						|
			    return -1;
 | 
						|
			}
 | 
						|
			if (lobj->sp == -1) {
 | 
						|
			    lobj->sp = sp;
 | 
						|
			}
 | 
						|
		    }
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  case ISEQ_ELEMENT_LABEL:
 | 
						|
	    {
 | 
						|
		LABEL *lobj = (LABEL *)list;
 | 
						|
		if (lobj->sp == -1) {
 | 
						|
		    lobj->sp = sp;
 | 
						|
		}
 | 
						|
		else {
 | 
						|
		    sp = lobj->sp;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  case ISEQ_ELEMENT_TRACE:
 | 
						|
	    {
 | 
						|
		/* ignore */
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  case ISEQ_ELEMENT_ADJUST:
 | 
						|
	    {
 | 
						|
		ADJUST *adjust = (ADJUST *)list;
 | 
						|
		int orig_sp = sp;
 | 
						|
 | 
						|
		sp = adjust->label ? adjust->label->sp : 0;
 | 
						|
		if (adjust->line_no != -1 && orig_sp - sp < 0) {
 | 
						|
		    BADINSN_DUMP(anchor, list, NULL);
 | 
						|
		    COMPILE_ERROR(iseq, adjust->line_no,
 | 
						|
				  "iseq_set_sequence: adjust bug %d < %d",
 | 
						|
				  orig_sp, sp);
 | 
						|
		    return -1;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  default:
 | 
						|
	    BADINSN_DUMP(anchor, list, NULL);
 | 
						|
	    COMPILE_ERROR(iseq, line, "unknown list type: %d", list->type);
 | 
						|
	    return -1;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    return stack_max;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
add_insn_info(struct iseq_insn_info_entry *insns_info, unsigned int *positions, int insns_info_index, int code_index, LINK_ELEMENT *list)
 | 
						|
{
 | 
						|
    if (list->type == ISEQ_ELEMENT_INSN) {
 | 
						|
	INSN *iobj = (INSN *)list;
 | 
						|
	if (insns_info_index == 0 ||
 | 
						|
	    insns_info[insns_info_index-1].line_no != iobj->insn_info.line_no ||
 | 
						|
	    insns_info[insns_info_index-1].events  != iobj->insn_info.events) {
 | 
						|
	    insns_info[insns_info_index].line_no    = iobj->insn_info.line_no;
 | 
						|
	    insns_info[insns_info_index].events     = iobj->insn_info.events;
 | 
						|
	    positions[insns_info_index]             = code_index;
 | 
						|
	    return TRUE;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    return FALSE;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    else if (list->type == ISEQ_ELEMENT_ADJUST) {
 | 
						|
	ADJUST *adjust = (ADJUST *)list;
 | 
						|
	if (insns_info_index > 0 ||
 | 
						|
	    insns_info[insns_info_index-1].line_no != adjust->line_no) {
 | 
						|
	    insns_info[insns_info_index].line_no    = adjust->line_no;
 | 
						|
	    insns_info[insns_info_index].events     = 0;
 | 
						|
	    positions[insns_info_index]             = code_index;
 | 
						|
	    return TRUE;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    return FALSE;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	VM_UNREACHABLE(add_insn_info);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  ruby insn object list -> raw instruction sequence
 | 
						|
 */
 | 
						|
static int
 | 
						|
iseq_set_sequence(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
 | 
						|
{
 | 
						|
    struct iseq_insn_info_entry *insns_info;
 | 
						|
    unsigned int *positions;
 | 
						|
    LINK_ELEMENT *list;
 | 
						|
    VALUE *generated_iseq;
 | 
						|
    rb_event_flag_t events = 0;
 | 
						|
 | 
						|
    int insn_num, code_index, insns_info_index, sp = 0;
 | 
						|
    int stack_max = fix_sp_depth(iseq, anchor);
 | 
						|
 | 
						|
    if (stack_max < 0) return COMPILE_NG;
 | 
						|
 | 
						|
    /* fix label position */
 | 
						|
    list = FIRST_ELEMENT(anchor);
 | 
						|
    insn_num = code_index = 0;
 | 
						|
    while (list) {
 | 
						|
	switch (list->type) {
 | 
						|
	  case ISEQ_ELEMENT_INSN:
 | 
						|
	    {
 | 
						|
		INSN *iobj = (INSN *)list;
 | 
						|
		/* update sp */
 | 
						|
		sp = calc_sp_depth(sp, iobj);
 | 
						|
		code_index += insn_data_length(iobj);
 | 
						|
		insn_num++;
 | 
						|
		iobj->insn_info.events |= events;
 | 
						|
		events = 0;
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  case ISEQ_ELEMENT_LABEL:
 | 
						|
	    {
 | 
						|
		LABEL *lobj = (LABEL *)list;
 | 
						|
		lobj->position = code_index;
 | 
						|
		sp = lobj->sp;
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  case ISEQ_ELEMENT_TRACE:
 | 
						|
	    {
 | 
						|
		TRACE *trace = (TRACE *)list;
 | 
						|
		events |= trace->event;
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  case ISEQ_ELEMENT_ADJUST:
 | 
						|
	    {
 | 
						|
		ADJUST *adjust = (ADJUST *)list;
 | 
						|
		if (adjust->line_no != -1) {
 | 
						|
		    int orig_sp = sp;
 | 
						|
		    sp = adjust->label ? adjust->label->sp : 0;
 | 
						|
		    if (orig_sp - sp > 0) {
 | 
						|
			if (orig_sp - sp > 1) code_index++; /* 1 operand */
 | 
						|
			code_index++; /* insn */
 | 
						|
			insn_num++;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  default: break;
 | 
						|
	}
 | 
						|
	list = list->next;
 | 
						|
    }
 | 
						|
 | 
						|
    /* make instruction sequence */
 | 
						|
    generated_iseq = ALLOC_N(VALUE, code_index);
 | 
						|
    insns_info = ALLOC_N(struct iseq_insn_info_entry, insn_num);
 | 
						|
    positions = ALLOC_N(unsigned int, insn_num);
 | 
						|
    iseq->body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, iseq->body->is_size);
 | 
						|
    iseq->body->ci_entries = (struct rb_call_info *)ruby_xmalloc(sizeof(struct rb_call_info) * iseq->body->ci_size +
 | 
						|
								 sizeof(struct rb_call_info_with_kwarg) * iseq->body->ci_kw_size);
 | 
						|
    MEMZERO(iseq->body->ci_entries + iseq->body->ci_size, struct rb_call_info_with_kwarg,  iseq->body->ci_kw_size); /* need to clear ci_kw entries */
 | 
						|
    iseq->body->cc_entries = ZALLOC_N(struct rb_call_cache, iseq->body->ci_size + iseq->body->ci_kw_size);
 | 
						|
 | 
						|
    ISEQ_COMPILE_DATA(iseq)->ci_index = ISEQ_COMPILE_DATA(iseq)->ci_kw_index = 0;
 | 
						|
 | 
						|
    list = FIRST_ELEMENT(anchor);
 | 
						|
    insns_info_index = code_index = sp = 0;
 | 
						|
 | 
						|
    while (list) {
 | 
						|
	switch (list->type) {
 | 
						|
	  case ISEQ_ELEMENT_INSN:
 | 
						|
	    {
 | 
						|
		int j, len, insn;
 | 
						|
		const char *types;
 | 
						|
		VALUE *operands;
 | 
						|
		INSN *iobj = (INSN *)list;
 | 
						|
 | 
						|
		/* update sp */
 | 
						|
		sp = calc_sp_depth(sp, iobj);
 | 
						|
		/* fprintf(stderr, "insn: %-16s, sp: %d\n", insn_name(iobj->insn_id), sp); */
 | 
						|
		operands = iobj->operands;
 | 
						|
		insn = iobj->insn_id;
 | 
						|
		generated_iseq[code_index] = insn;
 | 
						|
		types = insn_op_types(insn);
 | 
						|
		len = insn_len(insn);
 | 
						|
 | 
						|
		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) */
 | 
						|
			    LABEL *lobj = (LABEL *)operands[j];
 | 
						|
			    generated_iseq[code_index + 1 + j] = lobj->position - (code_index + len);
 | 
						|
			    break;
 | 
						|
			}
 | 
						|
		      case TS_CDHASH:
 | 
						|
			{
 | 
						|
			    VALUE map = operands[j];
 | 
						|
			    struct cdhash_set_label_struct data;
 | 
						|
                            data.hash = map;
 | 
						|
                            data.pos = code_index;
 | 
						|
                            data.len = len;
 | 
						|
			    rb_hash_foreach(map, cdhash_set_label_i, (VALUE)&data);
 | 
						|
 | 
						|
			    rb_hash_rehash(map);
 | 
						|
			    freeze_hide_obj(map);
 | 
						|
			    generated_iseq[code_index + 1 + j] = map;
 | 
						|
			    break;
 | 
						|
			}
 | 
						|
		      case TS_LINDEX:
 | 
						|
		      case TS_NUM:	/* ulong */
 | 
						|
			generated_iseq[code_index + 1 + j] = FIX2INT(operands[j]);
 | 
						|
			break;
 | 
						|
		      case TS_ISEQ:	/* iseq */
 | 
						|
			{
 | 
						|
			    VALUE v = operands[j];
 | 
						|
			    generated_iseq[code_index + 1 + j] = v;
 | 
						|
			    break;
 | 
						|
			}
 | 
						|
		      case TS_VALUE:	/* VALUE */
 | 
						|
			{
 | 
						|
			    VALUE v = operands[j];
 | 
						|
			    generated_iseq[code_index + 1 + j] = v;
 | 
						|
			    /* to mark ruby object */
 | 
						|
			    iseq_add_mark_object(iseq, v);
 | 
						|
			    break;
 | 
						|
			}
 | 
						|
		      case TS_IC: /* inline cache */
 | 
						|
			{
 | 
						|
			    unsigned int ic_index = FIX2UINT(operands[j]);
 | 
						|
			    IC ic = (IC)&iseq->body->is_entries[ic_index];
 | 
						|
			    if (UNLIKELY(ic_index >= iseq->body->is_size)) {
 | 
						|
				rb_bug("iseq_set_sequence: ic_index overflow: index: %d, size: %d", ic_index, iseq->body->is_size);
 | 
						|
			    }
 | 
						|
			    generated_iseq[code_index + 1 + j] = (VALUE)ic;
 | 
						|
			    break;
 | 
						|
			}
 | 
						|
		      case TS_CALLINFO: /* call info */
 | 
						|
			{
 | 
						|
			    struct rb_call_info *base_ci = (struct rb_call_info *)operands[j];
 | 
						|
			    struct rb_call_info *ci;
 | 
						|
 | 
						|
			    if (base_ci->flag & VM_CALL_KWARG) {
 | 
						|
				struct rb_call_info_with_kwarg *ci_kw_entries = (struct rb_call_info_with_kwarg *)&iseq->body->ci_entries[iseq->body->ci_size];
 | 
						|
				struct rb_call_info_with_kwarg *ci_kw = &ci_kw_entries[ISEQ_COMPILE_DATA(iseq)->ci_kw_index++];
 | 
						|
				*ci_kw = *((struct rb_call_info_with_kwarg *)base_ci);
 | 
						|
				ci = (struct rb_call_info *)ci_kw;
 | 
						|
				assert(ISEQ_COMPILE_DATA(iseq)->ci_kw_index <= iseq->body->ci_kw_size);
 | 
						|
			    }
 | 
						|
			    else {
 | 
						|
				ci = &iseq->body->ci_entries[ISEQ_COMPILE_DATA(iseq)->ci_index++];
 | 
						|
				*ci = *base_ci;
 | 
						|
				assert(ISEQ_COMPILE_DATA(iseq)->ci_index <= iseq->body->ci_size);
 | 
						|
			    }
 | 
						|
 | 
						|
			    generated_iseq[code_index + 1 + j] = (VALUE)ci;
 | 
						|
			    break;
 | 
						|
			}
 | 
						|
		      case TS_CALLCACHE:
 | 
						|
			{
 | 
						|
			    struct rb_call_cache *cc = &iseq->body->cc_entries[ISEQ_COMPILE_DATA(iseq)->ci_index + ISEQ_COMPILE_DATA(iseq)->ci_kw_index - 1];
 | 
						|
			    generated_iseq[code_index + 1 + j] = (VALUE)cc;
 | 
						|
			    break;
 | 
						|
			}
 | 
						|
		      case TS_ID: /* ID */
 | 
						|
			generated_iseq[code_index + 1 + j] = SYM2ID(operands[j]);
 | 
						|
			break;
 | 
						|
		      case TS_GENTRY:
 | 
						|
			{
 | 
						|
			    struct rb_global_entry *entry =
 | 
						|
				(struct rb_global_entry *)(operands[j] & (~1));
 | 
						|
			    generated_iseq[code_index + 1 + j] = (VALUE)entry;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		      case TS_FUNCPTR:
 | 
						|
			generated_iseq[code_index + 1 + j] = operands[j];
 | 
						|
			break;
 | 
						|
		      default:
 | 
						|
			BADINSN_ERROR(iseq, iobj->insn_info.line_no,
 | 
						|
				      "unknown operand type: %c", type);
 | 
						|
			return COMPILE_NG;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
		if (add_insn_info(insns_info, positions, insns_info_index, code_index, (LINK_ELEMENT *)iobj)) insns_info_index++;
 | 
						|
		code_index += len;
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  case ISEQ_ELEMENT_LABEL:
 | 
						|
	    {
 | 
						|
		LABEL *lobj = (LABEL *)list;
 | 
						|
		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) {
 | 
						|
		    const int diff = orig_sp - sp;
 | 
						|
		    if (diff > 0) {
 | 
						|
			if (add_insn_info(insns_info, positions, insns_info_index, code_index, (LINK_ELEMENT *)adjust)) insns_info_index++;
 | 
						|
		    }
 | 
						|
		    if (diff > 1) {
 | 
						|
			generated_iseq[code_index++] = BIN(adjuststack);
 | 
						|
			generated_iseq[code_index++] = orig_sp - sp;
 | 
						|
		    }
 | 
						|
		    else if (diff == 1) {
 | 
						|
			generated_iseq[code_index++] = BIN(pop);
 | 
						|
		    }
 | 
						|
		    else if (diff < 0) {
 | 
						|
			int label_no = adjust->label ? adjust->label->label_no : -1;
 | 
						|
			xfree(generated_iseq);
 | 
						|
			xfree(insns_info);
 | 
						|
			xfree(positions);
 | 
						|
			debug_list(anchor);
 | 
						|
			COMPILE_ERROR(iseq, adjust->line_no,
 | 
						|
				      "iseq_set_sequence: adjust bug to %d %d < %d",
 | 
						|
				      label_no, orig_sp, sp);
 | 
						|
			return COMPILE_NG;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  default:
 | 
						|
	    /* ignore */
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
	list = list->next;
 | 
						|
    }
 | 
						|
 | 
						|
    iseq->body->iseq_encoded = (void *)generated_iseq;
 | 
						|
    iseq->body->iseq_size = code_index;
 | 
						|
    iseq->body->stack_max = stack_max;
 | 
						|
 | 
						|
    /* get rid of memory leak when REALLOC failed */
 | 
						|
    iseq->body->insns_info.body = insns_info;
 | 
						|
    iseq->body->insns_info.positions = positions;
 | 
						|
 | 
						|
    REALLOC_N(insns_info, struct iseq_insn_info_entry, insns_info_index);
 | 
						|
    iseq->body->insns_info.body = insns_info;
 | 
						|
    REALLOC_N(positions, unsigned int, insns_info_index);
 | 
						|
    iseq->body->insns_info.positions = positions;
 | 
						|
    iseq->body->insns_info.size = insns_info_index;
 | 
						|
 | 
						|
    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;
 | 
						|
    unsigned int tlen, i;
 | 
						|
    struct iseq_catch_table_entry *entry;
 | 
						|
 | 
						|
    tlen = (int)RARRAY_LEN(ISEQ_COMPILE_DATA(iseq)->catch_table_ary);
 | 
						|
    tptr = RARRAY_CONST_PTR(ISEQ_COMPILE_DATA(iseq)->catch_table_ary);
 | 
						|
 | 
						|
    if (tlen > 0) {
 | 
						|
	struct iseq_catch_table *table = xmalloc(iseq_catch_table_bytes(tlen));
 | 
						|
	table->size = tlen;
 | 
						|
 | 
						|
	for (i = 0; i < table->size; i++) {
 | 
						|
	    ptr = RARRAY_CONST_PTR(tptr[i]);
 | 
						|
	    entry = &table->entries[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 = (rb_iseq_t *)ptr[3];
 | 
						|
 | 
						|
	    /* register iseq as mark object */
 | 
						|
	    if (entry->iseq != 0) {
 | 
						|
		iseq_add_mark_object(iseq, (VALUE)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;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	iseq->body->catch_table = table;
 | 
						|
	RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, 0); /* free */
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	iseq->body->catch_table = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    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;
 | 
						|
    VALUE *opt_table = (VALUE *)iseq->body->param.opt_table;
 | 
						|
 | 
						|
    if (iseq->body->param.flags.has_opt) {
 | 
						|
	for (i = 0; i < iseq->body->param.opt_num + 1; i++) {
 | 
						|
	    opt_table[i] = label_get_position((LABEL *)opt_table[i]);
 | 
						|
	}
 | 
						|
    }
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static LINK_ELEMENT *
 | 
						|
get_destination_insn(INSN *iobj)
 | 
						|
{
 | 
						|
    LABEL *lobj = (LABEL *)OPERAND_AT(iobj, 0);
 | 
						|
    LINK_ELEMENT *list;
 | 
						|
    rb_event_flag_t events = 0;
 | 
						|
 | 
						|
    list = lobj->link.next;
 | 
						|
    while (list) {
 | 
						|
	switch (list->type) {
 | 
						|
	  case ISEQ_ELEMENT_INSN:
 | 
						|
	  case ISEQ_ELEMENT_ADJUST:
 | 
						|
	    goto found;
 | 
						|
	  case ISEQ_ELEMENT_LABEL:
 | 
						|
	    /* ignore */
 | 
						|
	    break;
 | 
						|
	  case ISEQ_ELEMENT_TRACE:
 | 
						|
	    {
 | 
						|
		TRACE *trace = (TRACE *)list;
 | 
						|
		events |= trace->event;
 | 
						|
	    }
 | 
						|
	    break;
 | 
						|
	  default: break;
 | 
						|
	}
 | 
						|
	list = list->next;
 | 
						|
    }
 | 
						|
  found:
 | 
						|
    if (list && IS_INSN(list)) {
 | 
						|
	INSN *iobj = (INSN *)list;
 | 
						|
	iobj->insn_info.events |= events;
 | 
						|
    }
 | 
						|
    return list;
 | 
						|
}
 | 
						|
 | 
						|
static LINK_ELEMENT *
 | 
						|
get_next_insn(INSN *iobj)
 | 
						|
{
 | 
						|
    LINK_ELEMENT *list = iobj->link.next;
 | 
						|
 | 
						|
    while (list) {
 | 
						|
	if (IS_INSN(list) || IS_ADJUST(list)) {
 | 
						|
	    return list;
 | 
						|
	}
 | 
						|
	list = list->next;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static LINK_ELEMENT *
 | 
						|
get_prev_insn(INSN *iobj)
 | 
						|
{
 | 
						|
    LINK_ELEMENT *list = iobj->link.prev;
 | 
						|
 | 
						|
    while (list) {
 | 
						|
	if (IS_INSN(list) || IS_ADJUST(list)) {
 | 
						|
	    return list;
 | 
						|
	}
 | 
						|
	list = list->prev;
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
unref_destination(INSN *iobj, int pos)
 | 
						|
{
 | 
						|
    LABEL *lobj = (LABEL *)OPERAND_AT(iobj, pos);
 | 
						|
    --lobj->refcnt;
 | 
						|
    if (!lobj->refcnt) ELEM_REMOVE(&lobj->link);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
replace_destination(INSN *dobj, INSN *nobj)
 | 
						|
{
 | 
						|
    VALUE n = OPERAND_AT(nobj, 0);
 | 
						|
    LABEL *dl = (LABEL *)OPERAND_AT(dobj, 0);
 | 
						|
    LABEL *nl = (LABEL *)n;
 | 
						|
    --dl->refcnt;
 | 
						|
    ++nl->refcnt;
 | 
						|
    OPERAND_AT(dobj, 0) = n;
 | 
						|
    if (!dl->refcnt) ELEM_REMOVE(&dl->link);
 | 
						|
}
 | 
						|
 | 
						|
static LABEL*
 | 
						|
find_destination(INSN *i)
 | 
						|
{
 | 
						|
    int pos, len = insn_len(i->insn_id);
 | 
						|
    for (pos = 0; pos < len; ++pos) {
 | 
						|
	if (insn_op_types(i->insn_id)[pos] == TS_OFFSET) {
 | 
						|
	    return (LABEL *)OPERAND_AT(i, pos);
 | 
						|
	}
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
remove_unreachable_chunk(rb_iseq_t *iseq, LINK_ELEMENT *i)
 | 
						|
{
 | 
						|
    LINK_ELEMENT *first = i, *end;
 | 
						|
    int *unref_counts = 0, nlabels = ISEQ_COMPILE_DATA(iseq)->label_no;
 | 
						|
 | 
						|
    if (!i) return 0;
 | 
						|
    unref_counts = ALLOCA_N(int, nlabels);
 | 
						|
    MEMZERO(unref_counts, int, nlabels);
 | 
						|
    end = i;
 | 
						|
    do {
 | 
						|
	LABEL *lab;
 | 
						|
	if (IS_INSN(i)) {
 | 
						|
	    if (IS_INSN_ID(i, leave)) {
 | 
						|
		end = i;
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	    else if ((lab = find_destination((INSN *)i)) != 0) {
 | 
						|
		if (lab->unremovable) break;
 | 
						|
		unref_counts[lab->label_no]++;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	else if (IS_LABEL(i)) {
 | 
						|
	    lab = (LABEL *)i;
 | 
						|
	    if (lab->unremovable) return 0;
 | 
						|
	    if (lab->refcnt > unref_counts[lab->label_no]) {
 | 
						|
		if (i == first) return 0;
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	    continue;
 | 
						|
	}
 | 
						|
	else if (IS_TRACE(i)) {
 | 
						|
	    /* do nothing */
 | 
						|
	}
 | 
						|
	else if (IS_ADJUST(i)) {
 | 
						|
	    LABEL *dest = ((ADJUST *)i)->label;
 | 
						|
	    if (dest && dest->unremovable) return 0;
 | 
						|
	}
 | 
						|
	end = i;
 | 
						|
    } while ((i = i->next) != 0);
 | 
						|
    i = first;
 | 
						|
    do {
 | 
						|
	if (IS_INSN(i)) {
 | 
						|
	    struct rb_iseq_constant_body *body = iseq->body;
 | 
						|
	    VALUE insn = INSN_OF(i);
 | 
						|
	    int pos, len = insn_len(insn);
 | 
						|
	    for (pos = 0; pos < len; ++pos) {
 | 
						|
		switch (insn_op_types(insn)[pos]) {
 | 
						|
		  case TS_OFFSET:
 | 
						|
		    unref_destination((INSN *)i, pos);
 | 
						|
		    break;
 | 
						|
		  case TS_CALLINFO:
 | 
						|
		    if (((struct rb_call_info *)OPERAND_AT(i, pos))->flag & VM_CALL_KWARG)
 | 
						|
			--(body->ci_kw_size);
 | 
						|
		    else
 | 
						|
			--(body->ci_size);
 | 
						|
		    break;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	ELEM_REMOVE(i);
 | 
						|
    } while ((i != end) && (i = i->next) != 0);
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
iseq_pop_newarray(rb_iseq_t *iseq, INSN *iobj)
 | 
						|
{
 | 
						|
    switch (OPERAND_AT(iobj, 0)) {
 | 
						|
      case INT2FIX(0): /* empty array */
 | 
						|
	ELEM_REMOVE(&iobj->link);
 | 
						|
	return TRUE;
 | 
						|
      case INT2FIX(1): /* single element array */
 | 
						|
	ELEM_REMOVE(&iobj->link);
 | 
						|
	return FALSE;
 | 
						|
      default:
 | 
						|
	iobj->insn_id = BIN(adjuststack);
 | 
						|
	return TRUE;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
same_debug_pos_p(LINK_ELEMENT *iobj1, LINK_ELEMENT *iobj2)
 | 
						|
{
 | 
						|
    VALUE debug1 = OPERAND_AT(iobj1, 0);
 | 
						|
    VALUE debug2 = OPERAND_AT(iobj2, 0);
 | 
						|
    if (debug1 == debug2) return TRUE;
 | 
						|
    if (!RB_TYPE_P(debug1, T_ARRAY)) return FALSE;
 | 
						|
    if (!RB_TYPE_P(debug2, T_ARRAY)) return FALSE;
 | 
						|
    if (RARRAY_LEN(debug1) != 2) return FALSE;
 | 
						|
    if (RARRAY_LEN(debug2) != 2) return FALSE;
 | 
						|
    if (RARRAY_AREF(debug1, 0) != RARRAY_AREF(debug2, 0)) return FALSE;
 | 
						|
    if (RARRAY_AREF(debug1, 1) != RARRAY_AREF(debug2, 1)) return FALSE;
 | 
						|
    return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcallopt)
 | 
						|
{
 | 
						|
    INSN *const iobj = (INSN *)list;
 | 
						|
  again:
 | 
						|
    if (IS_INSN_ID(iobj, jump)) {
 | 
						|
	INSN *niobj, *diobj, *piobj;
 | 
						|
	diobj = (INSN *)get_destination_insn(iobj);
 | 
						|
	niobj = (INSN *)get_next_insn(iobj);
 | 
						|
 | 
						|
	if (diobj == niobj) {
 | 
						|
	    /*
 | 
						|
	     *   jump LABEL
 | 
						|
	     *  LABEL:
 | 
						|
	     * =>
 | 
						|
	     *   LABEL:
 | 
						|
	     */
 | 
						|
	    unref_destination(iobj, 0);
 | 
						|
	    ELEM_REMOVE(&iobj->link);
 | 
						|
	    return COMPILE_OK;
 | 
						|
	}
 | 
						|
	else if (iobj != diobj && IS_INSN_ID(diobj, jump) &&
 | 
						|
		 OPERAND_AT(iobj, 0) != OPERAND_AT(diobj, 0)) {
 | 
						|
	    /*
 | 
						|
	     *  useless jump elimination:
 | 
						|
	     *     jump LABEL1
 | 
						|
	     *     ...
 | 
						|
	     *   LABEL1:
 | 
						|
	     *     jump LABEL2
 | 
						|
	     *
 | 
						|
	     *   => in this case, first jump instruction should jump to
 | 
						|
	     *      LABEL2 directly
 | 
						|
	     */
 | 
						|
	    replace_destination(iobj, diobj);
 | 
						|
	    remove_unreachable_chunk(iseq, iobj->link.next);
 | 
						|
	    goto again;
 | 
						|
	}
 | 
						|
	else if (IS_INSN_ID(diobj, leave)) {
 | 
						|
	    INSN *pop;
 | 
						|
	    /*
 | 
						|
	     *  jump LABEL
 | 
						|
	     *  ...
 | 
						|
	     * LABEL:
 | 
						|
	     *  leave
 | 
						|
	     * =>
 | 
						|
	     *  leave
 | 
						|
	     *  pop
 | 
						|
	     *  ...
 | 
						|
	     * LABEL:
 | 
						|
	     *  leave
 | 
						|
	     */
 | 
						|
	    /* replace */
 | 
						|
	    unref_destination(iobj, 0);
 | 
						|
	    iobj->insn_id = BIN(leave);
 | 
						|
	    iobj->operand_size = 0;
 | 
						|
	    iobj->insn_info = diobj->insn_info;
 | 
						|
	    /* adjust stack depth */
 | 
						|
	    pop = new_insn_body(iseq, diobj->insn_info.line_no, BIN(pop), 0);
 | 
						|
	    ELEM_INSERT_NEXT(&iobj->link, &pop->link);
 | 
						|
	    goto again;
 | 
						|
	}
 | 
						|
	else if ((piobj = (INSN *)get_prev_insn(iobj)) != 0 &&
 | 
						|
		 (IS_INSN_ID(piobj, branchif) ||
 | 
						|
		  IS_INSN_ID(piobj, branchunless))) {
 | 
						|
	    INSN *pdiobj = (INSN *)get_destination_insn(piobj);
 | 
						|
	    if (niobj == pdiobj) {
 | 
						|
		/*
 | 
						|
		 * useless jump elimination (if/unless destination):
 | 
						|
		 *   if   L1
 | 
						|
		 *   jump L2
 | 
						|
		 * L1:
 | 
						|
		 *   ...
 | 
						|
		 * L2:
 | 
						|
		 *
 | 
						|
		 * ==>
 | 
						|
		 *   unless L2
 | 
						|
		 * L1:
 | 
						|
		 *   ...
 | 
						|
		 * L2:
 | 
						|
		 */
 | 
						|
		piobj->insn_id = (IS_INSN_ID(piobj, branchif))
 | 
						|
		  ? BIN(branchunless) : BIN(branchif);
 | 
						|
		replace_destination(piobj, iobj);
 | 
						|
		ELEM_REMOVE(&iobj->link);
 | 
						|
		return COMPILE_OK;
 | 
						|
	    }
 | 
						|
	    else if (diobj == pdiobj) {
 | 
						|
		/*
 | 
						|
		 * useless jump elimination (if/unless before jump):
 | 
						|
		 * L1:
 | 
						|
		 *   ...
 | 
						|
		 *   if   L1
 | 
						|
		 *   jump L1
 | 
						|
		 *
 | 
						|
		 * ==>
 | 
						|
		 * L1:
 | 
						|
		 *   ...
 | 
						|
		 *   pop
 | 
						|
		 *   jump L1
 | 
						|
		 */
 | 
						|
		INSN *popiobj = new_insn_core(iseq, iobj->insn_info.line_no,
 | 
						|
					      BIN(pop), 0, 0);
 | 
						|
		ELEM_REPLACE(&piobj->link, &popiobj->link);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	if (remove_unreachable_chunk(iseq, iobj->link.next)) {
 | 
						|
	    goto again;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    /*
 | 
						|
     * putstring "beg"
 | 
						|
     * putstring "end"
 | 
						|
     * newrange excl
 | 
						|
     *
 | 
						|
     * ==>
 | 
						|
     *
 | 
						|
     * putobject "beg".."end"
 | 
						|
     */
 | 
						|
    if (IS_INSN_ID(iobj, checkmatch)) {
 | 
						|
	INSN *range = (INSN *)get_prev_insn(iobj);
 | 
						|
	INSN *beg, *end;
 | 
						|
 | 
						|
	if (range && IS_INSN_ID(range, newrange) &&
 | 
						|
		(end = (INSN *)get_prev_insn(range)) != 0 &&
 | 
						|
		IS_INSN_ID(end, putstring) &&
 | 
						|
		(beg = (INSN *)get_prev_insn(end)) != 0 &&
 | 
						|
		IS_INSN_ID(beg, putstring)) {
 | 
						|
	    VALUE str_beg = OPERAND_AT(beg, 0);
 | 
						|
	    VALUE str_end = OPERAND_AT(end, 0);
 | 
						|
	    int excl = FIX2INT(OPERAND_AT(range, 0));
 | 
						|
	    VALUE lit_range = rb_range_new(str_beg, str_end, excl);
 | 
						|
 | 
						|
	    iseq_add_mark_object_compile_time(iseq, lit_range);
 | 
						|
	    ELEM_REMOVE(&beg->link);
 | 
						|
	    ELEM_REMOVE(&end->link);
 | 
						|
	    range->insn_id = BIN(putobject);
 | 
						|
	    OPERAND_AT(range, 0) = lit_range;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    if (IS_INSN_ID(iobj, leave)) {
 | 
						|
	remove_unreachable_chunk(iseq, iobj->link.next);
 | 
						|
    }
 | 
						|
 | 
						|
    if (IS_INSN_ID(iobj, branchif) ||
 | 
						|
	IS_INSN_ID(iobj, branchnil) ||
 | 
						|
	IS_INSN_ID(iobj, branchunless)) {
 | 
						|
	/*
 | 
						|
	 *   if L1
 | 
						|
	 *   ...
 | 
						|
	 * L1:
 | 
						|
	 *   jump L2
 | 
						|
	 * =>
 | 
						|
	 *   if L2
 | 
						|
	 */
 | 
						|
	INSN *nobj = (INSN *)get_destination_insn(iobj);
 | 
						|
	INSN *pobj = (INSN *)iobj->link.prev;
 | 
						|
	int prev_dup = 0;
 | 
						|
	if (pobj) {
 | 
						|
	    if (!IS_INSN(&pobj->link))
 | 
						|
		pobj = 0;
 | 
						|
	    else if (IS_INSN_ID(pobj, dup))
 | 
						|
		prev_dup = 1;
 | 
						|
	}
 | 
						|
 | 
						|
	for (;;) {
 | 
						|
	    if (IS_INSN_ID(nobj, jump)) {
 | 
						|
		replace_destination(iobj, nobj);
 | 
						|
	    }
 | 
						|
	    else if (prev_dup && IS_INSN_ID(nobj, dup) &&
 | 
						|
		     !!(nobj = (INSN *)nobj->link.next) &&
 | 
						|
		     /* basic blocks, with no labels in the middle */
 | 
						|
		     nobj->insn_id == iobj->insn_id) {
 | 
						|
		/*
 | 
						|
		 *   dup
 | 
						|
		 *   if L1
 | 
						|
		 *   ...
 | 
						|
		 * L1:
 | 
						|
		 *   dup
 | 
						|
		 *   if L2
 | 
						|
		 * =>
 | 
						|
		 *   dup
 | 
						|
		 *   if L2
 | 
						|
		 *   ...
 | 
						|
		 * L1:
 | 
						|
		 *   dup
 | 
						|
		 *   if L2
 | 
						|
		 */
 | 
						|
		replace_destination(iobj, nobj);
 | 
						|
	    }
 | 
						|
	    else if (pobj) {
 | 
						|
		/*
 | 
						|
		 *   putnil
 | 
						|
		 *   if L1
 | 
						|
		 * =>
 | 
						|
		 *   # nothing
 | 
						|
		 *
 | 
						|
		 *   putobject true
 | 
						|
		 *   if L1
 | 
						|
		 * =>
 | 
						|
		 *   jump L1
 | 
						|
		 *
 | 
						|
		 *   putstring ".."
 | 
						|
		 *   if L1
 | 
						|
		 * =>
 | 
						|
		 *   jump L1
 | 
						|
		 *
 | 
						|
		 *   putstring ".."
 | 
						|
		 *   dup
 | 
						|
		 *   if L1
 | 
						|
		 * =>
 | 
						|
		 *   putstring ".."
 | 
						|
		 *   jump L1
 | 
						|
		 *
 | 
						|
		 */
 | 
						|
		int cond;
 | 
						|
		if (prev_dup && IS_INSN(pobj->link.prev)) {
 | 
						|
		    pobj = (INSN *)pobj->link.prev;
 | 
						|
		}
 | 
						|
		if (IS_INSN_ID(pobj, putobject)) {
 | 
						|
		    cond = (IS_INSN_ID(iobj, branchif) ?
 | 
						|
			    OPERAND_AT(pobj, 0) != Qfalse :
 | 
						|
			    IS_INSN_ID(iobj, branchunless) ?
 | 
						|
			    OPERAND_AT(pobj, 0) == Qfalse :
 | 
						|
			    FALSE);
 | 
						|
		}
 | 
						|
		else if (IS_INSN_ID(pobj, putstring) ||
 | 
						|
			 IS_INSN_ID(pobj, duparray) ||
 | 
						|
			 IS_INSN_ID(pobj, newarray)) {
 | 
						|
		    cond = IS_INSN_ID(iobj, branchif);
 | 
						|
		}
 | 
						|
		else if (IS_INSN_ID(pobj, putnil)) {
 | 
						|
		    cond = !IS_INSN_ID(iobj, branchif);
 | 
						|
		}
 | 
						|
		else break;
 | 
						|
		if (prev_dup || !IS_INSN_ID(pobj, newarray)) {
 | 
						|
		    ELEM_REMOVE(iobj->link.prev);
 | 
						|
		}
 | 
						|
		else if (!iseq_pop_newarray(iseq, pobj)) {
 | 
						|
		    pobj = new_insn_core(iseq, pobj->insn_info.line_no, BIN(pop), 0, NULL);
 | 
						|
		    ELEM_INSERT_NEXT(&iobj->link, &pobj->link);
 | 
						|
		}
 | 
						|
		if (cond) {
 | 
						|
		    iobj->insn_id = BIN(jump);
 | 
						|
		    goto again;
 | 
						|
		}
 | 
						|
		else {
 | 
						|
		    unref_destination(iobj, 0);
 | 
						|
		    ELEM_REMOVE(&iobj->link);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	    else break;
 | 
						|
	    nobj = (INSN *)get_destination_insn(nobj);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    if (IS_INSN_ID(iobj, pop)) {
 | 
						|
	/*
 | 
						|
	 *  putself / putnil / putobject obj / putstring "..."
 | 
						|
	 *  pop
 | 
						|
	 * =>
 | 
						|
	 *  # do nothing
 | 
						|
	 */
 | 
						|
	LINK_ELEMENT *prev = iobj->link.prev;
 | 
						|
	if (IS_INSN(prev)) {
 | 
						|
	    enum ruby_vminsn_type previ = ((INSN *)prev)->insn_id;
 | 
						|
	    if (previ == BIN(putobject) || previ == BIN(putnil) ||
 | 
						|
		previ == BIN(putself) || previ == BIN(putstring) ||
 | 
						|
		previ == BIN(duparray)) {
 | 
						|
		/* just push operand or static value and pop soon, no
 | 
						|
		 * side effects */
 | 
						|
		ELEM_REMOVE(prev);
 | 
						|
		ELEM_REMOVE(&iobj->link);
 | 
						|
	    }
 | 
						|
	    else if (previ == BIN(newarray) && iseq_pop_newarray(iseq, (INSN*)prev)) {
 | 
						|
		ELEM_REMOVE(&iobj->link);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    if (IS_INSN_ID(iobj, newarray) ||
 | 
						|
	IS_INSN_ID(iobj, duparray) ||
 | 
						|
	IS_INSN_ID(iobj, expandarray) ||
 | 
						|
	IS_INSN_ID(iobj, concatarray) ||
 | 
						|
	IS_INSN_ID(iobj, splatarray) ||
 | 
						|
	0) {
 | 
						|
	/*
 | 
						|
	 *  newarray N
 | 
						|
	 *  splatarray
 | 
						|
	 * =>
 | 
						|
	 *  newarray N
 | 
						|
	 * newarray always puts an array
 | 
						|
	 */
 | 
						|
	LINK_ELEMENT *next = iobj->link.next;
 | 
						|
	if (IS_INSN(next) && IS_INSN_ID(next, splatarray)) {
 | 
						|
	    /* remove splatarray following always-array insn */
 | 
						|
	    ELEM_REMOVE(next);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    if (IS_INSN_ID(iobj, tostring)) {
 | 
						|
	LINK_ELEMENT *next = iobj->link.next;
 | 
						|
	/*
 | 
						|
	 *  tostring
 | 
						|
	 *  concatstrings 1
 | 
						|
	 * =>
 | 
						|
	 *  tostring
 | 
						|
	 */
 | 
						|
	if (IS_INSN(next) && IS_INSN_ID(next, concatstrings) &&
 | 
						|
	    OPERAND_AT(next, 0) == INT2FIX(1)) {
 | 
						|
	    ELEM_REMOVE(next);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    if (IS_INSN_ID(iobj, putstring) ||
 | 
						|
	(IS_INSN_ID(iobj, putobject) && RB_TYPE_P(OPERAND_AT(iobj, 0), T_STRING))) {
 | 
						|
	/*
 | 
						|
	 *  putstring ""
 | 
						|
	 *  concatstrings N
 | 
						|
	 * =>
 | 
						|
	 *  concatstrings N-1
 | 
						|
	 */
 | 
						|
	if (IS_NEXT_INSN_ID(&iobj->link, concatstrings) &&
 | 
						|
	    RSTRING_LEN(OPERAND_AT(iobj, 0)) == 0) {
 | 
						|
	    INSN *next = (INSN *)iobj->link.next;
 | 
						|
	    if ((OPERAND_AT(next, 0) = FIXNUM_INC(OPERAND_AT(next, 0), -1)) == INT2FIX(1)) {
 | 
						|
		ELEM_REMOVE(&next->link);
 | 
						|
	    }
 | 
						|
	    ELEM_REMOVE(&iobj->link);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    if (IS_INSN_ID(iobj, concatstrings)) {
 | 
						|
	/*
 | 
						|
	 *  concatstrings N
 | 
						|
	 *  concatstrings M
 | 
						|
	 * =>
 | 
						|
	 *  concatstrings N+M-1
 | 
						|
	 */
 | 
						|
	LINK_ELEMENT *next = iobj->link.next, *freeze = 0;
 | 
						|
	INSN *jump = 0;
 | 
						|
	if (IS_INSN(next) && IS_INSN_ID(next, freezestring))
 | 
						|
	    next = (freeze = next)->next;
 | 
						|
	if (IS_INSN(next) && IS_INSN_ID(next, jump))
 | 
						|
	    next = get_destination_insn(jump = (INSN *)next);
 | 
						|
	if (IS_INSN(next) && IS_INSN_ID(next, concatstrings)) {
 | 
						|
	    int n = FIX2INT(OPERAND_AT(iobj, 0)) + FIX2INT(OPERAND_AT(next, 0)) - 1;
 | 
						|
	    OPERAND_AT(iobj, 0) = INT2FIX(n);
 | 
						|
	    if (jump) {
 | 
						|
		LABEL *label = ((LABEL *)OPERAND_AT(jump, 0));
 | 
						|
		if (!--label->refcnt) {
 | 
						|
		    ELEM_REMOVE(&label->link);
 | 
						|
		}
 | 
						|
		else {
 | 
						|
		    label = NEW_LABEL(0);
 | 
						|
		    OPERAND_AT(jump, 0) = (VALUE)label;
 | 
						|
		}
 | 
						|
		label->refcnt++;
 | 
						|
		if (freeze && IS_NEXT_INSN_ID(next, freezestring)) {
 | 
						|
		    if (same_debug_pos_p(freeze, next->next)) {
 | 
						|
			ELEM_REMOVE(freeze);
 | 
						|
		    }
 | 
						|
		    else {
 | 
						|
			next = next->next;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
		ELEM_INSERT_NEXT(next, &label->link);
 | 
						|
		CHECK(iseq_peephole_optimize(iseq, get_next_insn(jump), do_tailcallopt));
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		if (freeze) ELEM_REMOVE(freeze);
 | 
						|
		ELEM_REMOVE(next);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    if (do_tailcallopt &&
 | 
						|
	(IS_INSN_ID(iobj, send) ||
 | 
						|
	 IS_INSN_ID(iobj, opt_aref_with) ||
 | 
						|
	 IS_INSN_ID(iobj, opt_aset_with) ||
 | 
						|
	 IS_INSN_ID(iobj, invokesuper))) {
 | 
						|
	/*
 | 
						|
	 *  send ...
 | 
						|
	 *  leave
 | 
						|
	 * =>
 | 
						|
	 *  send ..., ... | VM_CALL_TAILCALL, ...
 | 
						|
	 *  leave # unreachable
 | 
						|
	 */
 | 
						|
	INSN *piobj = NULL;
 | 
						|
	if (iobj->link.next) {
 | 
						|
	    LINK_ELEMENT *next = iobj->link.next;
 | 
						|
	    do {
 | 
						|
		if (!IS_INSN(next)) {
 | 
						|
		    next = next->next;
 | 
						|
		    continue;
 | 
						|
		}
 | 
						|
		switch (INSN_OF(next)) {
 | 
						|
		  case BIN(nop):
 | 
						|
		    next = next->next;
 | 
						|
		    break;
 | 
						|
		  case BIN(jump):
 | 
						|
		    /* if cond
 | 
						|
		     *   return tailcall
 | 
						|
		     * end
 | 
						|
		     */
 | 
						|
		    next = get_destination_insn((INSN *)next);
 | 
						|
		    break;
 | 
						|
		  case BIN(leave):
 | 
						|
		    piobj = iobj;
 | 
						|
		  default:
 | 
						|
		    next = NULL;
 | 
						|
		    break;
 | 
						|
		}
 | 
						|
	    } while (next);
 | 
						|
	}
 | 
						|
 | 
						|
	if (piobj) {
 | 
						|
	    struct rb_call_info *ci = (struct rb_call_info *)piobj->operands[0];
 | 
						|
	    if (IS_INSN_ID(piobj, send) || IS_INSN_ID(piobj, invokesuper)) {
 | 
						|
		if (piobj->operands[2] == 0) { /* no blockiseq */
 | 
						|
		    ci->flag |= VM_CALL_TAILCALL;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		ci->flag |= VM_CALL_TAILCALL;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    if (IS_INSN_ID(iobj, dup)) {
 | 
						|
	if (IS_NEXT_INSN_ID(&iobj->link, setlocal)) {
 | 
						|
	    LINK_ELEMENT *set1 = iobj->link.next, *set2 = NULL;
 | 
						|
	    if (IS_NEXT_INSN_ID(set1, setlocal)) {
 | 
						|
		set2 = set1->next;
 | 
						|
		if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) &&
 | 
						|
		    OPERAND_AT(set1, 1) == OPERAND_AT(set2, 1)) {
 | 
						|
		    ELEM_REMOVE(set1);
 | 
						|
		    ELEM_REMOVE(&iobj->link);
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	    else if (IS_NEXT_INSN_ID(set1, dup) &&
 | 
						|
		     IS_NEXT_INSN_ID(set1->next, setlocal)) {
 | 
						|
		set2 = set1->next->next;
 | 
						|
		if (OPERAND_AT(set1, 0) == OPERAND_AT(set2, 0) &&
 | 
						|
		    OPERAND_AT(set1, 1) == OPERAND_AT(set2, 1)) {
 | 
						|
		    ELEM_REMOVE(set1->next);
 | 
						|
		    ELEM_REMOVE(set2);
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    if (IS_INSN_ID(iobj, getlocal)) {
 | 
						|
	LINK_ELEMENT *niobj = &iobj->link;
 | 
						|
	if (IS_NEXT_INSN_ID(niobj, dup)) {
 | 
						|
	    niobj = niobj->next;
 | 
						|
	}
 | 
						|
	if (IS_NEXT_INSN_ID(niobj, setlocal)) {
 | 
						|
	    LINK_ELEMENT *set1 = niobj->next;
 | 
						|
	    if (OPERAND_AT(iobj, 0) == OPERAND_AT(set1, 0) &&
 | 
						|
		OPERAND_AT(iobj, 1) == OPERAND_AT(set1, 1)) {
 | 
						|
		ELEM_REMOVE(set1);
 | 
						|
		ELEM_REMOVE(niobj);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
insn_set_specialized_instruction(rb_iseq_t *iseq, INSN *iobj, int insn_id)
 | 
						|
{
 | 
						|
    iobj->insn_id = insn_id;
 | 
						|
    iobj->operand_size = insn_len(insn_id) - 1;
 | 
						|
 | 
						|
    if (insn_id == BIN(opt_neq)) {
 | 
						|
	VALUE *old_operands = iobj->operands;
 | 
						|
	iobj->operand_size = 4;
 | 
						|
	iobj->operands = (VALUE *)compile_data_alloc(iseq, iobj->operand_size * sizeof(VALUE));
 | 
						|
	iobj->operands[0] = old_operands[0];
 | 
						|
	iobj->operands[1] = Qfalse; /* CALL_CACHE */
 | 
						|
	iobj->operands[2] = (VALUE)new_callinfo(iseq, idEq, 1, 0, NULL, FALSE);
 | 
						|
	iobj->operands[3] = Qfalse; /* CALL_CACHE */
 | 
						|
    }
 | 
						|
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
iseq_specialized_instruction(rb_iseq_t *iseq, INSN *iobj)
 | 
						|
{
 | 
						|
    if (IS_INSN_ID(iobj, newarray) && iobj->link.next &&
 | 
						|
	IS_INSN(iobj->link.next)) {
 | 
						|
	/*
 | 
						|
	 *   [a, b, ...].max/min -> a, b, c, opt_newarray_max/min
 | 
						|
	 */
 | 
						|
	INSN *niobj = (INSN *)iobj->link.next;
 | 
						|
	if (IS_INSN_ID(niobj, send)) {
 | 
						|
	    struct rb_call_info *ci = (struct rb_call_info *)OPERAND_AT(niobj, 0);
 | 
						|
	    if ((ci->flag & VM_CALL_ARGS_SIMPLE) && ci->orig_argc == 0) {
 | 
						|
		switch (ci->mid) {
 | 
						|
		  case idMax:
 | 
						|
		    iobj->insn_id = BIN(opt_newarray_max);
 | 
						|
		    ELEM_REMOVE(&niobj->link);
 | 
						|
		    return COMPILE_OK;
 | 
						|
		  case idMin:
 | 
						|
		    iobj->insn_id = BIN(opt_newarray_min);
 | 
						|
		    ELEM_REMOVE(&niobj->link);
 | 
						|
		    return COMPILE_OK;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    if (IS_INSN_ID(iobj, send)) {
 | 
						|
	struct rb_call_info *ci = (struct rb_call_info *)OPERAND_AT(iobj, 0);
 | 
						|
	const rb_iseq_t *blockiseq = (rb_iseq_t *)OPERAND_AT(iobj, 2);
 | 
						|
 | 
						|
#define SP_INSN(opt) insn_set_specialized_instruction(iseq, iobj, BIN(opt_##opt))
 | 
						|
	if (ci->flag & VM_CALL_ARGS_SIMPLE) {
 | 
						|
	    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;
 | 
						|
	      case 2:
 | 
						|
		switch (ci->mid) {
 | 
						|
		  case idASET:	 SP_INSN(aset);	  return COMPILE_OK;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
	if ((ci->flag & VM_CALL_ARGS_BLOCKARG) == 0 && blockiseq == NULL) {
 | 
						|
	    iobj->insn_id = BIN(opt_send_without_block);
 | 
						|
	    iobj->operand_size = insn_len(iobj->insn_id) - 1;
 | 
						|
	}
 | 
						|
    }
 | 
						|
#undef SP_INSN
 | 
						|
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static inline int
 | 
						|
tailcallable_p(rb_iseq_t *iseq)
 | 
						|
{
 | 
						|
    switch (iseq->body->type) {
 | 
						|
      case ISEQ_TYPE_TOP:
 | 
						|
      case ISEQ_TYPE_EVAL:
 | 
						|
      case ISEQ_TYPE_MAIN:
 | 
						|
	/* not tail callable because cfp will be over popped */
 | 
						|
      case ISEQ_TYPE_RESCUE:
 | 
						|
      case ISEQ_TYPE_ENSURE:
 | 
						|
	/* rescue block can't tail call because of errinfo */
 | 
						|
	return FALSE;
 | 
						|
      default:
 | 
						|
	return TRUE;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
iseq_optimize(rb_iseq_t *iseq, LINK_ANCHOR *const anchor)
 | 
						|
{
 | 
						|
    LINK_ELEMENT *list;
 | 
						|
    const int do_peepholeopt = ISEQ_COMPILE_DATA(iseq)->option->peephole_optimization;
 | 
						|
    const int do_tailcallopt = tailcallable_p(iseq) &&
 | 
						|
	ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization;
 | 
						|
    const int do_si = ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction;
 | 
						|
    const int do_ou = ISEQ_COMPILE_DATA(iseq)->option->operands_unification;
 | 
						|
    int rescue_level = 0;
 | 
						|
    int tailcallopt = do_tailcallopt;
 | 
						|
 | 
						|
    list = FIRST_ELEMENT(anchor);
 | 
						|
 | 
						|
    while (list) {
 | 
						|
	if (IS_INSN(list)) {
 | 
						|
	    if (do_peepholeopt) {
 | 
						|
		iseq_peephole_optimize(iseq, list, tailcallopt);
 | 
						|
	    }
 | 
						|
	    if (do_si) {
 | 
						|
		iseq_specialized_instruction(iseq, (INSN *)list);
 | 
						|
	    }
 | 
						|
	    if (do_ou) {
 | 
						|
		insn_operands_unification((INSN *)list);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	if (IS_LABEL(list)) {
 | 
						|
	    switch (((LABEL *)list)->rescued) {
 | 
						|
	      case LABEL_RESCUE_BEG:
 | 
						|
		rescue_level++;
 | 
						|
		tailcallopt = FALSE;
 | 
						|
		break;
 | 
						|
	      case LABEL_RESCUE_END:
 | 
						|
		if (!--rescue_level) tailcallopt = do_tailcallopt;
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	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->insn_info.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 *const anchor)
 | 
						|
{
 | 
						|
#if OPT_INSTRUCTIONS_UNIFICATION
 | 
						|
    LINK_ELEMENT *list;
 | 
						|
    INSN *iobj, *niobj;
 | 
						|
    int id, k;
 | 
						|
    intptr_t j;
 | 
						|
 | 
						|
    list = FIRST_ELEMENT(anchor);
 | 
						|
    while (list) {
 | 
						|
	if (IS_INSN(list)) {
 | 
						|
	    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 (!IS_INSN(li) ||
 | 
						|
			    ((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, const LINK_ELEMENT *anchor, 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) {
 | 
						|
		BADINSN_DUMP(anchor, iobj, lobj);
 | 
						|
		COMPILE_ERROR(iseq, iobj->insn_info.line_no,
 | 
						|
			      "insn_set_sc_state error: %d at "LABEL_FORMAT
 | 
						|
			      ", %d expected\n",
 | 
						|
			      lobj->sc_state, lobj->label_no, nstate);
 | 
						|
		return COMPILE_NG;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	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 *const 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 */
 | 
						|
			    ELEM_REPLACE(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);
 | 
						|
 | 
						|
			    ELEM_REMOVE(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:
 | 
						|
			    COMPILE_ERROR(iseq, iobj->insn_info.line_no,
 | 
						|
					  "unreachable");
 | 
						|
			    return COMPILE_NG;
 | 
						|
			}
 | 
						|
			/* remove useless pop */
 | 
						|
			ELEM_REMOVE(list);
 | 
						|
			list = list->next;
 | 
						|
			goto redo_point;
 | 
						|
		    }
 | 
						|
		  default:;
 | 
						|
		    /* none */
 | 
						|
		}		/* end of switch */
 | 
						|
	      normal_insn:
 | 
						|
		state = insn_set_sc_state(iseq, anchor, 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
 | 
						|
all_string_result_p(const NODE *node)
 | 
						|
{
 | 
						|
    if (!node) return FALSE;
 | 
						|
    switch (nd_type(node)) {
 | 
						|
      case NODE_STR: case NODE_DSTR:
 | 
						|
	return TRUE;
 | 
						|
      case NODE_IF: case NODE_UNLESS:
 | 
						|
	if (!node->nd_body || !node->nd_else) return FALSE;
 | 
						|
	if (all_string_result_p(node->nd_body))
 | 
						|
	    return all_string_result_p(node->nd_else);
 | 
						|
	return FALSE;
 | 
						|
      case NODE_AND: case NODE_OR:
 | 
						|
	if (!node->nd_2nd)
 | 
						|
	    return all_string_result_p(node->nd_1st);
 | 
						|
	if (!all_string_result_p(node->nd_1st))
 | 
						|
	    return FALSE;
 | 
						|
	return all_string_result_p(node->nd_2nd);
 | 
						|
      default:
 | 
						|
	return FALSE;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_dstr_fragments(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int *cntp)
 | 
						|
{
 | 
						|
    const NODE *list = node->nd_next;
 | 
						|
    VALUE lit = node->nd_lit;
 | 
						|
    LINK_ELEMENT *first_lit = 0;
 | 
						|
    int cnt = 0;
 | 
						|
 | 
						|
    debugp_param("nd_lit", lit);
 | 
						|
    if (!NIL_P(lit)) {
 | 
						|
	cnt++;
 | 
						|
	if (!RB_TYPE_P(lit, T_STRING)) {
 | 
						|
	    COMPILE_ERROR(ERROR_ARGS "dstr: must be string: %s",
 | 
						|
			  rb_builtin_type_name(TYPE(lit)));
 | 
						|
	    return COMPILE_NG;
 | 
						|
	}
 | 
						|
	lit = freeze_literal(iseq, lit);
 | 
						|
	ADD_INSN1(ret, nd_line(node), putobject, lit);
 | 
						|
	if (RSTRING_LEN(lit) == 0) first_lit = LAST_ELEMENT(ret);
 | 
						|
    }
 | 
						|
 | 
						|
    while (list) {
 | 
						|
	const NODE *const head = list->nd_head;
 | 
						|
	if (nd_type(head) == NODE_STR) {
 | 
						|
	    lit = freeze_literal(iseq, head->nd_lit);
 | 
						|
	    ADD_INSN1(ret, nd_line(head), putobject, lit);
 | 
						|
	    lit = Qnil;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    CHECK(COMPILE(ret, "each string", head));
 | 
						|
	}
 | 
						|
	cnt++;
 | 
						|
	list = list->nd_next;
 | 
						|
    }
 | 
						|
    if (NIL_P(lit) && first_lit) {
 | 
						|
	ELEM_REMOVE(first_lit);
 | 
						|
	--cnt;
 | 
						|
    }
 | 
						|
    *cntp = cnt;
 | 
						|
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_dstr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
 | 
						|
{
 | 
						|
    int cnt;
 | 
						|
    CHECK(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 *const ret, const NODE *const node)
 | 
						|
{
 | 
						|
    int cnt;
 | 
						|
    CHECK(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_flip_flop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int again,
 | 
						|
		  LABEL *then_label, LABEL *else_label)
 | 
						|
{
 | 
						|
    const int line = nd_line(node);
 | 
						|
    LABEL *lend = NEW_LABEL(line);
 | 
						|
    rb_num_t cnt = ISEQ_FLIP_CNT_INCREMENT(iseq->body->local_iseq)
 | 
						|
	+ VM_SVAR_FLIPFLOP_START;
 | 
						|
    VALUE key = INT2FIX(cnt);
 | 
						|
 | 
						|
    ADD_INSN2(ret, line, getspecial, key, INT2FIX(0));
 | 
						|
    ADD_INSNL(ret, line, branchif, lend);
 | 
						|
 | 
						|
    /* *flip == 0 */
 | 
						|
    CHECK(COMPILE(ret, "flip2 beg", node->nd_beg));
 | 
						|
    ADD_INSNL(ret, line, branchunless, else_label);
 | 
						|
    ADD_INSN1(ret, line, putobject, Qtrue);
 | 
						|
    ADD_INSN1(ret, line, setspecial, key);
 | 
						|
    if (!again) {
 | 
						|
	ADD_INSNL(ret, line, jump, then_label);
 | 
						|
    }
 | 
						|
 | 
						|
    /* *flip == 1 */
 | 
						|
    ADD_LABEL(ret, lend);
 | 
						|
    CHECK(COMPILE(ret, "flip2 end", node->nd_end));
 | 
						|
    ADD_INSNL(ret, line, branchunless, then_label);
 | 
						|
    ADD_INSN1(ret, line, putobject, Qfalse);
 | 
						|
    ADD_INSN1(ret, line, setspecial, key);
 | 
						|
    ADD_INSNL(ret, line, jump, then_label);
 | 
						|
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_branch_condition(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *cond,
 | 
						|
			 LABEL *then_label, LABEL *else_label)
 | 
						|
{
 | 
						|
  again:
 | 
						|
    switch (nd_type(cond)) {
 | 
						|
      case NODE_AND:
 | 
						|
	{
 | 
						|
	    LABEL *label = NEW_LABEL(nd_line(cond));
 | 
						|
	    CHECK(compile_branch_condition(iseq, ret, cond->nd_1st, label,
 | 
						|
					   else_label));
 | 
						|
	    if (!label->refcnt) break;
 | 
						|
	    ADD_LABEL(ret, label);
 | 
						|
	    cond = cond->nd_2nd;
 | 
						|
	    goto again;
 | 
						|
	}
 | 
						|
      case NODE_OR:
 | 
						|
	{
 | 
						|
	    LABEL *label = NEW_LABEL(nd_line(cond));
 | 
						|
	    CHECK(compile_branch_condition(iseq, ret, cond->nd_1st, then_label,
 | 
						|
					   label));
 | 
						|
	    if (!label->refcnt) break;
 | 
						|
	    ADD_LABEL(ret, label);
 | 
						|
	    cond = cond->nd_2nd;
 | 
						|
	    goto again;
 | 
						|
	}
 | 
						|
      case NODE_LIT:		/* NODE_LIT is always true */
 | 
						|
      case NODE_TRUE:
 | 
						|
      case NODE_STR:
 | 
						|
      case NODE_ZARRAY:
 | 
						|
      case NODE_LAMBDA:
 | 
						|
	/* 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;
 | 
						|
      case NODE_FLIP2:
 | 
						|
	CHECK(compile_flip_flop(iseq, ret, cond, TRUE, then_label, else_label));
 | 
						|
	break;
 | 
						|
      case NODE_FLIP3:
 | 
						|
	CHECK(compile_flip_flop(iseq, ret, cond, FALSE, then_label, else_label));
 | 
						|
	break;
 | 
						|
      case NODE_DEFINED:
 | 
						|
	CHECK(compile_defined_expr(iseq, ret, cond, Qfalse));
 | 
						|
	goto branch;
 | 
						|
      default:
 | 
						|
	CHECK(COMPILE(ret, "branch condition", cond));
 | 
						|
      branch:
 | 
						|
	ADD_INSNL(ret, nd_line(cond), branchunless, else_label);
 | 
						|
	ADD_INSNL(ret, nd_line(cond), jump, then_label);
 | 
						|
	break;
 | 
						|
    }
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_array_keyword_arg(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
 | 
						|
			  const NODE *const root_node,
 | 
						|
			  struct rb_call_info_kw_arg **const kw_arg_ptr,
 | 
						|
			  unsigned int *flag)
 | 
						|
{
 | 
						|
    if (kw_arg_ptr == NULL) return FALSE;
 | 
						|
 | 
						|
    if (nd_type(root_node) == NODE_HASH && root_node->nd_head && nd_type(root_node->nd_head) == NODE_ARRAY) {
 | 
						|
	const NODE *node = root_node->nd_head;
 | 
						|
 | 
						|
	while (node) {
 | 
						|
	    const NODE *key_node = node->nd_head;
 | 
						|
 | 
						|
	    assert(nd_type(node) == NODE_ARRAY);
 | 
						|
	    if (!key_node) {
 | 
						|
		if (flag && !root_node->nd_alen) *flag |= VM_CALL_KW_SPLAT;
 | 
						|
		return FALSE;
 | 
						|
	    }
 | 
						|
	    else if (nd_type(key_node) == NODE_LIT && RB_TYPE_P(key_node->nd_lit, T_SYMBOL)) {
 | 
						|
		/* can be keywords */
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		return FALSE;
 | 
						|
	    }
 | 
						|
	    node = node->nd_next; /* skip value node */
 | 
						|
	    node = node->nd_next;
 | 
						|
	}
 | 
						|
 | 
						|
	/* may be keywords */
 | 
						|
	node = root_node->nd_head;
 | 
						|
	{
 | 
						|
	    int len = (int)node->nd_alen / 2;
 | 
						|
	    struct rb_call_info_kw_arg *kw_arg  = (struct rb_call_info_kw_arg *)ruby_xmalloc(sizeof(struct rb_call_info_kw_arg) + sizeof(VALUE) * (len - 1));
 | 
						|
	    VALUE *keywords = kw_arg->keywords;
 | 
						|
	    int i = 0;
 | 
						|
	    kw_arg->keyword_len = len;
 | 
						|
 | 
						|
	    *kw_arg_ptr = kw_arg;
 | 
						|
 | 
						|
	    for (i=0; node != NULL; i++, node = node->nd_next->nd_next) {
 | 
						|
		const NODE *key_node = node->nd_head;
 | 
						|
		const NODE *val_node = node->nd_next->nd_head;
 | 
						|
		keywords[i] = key_node->nd_lit;
 | 
						|
		COMPILE(ret, "keyword values", val_node);
 | 
						|
	    }
 | 
						|
	    assert(i == len);
 | 
						|
	    return TRUE;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
enum compile_array_type_t {
 | 
						|
    COMPILE_ARRAY_TYPE_ARRAY,
 | 
						|
    COMPILE_ARRAY_TYPE_HASH,
 | 
						|
    COMPILE_ARRAY_TYPE_ARGS
 | 
						|
};
 | 
						|
 | 
						|
static inline int
 | 
						|
static_literal_node_p(const NODE *node)
 | 
						|
{
 | 
						|
    node = node->nd_head;
 | 
						|
    switch (nd_type(node)) {
 | 
						|
      case NODE_LIT:
 | 
						|
      case NODE_NIL:
 | 
						|
      case NODE_TRUE:
 | 
						|
      case NODE_FALSE:
 | 
						|
	return TRUE;
 | 
						|
      default:
 | 
						|
	return FALSE;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static inline VALUE
 | 
						|
static_literal_value(const NODE *node)
 | 
						|
{
 | 
						|
    node = node->nd_head;
 | 
						|
    switch (nd_type(node)) {
 | 
						|
      case NODE_NIL:
 | 
						|
	return Qnil;
 | 
						|
      case NODE_TRUE:
 | 
						|
	return Qtrue;
 | 
						|
      case NODE_FALSE:
 | 
						|
	return Qfalse;
 | 
						|
      default:
 | 
						|
	return node->nd_lit;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_array(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node_root,
 | 
						|
	      enum compile_array_type_t type, struct rb_call_info_kw_arg **keywords_ptr,
 | 
						|
	      unsigned int *flag, int popped)
 | 
						|
{
 | 
						|
    const NODE *node = node_root;
 | 
						|
    int line = (int)nd_line(node);
 | 
						|
    int len = 0;
 | 
						|
 | 
						|
    if (nd_type(node) == NODE_ZARRAY) {
 | 
						|
	if (!popped) {
 | 
						|
	    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) {
 | 
						|
	    const NODE *start_node = node, *end_node;
 | 
						|
	    const 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) {
 | 
						|
		    EXPECT_NODE("compile_array", node, NODE_ARRAY, -1);
 | 
						|
		}
 | 
						|
 | 
						|
		if (type != COMPILE_ARRAY_TYPE_ARRAY && !node->nd_head) {
 | 
						|
		    kw = node->nd_next;
 | 
						|
		    node = 0;
 | 
						|
		    if (kw) {
 | 
						|
			opt_p = 0;
 | 
						|
			node = kw->nd_next;
 | 
						|
			kw = kw->nd_head;
 | 
						|
		    }
 | 
						|
		    break;
 | 
						|
		}
 | 
						|
		if (opt_p && !static_literal_node_p(node)) {
 | 
						|
		    opt_p = 0;
 | 
						|
		}
 | 
						|
 | 
						|
		if (type == COMPILE_ARRAY_TYPE_ARGS &&
 | 
						|
		    node->nd_next == NULL /* last node */ &&
 | 
						|
		    compile_array_keyword_arg(iseq, anchor, node->nd_head, keywords_ptr, flag)) {
 | 
						|
		    len--;
 | 
						|
		}
 | 
						|
		else {
 | 
						|
		    COMPILE_(anchor, "array element", node->nd_head, popped);
 | 
						|
		}
 | 
						|
	    }
 | 
						|
 | 
						|
	    if (opt_p && type != COMPILE_ARRAY_TYPE_ARGS) {
 | 
						|
		if (!popped) {
 | 
						|
		    VALUE ary = rb_ary_tmp_new(i);
 | 
						|
 | 
						|
		    end_node = node;
 | 
						|
		    node = start_node;
 | 
						|
 | 
						|
		    while (node != end_node) {
 | 
						|
			rb_ary_push(ary, static_literal_value(node));
 | 
						|
			node = node->nd_next;
 | 
						|
		    }
 | 
						|
		    while (node && node->nd_next &&
 | 
						|
			   static_literal_node_p(node) &&
 | 
						|
			   static_literal_node_p(node->nd_next)) {
 | 
						|
			VALUE elem[2];
 | 
						|
			elem[0] = static_literal_value(node);
 | 
						|
			elem[1] = static_literal_value(node->nd_next);
 | 
						|
			rb_ary_cat(ary, elem, 2);
 | 
						|
			node = node->nd_next->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, 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 {
 | 
						|
#if 0
 | 
						|
			    ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | 
						|
			    ADD_INSN1(ret, line, putobject, ary);
 | 
						|
			    ADD_SEND(ret, line, id_core_hash_merge_ary, INT2FIX(1));
 | 
						|
			    /* wrong number of arguments -----------------------^ */
 | 
						|
#else
 | 
						|
			    COMPILE_ERROR(ERROR_ARGS "core#hash_merge_ary");
 | 
						|
			    return -1;
 | 
						|
#endif
 | 
						|
			}
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		if (!popped || kw) {
 | 
						|
		    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 (i > 0) {
 | 
						|
			    if (first) {
 | 
						|
				ADD_INSN1(anchor, line, newhash, INT2FIX(i));
 | 
						|
				APPEND_LIST(ret, anchor);
 | 
						|
			    }
 | 
						|
			    else {
 | 
						|
				ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | 
						|
				ADD_INSN(ret, line, swap);
 | 
						|
				APPEND_LIST(ret, anchor);
 | 
						|
				ADD_SEND(ret, line, id_core_hash_merge_ptr, INT2FIX(i + 1));
 | 
						|
			    }
 | 
						|
			}
 | 
						|
			if (kw) {
 | 
						|
			    VALUE nhash = (i > 0 || !first) ? INT2FIX(2) : INT2FIX(1);
 | 
						|
			    if (!popped) {
 | 
						|
				ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | 
						|
				if (i > 0 || !first) ADD_INSN(ret, line, swap);
 | 
						|
			    }
 | 
						|
			    COMPILE(ret, "keyword splat", kw);
 | 
						|
			    if (popped) {
 | 
						|
				ADD_INSN(ret, line, pop);
 | 
						|
			    }
 | 
						|
			    else {
 | 
						|
				ADD_SEND(ret, line, id_core_hash_merge_kwd, nhash);
 | 
						|
				if (nhash == INT2FIX(1)) ADD_SEND(ret, line, rb_intern("dup"), INT2FIX(0));
 | 
						|
			    }
 | 
						|
			}
 | 
						|
			first = 0;
 | 
						|
			break;
 | 
						|
		      case COMPILE_ARRAY_TYPE_ARGS:
 | 
						|
			APPEND_LIST(ret, anchor);
 | 
						|
			break;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
		else {
 | 
						|
		    /* popped */
 | 
						|
		    APPEND_LIST(ret, anchor);
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
    return len;
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
case_when_optimizable_literal(const NODE *const 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_obj_is_kind_of(v, rb_cNumeric)) {
 | 
						|
	    return v;
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_NIL:
 | 
						|
	return Qnil;
 | 
						|
      case NODE_TRUE:
 | 
						|
	return Qtrue;
 | 
						|
      case NODE_FALSE:
 | 
						|
	return Qfalse;
 | 
						|
      case NODE_STR:
 | 
						|
	return rb_fstring(node->nd_lit);
 | 
						|
    }
 | 
						|
    return Qundef;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
when_vals(rb_iseq_t *iseq, LINK_ANCHOR *const cond_seq, const NODE *vals,
 | 
						|
	  LABEL *l1, int only_special_literals, VALUE literals)
 | 
						|
{
 | 
						|
    while (vals) {
 | 
						|
	const 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) {
 | 
						|
		VALUE file = rb_iseq_path(iseq);
 | 
						|
		rb_compile_warning(RSTRING_PTR(file), 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);
 | 
						|
	    lit = freeze_literal(iseq, val->nd_lit);
 | 
						|
	    ADD_INSN1(cond_seq, nd_line(val), putobject, 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 *const ret, const NODE *const node)
 | 
						|
{
 | 
						|
    switch (nd_type(node)) {
 | 
						|
      case NODE_ATTRASGN: {
 | 
						|
	INSN *iobj;
 | 
						|
	struct rb_call_info *ci;
 | 
						|
	VALUE dupidx;
 | 
						|
	int line = nd_line(node);
 | 
						|
 | 
						|
	CHECK(COMPILE_POPPED(ret, "masgn lhs (NODE_ATTRASGN)", node));
 | 
						|
 | 
						|
	iobj = (INSN *)get_prev_insn((INSN *)LAST_ELEMENT(ret)); /* send insn */
 | 
						|
	ci = (struct rb_call_info *)iobj->operands[0];
 | 
						|
	ci->orig_argc += 1;
 | 
						|
	dupidx = INT2FIX(ci->orig_argc);
 | 
						|
 | 
						|
	INSERT_BEFORE_INSN1(iobj, line, topn, dupidx);
 | 
						|
	if (ci->flag & VM_CALL_ARGS_SPLAT) {
 | 
						|
	    --ci->orig_argc;
 | 
						|
	    INSERT_BEFORE_INSN1(iobj, line, newarray, INT2FIX(1));
 | 
						|
	    INSERT_BEFORE_INSN(iobj, line, concatarray);
 | 
						|
	}
 | 
						|
	ADD_INSN(ret, line, pop);	/* result */
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_MASGN: {
 | 
						|
	DECL_ANCHOR(anchor);
 | 
						|
	INIT_ANCHOR(anchor);
 | 
						|
	CHECK(COMPILE_POPPED(anchor, "nest masgn lhs", node));
 | 
						|
	ELEM_REMOVE(FIRST_ELEMENT(anchor));
 | 
						|
	ADD_SEQ(ret, anchor);
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      default: {
 | 
						|
	DECL_ANCHOR(anchor);
 | 
						|
	INIT_ANCHOR(anchor);
 | 
						|
	CHECK(COMPILE_POPPED(anchor, "masgn lhs", node));
 | 
						|
	ELEM_REMOVE(FIRST_ELEMENT(anchor));
 | 
						|
	ADD_SEQ(ret, anchor);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_massign_opt_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *lhsn)
 | 
						|
{
 | 
						|
    if (lhsn) {
 | 
						|
	CHECK(compile_massign_opt_lhs(iseq, ret, lhsn->nd_next));
 | 
						|
	CHECK(compile_massign_lhs(iseq, ret, lhsn->nd_head));
 | 
						|
    }
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_massign_opt(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
 | 
						|
		    const NODE *rhsn, const NODE *orig_lhsn)
 | 
						|
{
 | 
						|
    VALUE mem[64];
 | 
						|
    const int memsize = numberof(mem);
 | 
						|
    int memindex = 0;
 | 
						|
    int llen = 0, rlen = 0;
 | 
						|
    int i;
 | 
						|
    const 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) {
 | 
						|
	const 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_CVASGN:
 | 
						|
	    MEMORY(ln->nd_vid);
 | 
						|
	    break;
 | 
						|
	  default:
 | 
						|
	    return 0;
 | 
						|
	}
 | 
						|
	lhsn = lhsn->nd_next;
 | 
						|
	llen++;
 | 
						|
    }
 | 
						|
 | 
						|
    while (rhsn) {
 | 
						|
	if (llen <= rlen) {
 | 
						|
	    COMPILE_POPPED(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 void
 | 
						|
adjust_stack(rb_iseq_t *iseq, LINK_ANCHOR *const ret, int line, int rlen, int llen)
 | 
						|
{
 | 
						|
    if (rlen < llen) {
 | 
						|
	do {ADD_INSN(ret, line, putnil);} while (++rlen < llen);
 | 
						|
    }
 | 
						|
    else if (rlen > llen) {
 | 
						|
	do {ADD_INSN(ret, line, pop);} while (--rlen > llen);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_massign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
 | 
						|
{
 | 
						|
    const NODE *rhsn = node->nd_value;
 | 
						|
    const NODE *splatn = node->nd_args;
 | 
						|
    const NODE *lhsn = node->nd_head;
 | 
						|
    int lhs_splat = (splatn && splatn != NODE_SPECIAL_NO_NAME_REST) ? 1 : 0;
 | 
						|
 | 
						|
    if (!popped || splatn || !compile_massign_opt(iseq, ret, rhsn, lhsn)) {
 | 
						|
	int llen = 0;
 | 
						|
	int expand = 1;
 | 
						|
	DECL_ANCHOR(lhsseq);
 | 
						|
 | 
						|
	INIT_ANCHOR(lhsseq);
 | 
						|
 | 
						|
	while (lhsn) {
 | 
						|
	    CHECK(compile_massign_lhs(iseq, lhsseq, lhsn->nd_head));
 | 
						|
	    llen += 1;
 | 
						|
	    lhsn = lhsn->nd_next;
 | 
						|
	}
 | 
						|
 | 
						|
	COMPILE(ret, "normal masgn rhs", rhsn);
 | 
						|
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN(ret, nd_line(node), dup);
 | 
						|
	}
 | 
						|
	else if (!lhs_splat) {
 | 
						|
	    INSN *last = (INSN*)ret->last;
 | 
						|
	    if (IS_INSN(&last->link) &&
 | 
						|
		IS_INSN_ID(last, newarray) &&
 | 
						|
		last->operand_size == 1) {
 | 
						|
		int rlen = FIX2INT(OPERAND_AT(last, 0));
 | 
						|
		/* special case: assign to aset or attrset */
 | 
						|
		if (llen == 2) {
 | 
						|
		    POP_ELEMENT(ret);
 | 
						|
		    adjust_stack(iseq, ret, nd_line(node), rlen, llen);
 | 
						|
		    ADD_INSN(ret, nd_line(node), swap);
 | 
						|
		    expand = 0;
 | 
						|
		}
 | 
						|
		else if (llen > 2 && llen != rlen) {
 | 
						|
		    POP_ELEMENT(ret);
 | 
						|
		    adjust_stack(iseq, ret, nd_line(node), rlen, llen);
 | 
						|
		    ADD_INSN1(ret, nd_line(node), reverse, INT2FIX(llen));
 | 
						|
		    expand = 0;
 | 
						|
		}
 | 
						|
		else if (llen > 2) {
 | 
						|
		    last->insn_id = BIN(reverse);
 | 
						|
		    expand = 0;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	if (expand) {
 | 
						|
	    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 */
 | 
						|
		const NODE *postn = splatn->nd_2nd;
 | 
						|
		const NODE *restn = splatn->nd_1st;
 | 
						|
		int num = (int)postn->nd_alen;
 | 
						|
		int flag = 0x02 | ((restn == NODE_SPECIAL_NO_NAME_REST) ? 0x00 : 0x01);
 | 
						|
 | 
						|
		ADD_INSN2(ret, nd_line(splatn), expandarray,
 | 
						|
			  INT2FIX(num), INT2FIX(flag));
 | 
						|
 | 
						|
		if (restn != NODE_SPECIAL_NO_NAME_REST) {
 | 
						|
		    CHECK(compile_massign_lhs(iseq, ret, restn));
 | 
						|
		}
 | 
						|
		while (postn) {
 | 
						|
		    CHECK(compile_massign_lhs(iseq, ret, postn->nd_head));
 | 
						|
		    postn = postn->nd_next;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		/* a, b, *r */
 | 
						|
		CHECK(compile_massign_lhs(iseq, ret, splatn));
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_const_prefix(rb_iseq_t *iseq, const NODE *const node,
 | 
						|
		     LINK_ANCHOR *const pref, LINK_ANCHOR *const body)
 | 
						|
{
 | 
						|
    switch (nd_type(node)) {
 | 
						|
      case NODE_CONST:
 | 
						|
	debugi("compile_const_prefix - colon", node->nd_vid);
 | 
						|
	ADD_INSN1(body, nd_line(node), getconstant, ID2SYM(node->nd_vid));
 | 
						|
	break;
 | 
						|
      case NODE_COLON3:
 | 
						|
	debugi("compile_const_prefix - 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:
 | 
						|
	CHECK(compile_const_prefix(iseq, node->nd_head, pref, body));
 | 
						|
	debugi("compile_const_prefix - colon2", node->nd_mid);
 | 
						|
	ADD_INSN1(body, nd_line(node), getconstant, ID2SYM(node->nd_mid));
 | 
						|
	break;
 | 
						|
      default:
 | 
						|
	CHECK(COMPILE(pref, "const colon2 prefix", node));
 | 
						|
	break;
 | 
						|
    }
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_cpath(LINK_ANCHOR *const ret, rb_iseq_t *iseq, const NODE *cpath)
 | 
						|
{
 | 
						|
    if (nd_type(cpath) == NODE_COLON3) {
 | 
						|
	/* toplevel class ::Foo */
 | 
						|
	ADD_INSN1(ret, nd_line(cpath), putobject, rb_cObject);
 | 
						|
	return VM_DEFINECLASS_FLAG_SCOPED;
 | 
						|
    }
 | 
						|
    else if (cpath->nd_head) {
 | 
						|
	/* Bar::Foo */
 | 
						|
	COMPILE(ret, "nd_else->nd_head", cpath->nd_head);
 | 
						|
	return VM_DEFINECLASS_FLAG_SCOPED;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	/* class at cbase Foo */
 | 
						|
	ADD_INSN1(ret, nd_line(cpath), putspecialobject,
 | 
						|
		  INT2FIX(VM_SPECIAL_OBJECT_CONST_BASE));
 | 
						|
	return 0;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
#define private_recv_p(node) (nd_type((node)->nd_recv) == NODE_SELF)
 | 
						|
static int
 | 
						|
defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
 | 
						|
	     const NODE *const node, LABEL **lfinish, VALUE needstr);
 | 
						|
 | 
						|
static int
 | 
						|
defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
 | 
						|
	      const NODE *const node, LABEL **lfinish, VALUE needstr)
 | 
						|
{
 | 
						|
    enum defined_type expr_type = DEFINED_NOT_DEFINED;
 | 
						|
    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:{
 | 
						|
	const NODE *vals = node;
 | 
						|
 | 
						|
	do {
 | 
						|
	    defined_expr0(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_expr0(iseq, ret, node->nd_head, lfinish, Qfalse);
 | 
						|
	ADD_INSNL(ret, nd_line(node), branchunless, lfinish[1]);
 | 
						|
	COMPILE(ret, "defined/colon2#nd_head", node->nd_head);
 | 
						|
 | 
						|
	ADD_INSN3(ret, nd_line(node), defined,
 | 
						|
		  (rb_is_const_id(node->nd_mid) ?
 | 
						|
		   INT2FIX(DEFINED_CONST) : 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_OPCALL:
 | 
						|
      case NODE_VCALL:
 | 
						|
      case NODE_FCALL:
 | 
						|
      case NODE_ATTRASGN:{
 | 
						|
	const int explicit_receiver =
 | 
						|
	    (type == NODE_CALL || type == NODE_OPCALL ||
 | 
						|
	     (type == NODE_ATTRASGN && !private_recv_p(node)));
 | 
						|
 | 
						|
	if (!lfinish[1] && (node->nd_args || explicit_receiver)) {
 | 
						|
	    lfinish[1] = NEW_LABEL(nd_line(node));
 | 
						|
	}
 | 
						|
	if (node->nd_args) {
 | 
						|
	    defined_expr0(iseq, ret, node->nd_args, lfinish, Qfalse);
 | 
						|
	    ADD_INSNL(ret, nd_line(node), branchunless, lfinish[1]);
 | 
						|
	}
 | 
						|
	if (explicit_receiver) {
 | 
						|
	    defined_expr0(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_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;
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
build_defined_rescue_iseq(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *unused)
 | 
						|
{
 | 
						|
    ADD_INSN(ret, 0, putnil);
 | 
						|
    iseq_set_exception_local_table(iseq);
 | 
						|
    return Qnil;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
 | 
						|
	     const NODE *const 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);
 | 
						|
	const rb_iseq_t *rescue;
 | 
						|
	rescue = new_child_iseq_ifunc(iseq, IFUNC_NEW(build_defined_rescue_iseq, 0, 0),
 | 
						|
				      rb_str_concat(rb_str_new2("defined guard in "),
 | 
						|
						    iseq->body->location.label),
 | 
						|
				      iseq, ISEQ_TYPE_RESCUE, 0);
 | 
						|
	lstart->rescued = LABEL_RESCUE_BEG;
 | 
						|
	lend->rescued = LABEL_RESCUE_END;
 | 
						|
	APPEND_LABEL(ret, lcur, lstart);
 | 
						|
	ADD_LABEL(ret, lend);
 | 
						|
	ADD_CATCH_ENTRY(CATCH_TYPE_RESCUE, lstart, lend, rescue, lfinish[1]);
 | 
						|
    }
 | 
						|
    return done;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, VALUE needstr)
 | 
						|
{
 | 
						|
    const int line = nd_line(node);
 | 
						|
    if (!node->nd_head) {
 | 
						|
	VALUE str = rb_iseq_defined_string(DEFINED_NIL);
 | 
						|
	ADD_INSN1(ret, line, putobject, str);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	LABEL *lfinish[2];
 | 
						|
	LINK_ELEMENT *last = ret->last;
 | 
						|
	lfinish[0] = NEW_LABEL(line);
 | 
						|
	lfinish[1] = 0;
 | 
						|
	defined_expr(iseq, ret, node->nd_head, lfinish, needstr);
 | 
						|
	if (lfinish[1]) {
 | 
						|
	    ELEM_INSERT_NEXT(last, &new_insn_body(iseq, line, BIN(putnil), 0)->link);
 | 
						|
	    ADD_INSN(ret, line, swap);
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	    ADD_LABEL(ret, lfinish[1]);
 | 
						|
	}
 | 
						|
	ADD_LABEL(ret, lfinish[0]);
 | 
						|
    }
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
make_name_for_block(const rb_iseq_t *orig_iseq)
 | 
						|
{
 | 
						|
    int level = 1;
 | 
						|
    const rb_iseq_t *iseq = orig_iseq;
 | 
						|
 | 
						|
    if (orig_iseq->body->parent_iseq != 0) {
 | 
						|
	while (orig_iseq->body->local_iseq != iseq) {
 | 
						|
	    if (iseq->body->type == ISEQ_TYPE_BLOCK) {
 | 
						|
		level++;
 | 
						|
	    }
 | 
						|
	    iseq = iseq->body->parent_iseq;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    if (level == 1) {
 | 
						|
	return rb_sprintf("block in %"PRIsVALUE, iseq->body->location.label);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	return rb_sprintf("block (%d levels) in %"PRIsVALUE, level, iseq->body->location.label);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
push_ensure_entry(rb_iseq_t *iseq,
 | 
						|
		  struct iseq_compile_data_ensure_node_stack *enl,
 | 
						|
		  struct ensure_range *er, const NODE *const node)
 | 
						|
{
 | 
						|
    enl->ensure_node = node;
 | 
						|
    enl->prev = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;	/* prev */
 | 
						|
    enl->erange = er;
 | 
						|
    ISEQ_COMPILE_DATA(iseq)->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 *const ret, rb_iseq_t *iseq, int is_return)
 | 
						|
{
 | 
						|
    struct iseq_compile_data_ensure_node_stack *enlp =
 | 
						|
	ISEQ_COMPILE_DATA(iseq)->ensure_node_stack;
 | 
						|
    struct iseq_compile_data_ensure_node_stack *prev_enlp = enlp;
 | 
						|
    DECL_ANCHOR(ensure);
 | 
						|
 | 
						|
    INIT_ANCHOR(ensure);
 | 
						|
    while (enlp) {
 | 
						|
	if (enlp->erange != NULL) {
 | 
						|
	    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(iseq)->ensure_node_stack = enlp->prev;
 | 
						|
	    ADD_LABEL(ensure_part, lstart);
 | 
						|
	    COMPILE_POPPED(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(iseq)->ensure_node_stack = prev_enlp;
 | 
						|
    ADD_SEQ(ret, ensure);
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
setup_args(rb_iseq_t *iseq, LINK_ANCHOR *const args, const NODE *argn,
 | 
						|
	   unsigned int *flag, struct rb_call_info_kw_arg **keywords)
 | 
						|
{
 | 
						|
    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);
 | 
						|
	    ADD_INSN1(args, nd_line(argn), splatarray, nsplat ? Qtrue : Qfalse);
 | 
						|
	    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 (nd_type(argn) == NODE_ARGSCAT) {
 | 
						|
		ADD_INSN1(tmp, nd_line(argn), splatarray, nsplat ? Qtrue : Qfalse);
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		ADD_INSN1(tmp, nd_line(argn), newarray, INT2FIX(1));
 | 
						|
	    }
 | 
						|
	    INSERT_LIST(args_splat, tmp);
 | 
						|
	    nsplat++;
 | 
						|
	    *flag |= VM_CALL_ARGS_SPLAT;
 | 
						|
	    if (nd_type(argn->nd_body) == NODE_HASH)
 | 
						|
		*flag |= VM_CALL_KW_SPLAT;
 | 
						|
 | 
						|
	    if (next_is_array) {
 | 
						|
		int len = compile_array(iseq, args, argn->nd_head, COMPILE_ARRAY_TYPE_ARGS, NULL, flag, FALSE);
 | 
						|
		if (len < 0) return Qnil;
 | 
						|
		argc = INT2FIX(len + 1);
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		argn = argn->nd_head;
 | 
						|
		goto setup_argn;
 | 
						|
	    }
 | 
						|
	    break;
 | 
						|
	  }
 | 
						|
	  case NODE_ARRAY:
 | 
						|
	    {
 | 
						|
		int len = compile_array(iseq, args, argn, COMPILE_ARRAY_TYPE_ARGS, keywords, flag, FALSE);
 | 
						|
		if (len < 0) return Qnil;
 | 
						|
		argc = INT2FIX(len);
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  default: {
 | 
						|
	    UNKNOWN_NODE("setup_arg", argn, Qnil);
 | 
						|
	  }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    if (nsplat > 1) {
 | 
						|
	int i;
 | 
						|
	for (i=1; i<nsplat; i++) {
 | 
						|
	    ADD_INSN(args_splat, nd_line(argn), concatarray);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    if (!LIST_INSN_SIZE_ZERO(args_splat)) {
 | 
						|
	ADD_SEQ(args, args_splat);
 | 
						|
    }
 | 
						|
 | 
						|
    if (*flag & VM_CALL_ARGS_BLOCKARG) {
 | 
						|
	if (LIST_INSN_SIZE_ONE(arg_block)) {
 | 
						|
	    LINK_ELEMENT *elem = FIRST_ELEMENT(arg_block);
 | 
						|
	    if (elem->type == ISEQ_ELEMENT_INSN) {
 | 
						|
		INSN *iobj = (INSN *)elem;
 | 
						|
		if (iobj->insn_id == BIN(getblockparam)) {
 | 
						|
		    iobj->insn_id = BIN(getblockparamproxy);
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	ADD_SEQ(args, arg_block);
 | 
						|
    }
 | 
						|
    return argc;
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
build_postexe_iseq(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *body)
 | 
						|
{
 | 
						|
    int line = nd_line(body);
 | 
						|
    VALUE argc = INT2FIX(0);
 | 
						|
    const rb_iseq_t *block = NEW_CHILD_ISEQ(body, make_name_for_block(iseq->body->parent_iseq), ISEQ_TYPE_BLOCK, line);
 | 
						|
 | 
						|
    ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | 
						|
    ADD_CALL_WITH_BLOCK(ret, line, id_core_set_postexe, argc, block);
 | 
						|
    iseq_set_local_table(iseq, 0);
 | 
						|
    return Qnil;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
compile_named_capture_assign(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node)
 | 
						|
{
 | 
						|
    const NODE *vars;
 | 
						|
    LINK_ELEMENT *last;
 | 
						|
    int line = nd_line(node);
 | 
						|
    LABEL *fail_label = NEW_LABEL(line), *end_label = NEW_LABEL(line);
 | 
						|
 | 
						|
#if !(defined(NAMED_CAPTURE_BY_SVAR) && NAMED_CAPTURE_BY_SVAR-0)
 | 
						|
    ADD_INSN1(ret, line, getglobal, ((VALUE)rb_global_entry(idBACKREF) | 1));
 | 
						|
#else
 | 
						|
    ADD_INSN2(ret, line, getspecial, INT2FIX(1) /* '~' */, INT2FIX(0));
 | 
						|
#endif
 | 
						|
    ADD_INSN(ret, line, dup);
 | 
						|
    ADD_INSNL(ret, line, branchunless, fail_label);
 | 
						|
 | 
						|
    for (vars = node; vars; vars = vars->nd_next) {
 | 
						|
	INSN *cap;
 | 
						|
	if (vars->nd_next) {
 | 
						|
	    ADD_INSN(ret, line, dup);
 | 
						|
	}
 | 
						|
	last = ret->last;
 | 
						|
	COMPILE_POPPED(ret, "capture", vars->nd_head);
 | 
						|
	last = last->next; /* putobject :var */
 | 
						|
	cap = new_insn_send(iseq, line, idAREF, INT2FIX(1),
 | 
						|
			    NULL, INT2FIX(0), NULL);
 | 
						|
	ELEM_INSERT_PREV(last->next, (LINK_ELEMENT *)cap);
 | 
						|
#if !defined(NAMED_CAPTURE_SINGLE_OPT) || NAMED_CAPTURE_SINGLE_OPT-0
 | 
						|
	if (!vars->nd_next && vars == node) {
 | 
						|
	    /* only one name */
 | 
						|
	    DECL_ANCHOR(nom);
 | 
						|
 | 
						|
	    INIT_ANCHOR(nom);
 | 
						|
	    ADD_INSNL(nom, line, jump, end_label);
 | 
						|
	    ADD_LABEL(nom, fail_label);
 | 
						|
# if 0				/* $~ must be MatchData or nil */
 | 
						|
	    ADD_INSN(nom, line, pop);
 | 
						|
	    ADD_INSN(nom, line, putnil);
 | 
						|
# endif
 | 
						|
	    ADD_LABEL(nom, end_label);
 | 
						|
	    (nom->last->next = cap->link.next)->prev = nom->last;
 | 
						|
	    (cap->link.next = nom->anchor.next)->prev = &cap->link;
 | 
						|
	    return;
 | 
						|
	}
 | 
						|
#endif
 | 
						|
    }
 | 
						|
    ADD_INSNL(ret, line, jump, end_label);
 | 
						|
    ADD_LABEL(ret, fail_label);
 | 
						|
    ADD_INSN(ret, line, pop);
 | 
						|
    for (vars = node; vars; vars = vars->nd_next) {
 | 
						|
	last = ret->last;
 | 
						|
	COMPILE_POPPED(ret, "capture", vars->nd_head);
 | 
						|
	last = last->next; /* putobject :var */
 | 
						|
	((INSN*)last)->insn_id = BIN(putnil);
 | 
						|
	((INSN*)last)->operand_size = 0;
 | 
						|
    }
 | 
						|
    ADD_LABEL(ret, end_label);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
number_literal_p(const NODE *n)
 | 
						|
{
 | 
						|
    return (n && nd_type(n) == NODE_LIT && RB_INTEGER_TYPE_P(n->nd_lit));
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_if(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
 | 
						|
{
 | 
						|
    const NODE *const node_body = type == NODE_IF ? node->nd_body : node->nd_else;
 | 
						|
    const NODE *const node_else = type == NODE_IF ? node->nd_else : node->nd_body;
 | 
						|
 | 
						|
    const int line = nd_line(node);
 | 
						|
    const int lineno = nd_first_lineno(node);
 | 
						|
    const int column = nd_first_column(node);
 | 
						|
    const int last_lineno = nd_last_lineno(node);
 | 
						|
    const int last_column = nd_last_column(node);
 | 
						|
    DECL_ANCHOR(cond_seq);
 | 
						|
    DECL_ANCHOR(then_seq);
 | 
						|
    DECL_ANCHOR(else_seq);
 | 
						|
    LABEL *then_label, *else_label, *end_label;
 | 
						|
    VALUE branches = 0;
 | 
						|
 | 
						|
    INIT_ANCHOR(cond_seq);
 | 
						|
    INIT_ANCHOR(then_seq);
 | 
						|
    INIT_ANCHOR(else_seq);
 | 
						|
    then_label = NEW_LABEL(line);
 | 
						|
    else_label = NEW_LABEL(line);
 | 
						|
    end_label = 0;
 | 
						|
 | 
						|
    compile_branch_condition(iseq, cond_seq, node->nd_cond,
 | 
						|
			     then_label, else_label);
 | 
						|
    CHECK(COMPILE_(then_seq, "then", node_body, popped));
 | 
						|
    CHECK(COMPILE_(else_seq, "else", node_else, popped));
 | 
						|
 | 
						|
    ADD_SEQ(ret, cond_seq);
 | 
						|
 | 
						|
    if (then_label->refcnt && else_label->refcnt) {
 | 
						|
	DECL_BRANCH_BASE(branches, lineno, column, last_lineno, last_column, type == NODE_IF ? "if" : "unless");
 | 
						|
    }
 | 
						|
 | 
						|
    if (then_label->refcnt) {
 | 
						|
	ADD_LABEL(ret, then_label);
 | 
						|
	if (else_label->refcnt) {
 | 
						|
	    ADD_TRACE_BRANCH_COVERAGE(
 | 
						|
		ret,
 | 
						|
		node_body ? nd_first_lineno(node_body) : lineno,
 | 
						|
		node_body ? nd_first_column(node_body) : column,
 | 
						|
		node_body ? nd_last_lineno(node_body) : last_lineno,
 | 
						|
		node_body ? nd_last_column(node_body) : last_column,
 | 
						|
		type == NODE_IF ? "then" : "else",
 | 
						|
		branches);
 | 
						|
	}
 | 
						|
	ADD_SEQ(ret, then_seq);
 | 
						|
	end_label = NEW_LABEL(line);
 | 
						|
	ADD_INSNL(ret, line, jump, end_label);
 | 
						|
    }
 | 
						|
 | 
						|
    if (else_label->refcnt) {
 | 
						|
	ADD_LABEL(ret, else_label);
 | 
						|
	if (then_label->refcnt) {
 | 
						|
	    ADD_TRACE_BRANCH_COVERAGE(
 | 
						|
		ret,
 | 
						|
		node_else ? nd_first_lineno(node_else) : lineno,
 | 
						|
		node_else ? nd_first_column(node_else) : column,
 | 
						|
		node_else ? nd_last_lineno(node_else) : last_lineno,
 | 
						|
		node_else ? nd_last_column(node_else) : last_column,
 | 
						|
		type == NODE_IF ? "else" : "then",
 | 
						|
		branches);
 | 
						|
	}
 | 
						|
	ADD_SEQ(ret, else_seq);
 | 
						|
    }
 | 
						|
 | 
						|
    if (end_label) {
 | 
						|
	ADD_LABEL(ret, end_label);
 | 
						|
    }
 | 
						|
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
 | 
						|
{
 | 
						|
    const NODE *vals;
 | 
						|
    const NODE *node = orig_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();
 | 
						|
    int line, lineno, column, last_lineno, last_column;
 | 
						|
    enum node_type type;
 | 
						|
    VALUE branches = 0;
 | 
						|
 | 
						|
    INIT_ANCHOR(head);
 | 
						|
    INIT_ANCHOR(body_seq);
 | 
						|
    INIT_ANCHOR(cond_seq);
 | 
						|
 | 
						|
    rb_hash_tbl_raw(literals)->type = &cdhash_type;
 | 
						|
 | 
						|
    CHECK(COMPILE(head, "case base", node->nd_head));
 | 
						|
 | 
						|
    DECL_BRANCH_BASE(branches, nd_first_lineno(node), nd_first_column(node), nd_last_lineno(node), nd_last_column(node), "case");
 | 
						|
 | 
						|
    node = node->nd_body;
 | 
						|
    type = nd_type(node);
 | 
						|
    line = nd_line(node);
 | 
						|
    lineno = nd_first_lineno(node);
 | 
						|
    column = nd_first_column(node);
 | 
						|
    last_lineno = nd_last_lineno(node);
 | 
						|
    last_column = nd_last_column(node);
 | 
						|
 | 
						|
    if (type != NODE_WHEN) {
 | 
						|
	COMPILE_ERROR(ERROR_ARGS "NODE_CASE: unexpected node. must be NODE_WHEN, but %s", ruby_node_name(type));
 | 
						|
	return COMPILE_NG;
 | 
						|
    }
 | 
						|
 | 
						|
    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);
 | 
						|
	ADD_TRACE_BRANCH_COVERAGE(
 | 
						|
		body_seq,
 | 
						|
		node->nd_body ? nd_first_lineno(node->nd_body) : lineno,
 | 
						|
		node->nd_body ? nd_first_column(node->nd_body) : column,
 | 
						|
		node->nd_body ? nd_last_lineno(node->nd_body) : last_lineno,
 | 
						|
		node->nd_body ? nd_last_column(node->nd_body) : last_column,
 | 
						|
		"when",
 | 
						|
		branches);
 | 
						|
	CHECK(COMPILE_(body_seq, "when body", node->nd_body, popped));
 | 
						|
	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);
 | 
						|
		CHECK(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:
 | 
						|
		UNKNOWN_NODE("NODE_CASE", vals, COMPILE_NG);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    EXPECT_NODE_NONULL("NODE_CASE", node, NODE_ARRAY, COMPILE_NG);
 | 
						|
	}
 | 
						|
 | 
						|
	node = node->nd_next;
 | 
						|
	if (!node) {
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
	type = nd_type(node);
 | 
						|
	line = nd_line(node);
 | 
						|
	lineno = nd_first_lineno(node);
 | 
						|
	column = nd_first_column(node);
 | 
						|
	last_lineno = nd_last_lineno(node);
 | 
						|
	last_column = nd_last_column(node);
 | 
						|
    }
 | 
						|
    /* else */
 | 
						|
    if (node) {
 | 
						|
	ADD_LABEL(cond_seq, elselabel);
 | 
						|
	ADD_INSN(cond_seq, line, pop);
 | 
						|
	ADD_TRACE_BRANCH_COVERAGE(cond_seq, nd_first_lineno(node), nd_first_column(node), nd_last_lineno(node), nd_last_column(node), "else", branches);
 | 
						|
	CHECK(COMPILE_(cond_seq, "else", node, popped));
 | 
						|
	ADD_INSNL(cond_seq, line, jump, endlabel);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	debugs("== else (implicit)\n");
 | 
						|
	ADD_LABEL(cond_seq, elselabel);
 | 
						|
	ADD_INSN(cond_seq, nd_line(orig_node), pop);
 | 
						|
	ADD_TRACE_BRANCH_COVERAGE(cond_seq, nd_first_lineno(orig_node), nd_first_column(orig_node), nd_last_lineno(orig_node), nd_last_column(orig_node), "else", branches);
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN(cond_seq, nd_line(orig_node), putnil);
 | 
						|
	}
 | 
						|
	ADD_INSNL(cond_seq, nd_line(orig_node), jump, endlabel);
 | 
						|
    }
 | 
						|
 | 
						|
    if (only_special_literals) {
 | 
						|
	iseq_add_mark_object(iseq, literals);
 | 
						|
 | 
						|
	ADD_INSN(ret, nd_line(orig_node), dup);
 | 
						|
	ADD_INSN2(ret, nd_line(orig_node), opt_case_dispatch, literals, elselabel);
 | 
						|
	LABEL_REF(elselabel);
 | 
						|
    }
 | 
						|
 | 
						|
    ADD_SEQ(ret, cond_seq);
 | 
						|
    ADD_SEQ(ret, body_seq);
 | 
						|
    ADD_LABEL(ret, endlabel);
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_case2(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_node, int popped)
 | 
						|
{
 | 
						|
    const NODE *vals;
 | 
						|
    const NODE *val;
 | 
						|
    const NODE *node = orig_node->nd_body;
 | 
						|
    LABEL *endlabel;
 | 
						|
    DECL_ANCHOR(body_seq);
 | 
						|
    VALUE branches = 0;
 | 
						|
 | 
						|
    DECL_BRANCH_BASE(branches, nd_first_lineno(orig_node), nd_first_column(orig_node), nd_last_lineno(orig_node), nd_last_column(orig_node), "case");
 | 
						|
 | 
						|
    INIT_ANCHOR(body_seq);
 | 
						|
    endlabel = NEW_LABEL(nd_line(node));
 | 
						|
 | 
						|
    while (node && nd_type(node) == NODE_WHEN) {
 | 
						|
	const int line = nd_line(node);
 | 
						|
	const int lineno = nd_first_lineno(node);
 | 
						|
	const int column = nd_first_column(node);
 | 
						|
	const int last_lineno = nd_last_lineno(node);
 | 
						|
	const int last_column = nd_last_column(node);
 | 
						|
	LABEL *l1 = NEW_LABEL(line);
 | 
						|
	ADD_LABEL(body_seq, l1);
 | 
						|
	ADD_TRACE_BRANCH_COVERAGE(
 | 
						|
		body_seq,
 | 
						|
		node->nd_body ? nd_first_lineno(node->nd_body) : lineno,
 | 
						|
		node->nd_body ? nd_first_column(node->nd_body) : column,
 | 
						|
		node->nd_body ? nd_last_lineno(node->nd_body) : last_lineno,
 | 
						|
		node->nd_body ? nd_last_column(node->nd_body) : last_column,
 | 
						|
		"when",
 | 
						|
		branches);
 | 
						|
	CHECK(COMPILE_(body_seq, "when", node->nd_body, popped));
 | 
						|
	ADD_INSNL(body_seq, line, jump, endlabel);
 | 
						|
 | 
						|
	vals = node->nd_head;
 | 
						|
	if (!vals) {
 | 
						|
	    COMPILE_ERROR(ERROR_ARGS "NODE_WHEN: must be NODE_ARRAY, but 0");
 | 
						|
	    return COMPILE_NG;
 | 
						|
	}
 | 
						|
	switch (nd_type(vals)) {
 | 
						|
	  case NODE_ARRAY:
 | 
						|
	    while (vals) {
 | 
						|
		LABEL *lnext;
 | 
						|
		val = vals->nd_head;
 | 
						|
		lnext = NEW_LABEL(nd_line(val));
 | 
						|
		debug_compile("== when2\n", (void)0);
 | 
						|
		CHECK(compile_branch_condition(iseq, ret, val, l1, lnext));
 | 
						|
		ADD_LABEL(ret, lnext);
 | 
						|
		vals = vals->nd_next;
 | 
						|
	    }
 | 
						|
	    break;
 | 
						|
	  case NODE_SPLAT:
 | 
						|
	  case NODE_ARGSCAT:
 | 
						|
	  case NODE_ARGSPUSH:
 | 
						|
	    ADD_INSN(ret, nd_line(vals), putnil);
 | 
						|
	    CHECK(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:
 | 
						|
	    UNKNOWN_NODE("NODE_WHEN", vals, COMPILE_NG);
 | 
						|
	}
 | 
						|
	node = node->nd_next;
 | 
						|
    }
 | 
						|
    /* else */
 | 
						|
    ADD_TRACE_BRANCH_COVERAGE(
 | 
						|
	ret,
 | 
						|
	node ? nd_first_lineno(node) : nd_first_lineno(orig_node),
 | 
						|
	node ? nd_first_column(node) : nd_first_column(orig_node),
 | 
						|
	node ? nd_last_lineno(node) : nd_last_lineno(orig_node),
 | 
						|
	node ? nd_last_column(node) : nd_last_column(orig_node),
 | 
						|
	"else",
 | 
						|
	branches);
 | 
						|
    CHECK(COMPILE_(ret, "else", node, popped));
 | 
						|
    ADD_INSNL(ret, nd_line(orig_node), jump, endlabel);
 | 
						|
 | 
						|
    ADD_SEQ(ret, body_seq);
 | 
						|
    ADD_LABEL(ret, endlabel);
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_loop(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped, const enum node_type type)
 | 
						|
{
 | 
						|
    const int line = (int)nd_line(node);
 | 
						|
    const int lineno = nd_first_lineno(node);
 | 
						|
    const int column = nd_first_column(node);
 | 
						|
    const int last_lineno = nd_last_lineno(node);
 | 
						|
    const int last_column = nd_last_column(node);
 | 
						|
    LABEL *prev_start_label = ISEQ_COMPILE_DATA(iseq)->start_label;
 | 
						|
    LABEL *prev_end_label = ISEQ_COMPILE_DATA(iseq)->end_label;
 | 
						|
    LABEL *prev_redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label;
 | 
						|
    int prev_loopval_popped = ISEQ_COMPILE_DATA(iseq)->loopval_popped;
 | 
						|
    VALUE branches = 0;
 | 
						|
 | 
						|
    struct iseq_compile_data_ensure_node_stack enl;
 | 
						|
 | 
						|
    LABEL *next_label = ISEQ_COMPILE_DATA(iseq)->start_label = NEW_LABEL(line);	/* next  */
 | 
						|
    LABEL *redo_label = ISEQ_COMPILE_DATA(iseq)->redo_label = NEW_LABEL(line);	/* redo  */
 | 
						|
    LABEL *break_label = ISEQ_COMPILE_DATA(iseq)->end_label = NEW_LABEL(line);	/* break */
 | 
						|
    LABEL *end_label = NEW_LABEL(line);
 | 
						|
    LABEL *adjust_label = NEW_LABEL(line);
 | 
						|
 | 
						|
    LABEL *next_catch_label = NEW_LABEL(line);
 | 
						|
    LABEL *tmp_label = NULL;
 | 
						|
 | 
						|
    ISEQ_COMPILE_DATA(iseq)->loopval_popped = 0;
 | 
						|
    push_ensure_entry(iseq, &enl, NULL, NULL);
 | 
						|
 | 
						|
    if (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_LABEL(ret, adjust_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);
 | 
						|
    DECL_BRANCH_BASE(branches, lineno, column, last_lineno, last_column, type == NODE_WHILE ? "while" : "until");
 | 
						|
    ADD_TRACE_BRANCH_COVERAGE(
 | 
						|
	ret,
 | 
						|
	node->nd_body ? nd_first_lineno(node->nd_body) : lineno,
 | 
						|
	node->nd_body ? nd_first_column(node->nd_body) : column,
 | 
						|
	node->nd_body ? nd_last_lineno(node->nd_body) : last_lineno,
 | 
						|
	node->nd_body ? nd_last_column(node->nd_body) : last_column,
 | 
						|
	"body",
 | 
						|
	branches);
 | 
						|
    CHECK(COMPILE_POPPED(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 {
 | 
						|
	/* until */
 | 
						|
	compile_branch_condition(iseq, ret, node->nd_cond,
 | 
						|
				 end_label, redo_label);
 | 
						|
    }
 | 
						|
 | 
						|
    ADD_LABEL(ret, end_label);
 | 
						|
    ADD_ADJUST_RESTORE(ret, adjust_label);
 | 
						|
 | 
						|
    if (node->nd_state == Qundef) {
 | 
						|
	/* ADD_INSN(ret, line, putundef); */
 | 
						|
	COMPILE_ERROR(ERROR_ARGS "unsupported: putundef");
 | 
						|
	return COMPILE_NG;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	ADD_INSN(ret, line, putnil);
 | 
						|
    }
 | 
						|
 | 
						|
    ADD_LABEL(ret, break_label);	/* break */
 | 
						|
 | 
						|
    if (popped) {
 | 
						|
	ADD_INSN(ret, line, pop);
 | 
						|
    }
 | 
						|
 | 
						|
    ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label, NULL,
 | 
						|
		    break_label);
 | 
						|
    ADD_CATCH_ENTRY(CATCH_TYPE_NEXT, redo_label, break_label, NULL,
 | 
						|
		    next_catch_label);
 | 
						|
    ADD_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, NULL,
 | 
						|
		    ISEQ_COMPILE_DATA(iseq)->redo_label);
 | 
						|
 | 
						|
    ISEQ_COMPILE_DATA(iseq)->start_label = prev_start_label;
 | 
						|
    ISEQ_COMPILE_DATA(iseq)->end_label = prev_end_label;
 | 
						|
    ISEQ_COMPILE_DATA(iseq)->redo_label = prev_redo_label;
 | 
						|
    ISEQ_COMPILE_DATA(iseq)->loopval_popped = prev_loopval_popped;
 | 
						|
    ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->prev;
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_iter(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
 | 
						|
{
 | 
						|
    const int line = nd_line(node);
 | 
						|
    const rb_iseq_t *prevblock = ISEQ_COMPILE_DATA(iseq)->current_block;
 | 
						|
    LABEL *retry_label = NEW_LABEL(line);
 | 
						|
    LABEL *retry_end_l = NEW_LABEL(line);
 | 
						|
    const rb_iseq_t *child_iseq;
 | 
						|
 | 
						|
    ADD_LABEL(ret, retry_label);
 | 
						|
    if (nd_type(node) == NODE_FOR) {
 | 
						|
	CHECK(COMPILE(ret, "iter caller (for)", node->nd_iter));
 | 
						|
 | 
						|
	ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
 | 
						|
	    NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq),
 | 
						|
			   ISEQ_TYPE_BLOCK, line);
 | 
						|
	ADD_SEND_WITH_BLOCK(ret, line, idEach, INT2FIX(0), child_iseq);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	ISEQ_COMPILE_DATA(iseq)->current_block = child_iseq =
 | 
						|
	    NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq),
 | 
						|
			   ISEQ_TYPE_BLOCK, line);
 | 
						|
	CHECK(COMPILE(ret, "iter caller", node->nd_iter));
 | 
						|
    }
 | 
						|
    ADD_LABEL(ret, retry_end_l);
 | 
						|
 | 
						|
    if (popped) {
 | 
						|
	ADD_INSN(ret, line, pop);
 | 
						|
    }
 | 
						|
 | 
						|
    ISEQ_COMPILE_DATA(iseq)->current_block = prevblock;
 | 
						|
 | 
						|
    ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, retry_label, retry_end_l, child_iseq, retry_end_l);
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_for_masgn(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
 | 
						|
{
 | 
						|
    /* massign to var in "for"
 | 
						|
     * args.length == 1 && Array === (tmp = args[0]) ? tmp : args
 | 
						|
     */
 | 
						|
    const int line = nd_line(node);
 | 
						|
    const NODE *var = node->nd_var;
 | 
						|
    LABEL *not_single = NEW_LABEL(nd_line(var));
 | 
						|
    LABEL *not_ary = NEW_LABEL(nd_line(var));
 | 
						|
    CHECK(COMPILE(ret, "for var", var));
 | 
						|
    ADD_INSN(ret, line, dup);
 | 
						|
    ADD_CALL(ret, line, idLength, INT2FIX(0));
 | 
						|
    ADD_INSN1(ret, line, putobject, INT2FIX(1));
 | 
						|
    ADD_CALL(ret, line, idEq, INT2FIX(1));
 | 
						|
    ADD_INSNL(ret, line, branchunless, not_single);
 | 
						|
    ADD_INSN(ret, line, dup);
 | 
						|
    ADD_INSN1(ret, line, putobject, INT2FIX(0));
 | 
						|
    ADD_CALL(ret, line, idAREF, INT2FIX(1));
 | 
						|
    ADD_INSN1(ret, line, putobject, rb_cArray);
 | 
						|
    ADD_INSN1(ret, line, topn, INT2FIX(1));
 | 
						|
    ADD_CALL(ret, line, idEqq, INT2FIX(1));
 | 
						|
    ADD_INSNL(ret, line, branchunless, not_ary);
 | 
						|
    ADD_INSN(ret, line, swap);
 | 
						|
    ADD_LABEL(ret, not_ary);
 | 
						|
    ADD_INSN(ret, line, pop);
 | 
						|
    ADD_LABEL(ret, not_single);
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_break(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
 | 
						|
{
 | 
						|
    const int line = nd_line(node);
 | 
						|
    unsigned long level = 0;
 | 
						|
 | 
						|
    if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0) {
 | 
						|
	/* while/until */
 | 
						|
	LABEL *splabel = NEW_LABEL(0);
 | 
						|
	ADD_LABEL(ret, splabel);
 | 
						|
	ADD_ADJUST(ret, line, ISEQ_COMPILE_DATA(iseq)->redo_label);
 | 
						|
	CHECK(COMPILE_(ret, "break val (while/until)", node->nd_stts,
 | 
						|
		       ISEQ_COMPILE_DATA(iseq)->loopval_popped));
 | 
						|
	add_ensure_iseq(ret, iseq, 0);
 | 
						|
	ADD_INSNL(ret, line, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
 | 
						|
	ADD_ADJUST_RESTORE(ret, splabel);
 | 
						|
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN(ret, line, putnil);
 | 
						|
	}
 | 
						|
    }
 | 
						|
    else if (iseq->body->type == ISEQ_TYPE_BLOCK) {
 | 
						|
      break_by_insn:
 | 
						|
	/* escape from block */
 | 
						|
	CHECK(COMPILE(ret, "break val (block)", node->nd_stts));
 | 
						|
	ADD_INSN1(ret, line, throw, INT2FIX(level | TAG_BREAK));
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
    }
 | 
						|
    else if (iseq->body->type == ISEQ_TYPE_EVAL) {
 | 
						|
      break_in_eval:
 | 
						|
	COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with break");
 | 
						|
	return COMPILE_NG;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	const rb_iseq_t *ip = iseq->body->parent_iseq;
 | 
						|
 | 
						|
	while (ip) {
 | 
						|
	    if (!ISEQ_COMPILE_DATA(ip)) {
 | 
						|
		ip = 0;
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
 | 
						|
	    level++;
 | 
						|
	    if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
 | 
						|
		level = VM_THROW_NO_ESCAPE_FLAG;
 | 
						|
		goto break_by_insn;
 | 
						|
	    }
 | 
						|
	    else if (ip->body->type == ISEQ_TYPE_BLOCK) {
 | 
						|
		level <<= VM_THROW_LEVEL_SHIFT;
 | 
						|
		goto break_by_insn;
 | 
						|
	    }
 | 
						|
	    else if (ip->body->type == ISEQ_TYPE_EVAL) {
 | 
						|
		goto break_in_eval;
 | 
						|
	    }
 | 
						|
 | 
						|
	    ip = ip->body->parent_iseq;
 | 
						|
	}
 | 
						|
	COMPILE_ERROR(ERROR_ARGS "Invalid break");
 | 
						|
	return COMPILE_NG;
 | 
						|
    }
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_next(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
 | 
						|
{
 | 
						|
    const int line = nd_line(node);
 | 
						|
    unsigned long level = 0;
 | 
						|
 | 
						|
    if (ISEQ_COMPILE_DATA(iseq)->redo_label != 0) {
 | 
						|
	LABEL *splabel = NEW_LABEL(0);
 | 
						|
	debugs("next in while loop\n");
 | 
						|
	ADD_LABEL(ret, splabel);
 | 
						|
	CHECK(COMPILE(ret, "next val/valid syntax?", node->nd_stts));
 | 
						|
	add_ensure_iseq(ret, iseq, 0);
 | 
						|
	ADD_ADJUST(ret, line, ISEQ_COMPILE_DATA(iseq)->redo_label);
 | 
						|
	ADD_INSNL(ret, line, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
 | 
						|
	ADD_ADJUST_RESTORE(ret, splabel);
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN(ret, line, putnil);
 | 
						|
	}
 | 
						|
    }
 | 
						|
    else if (ISEQ_COMPILE_DATA(iseq)->end_label) {
 | 
						|
	LABEL *splabel = NEW_LABEL(0);
 | 
						|
	debugs("next in block\n");
 | 
						|
	ADD_LABEL(ret, splabel);
 | 
						|
	ADD_ADJUST(ret, line, ISEQ_COMPILE_DATA(iseq)->start_label);
 | 
						|
	CHECK(COMPILE(ret, "next val", node->nd_stts));
 | 
						|
	add_ensure_iseq(ret, iseq, 0);
 | 
						|
	ADD_INSNL(ret, line, jump, ISEQ_COMPILE_DATA(iseq)->end_label);
 | 
						|
	ADD_ADJUST_RESTORE(ret, splabel);
 | 
						|
	splabel->unremovable = FALSE;
 | 
						|
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN(ret, line, putnil);
 | 
						|
	}
 | 
						|
    }
 | 
						|
    else if (iseq->body->type == ISEQ_TYPE_EVAL) {
 | 
						|
      next_in_eval:
 | 
						|
	COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with next");
 | 
						|
	return COMPILE_NG;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	const rb_iseq_t *ip = iseq;
 | 
						|
 | 
						|
	while (ip) {
 | 
						|
	    if (!ISEQ_COMPILE_DATA(ip)) {
 | 
						|
		ip = 0;
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
 | 
						|
	    level = VM_THROW_NO_ESCAPE_FLAG;
 | 
						|
	    if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
 | 
						|
		/* while loop */
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	    else if (ip->body->type == ISEQ_TYPE_BLOCK) {
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	    else if (ip->body->type == ISEQ_TYPE_EVAL) {
 | 
						|
		goto next_in_eval;
 | 
						|
	    }
 | 
						|
 | 
						|
	    ip = ip->body->parent_iseq;
 | 
						|
	}
 | 
						|
	if (ip != 0) {
 | 
						|
	    CHECK(COMPILE(ret, "next val", node->nd_stts));
 | 
						|
	    ADD_INSN1(ret, line, throw, INT2FIX(level | TAG_NEXT));
 | 
						|
 | 
						|
	    if (popped) {
 | 
						|
		ADD_INSN(ret, line, pop);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    COMPILE_ERROR(ERROR_ARGS "Invalid next");
 | 
						|
	    return COMPILE_NG;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_redo(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
 | 
						|
{
 | 
						|
    const int line = nd_line(node);
 | 
						|
 | 
						|
    if (ISEQ_COMPILE_DATA(iseq)->redo_label) {
 | 
						|
	LABEL *splabel = NEW_LABEL(0);
 | 
						|
	debugs("redo in while");
 | 
						|
	ADD_LABEL(ret, splabel);
 | 
						|
	ADD_ADJUST(ret, line, ISEQ_COMPILE_DATA(iseq)->redo_label);
 | 
						|
	add_ensure_iseq(ret, iseq, 0);
 | 
						|
	ADD_INSNL(ret, line, jump, ISEQ_COMPILE_DATA(iseq)->redo_label);
 | 
						|
	ADD_ADJUST_RESTORE(ret, splabel);
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN(ret, line, putnil);
 | 
						|
	}
 | 
						|
    }
 | 
						|
    else if (iseq->body->type == ISEQ_TYPE_EVAL) {
 | 
						|
      redo_in_eval:
 | 
						|
	COMPILE_ERROR(ERROR_ARGS "Can't escape from eval with redo");
 | 
						|
	return COMPILE_NG;
 | 
						|
    }
 | 
						|
    else if (ISEQ_COMPILE_DATA(iseq)->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(iseq)->start_label);
 | 
						|
	ADD_INSNL(ret, line, jump, ISEQ_COMPILE_DATA(iseq)->start_label);
 | 
						|
	ADD_ADJUST_RESTORE(ret, splabel);
 | 
						|
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN(ret, line, putnil);
 | 
						|
	}
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	const rb_iseq_t *ip = iseq;
 | 
						|
	const unsigned long level = VM_THROW_NO_ESCAPE_FLAG;
 | 
						|
 | 
						|
	while (ip) {
 | 
						|
	    if (!ISEQ_COMPILE_DATA(ip)) {
 | 
						|
		ip = 0;
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
 | 
						|
	    if (ISEQ_COMPILE_DATA(ip)->redo_label != 0) {
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	    else if (ip->body->type == ISEQ_TYPE_BLOCK) {
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	    else if (ip->body->type == ISEQ_TYPE_EVAL) {
 | 
						|
		goto redo_in_eval;
 | 
						|
	    }
 | 
						|
 | 
						|
	    ip = ip->body->parent_iseq;
 | 
						|
	}
 | 
						|
	if (ip != 0) {
 | 
						|
	    ADD_INSN(ret, line, putnil);
 | 
						|
	    ADD_INSN1(ret, line, throw, INT2FIX(level | TAG_REDO));
 | 
						|
 | 
						|
	    if (popped) {
 | 
						|
		ADD_INSN(ret, line, pop);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    COMPILE_ERROR(ERROR_ARGS "Invalid redo");
 | 
						|
	    return COMPILE_NG;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_retry(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
 | 
						|
{
 | 
						|
    const int line = nd_line(node);
 | 
						|
 | 
						|
    if (iseq->body->type == ISEQ_TYPE_RESCUE) {
 | 
						|
	ADD_INSN(ret, line, putnil);
 | 
						|
	ADD_INSN1(ret, line, throw, INT2FIX(TAG_RETRY));
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	COMPILE_ERROR(ERROR_ARGS "Invalid retry");
 | 
						|
	return COMPILE_NG;
 | 
						|
    }
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_rescue(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
 | 
						|
{
 | 
						|
    const int line = nd_line(node);
 | 
						|
    LABEL *lstart = NEW_LABEL(line);
 | 
						|
    LABEL *lend = NEW_LABEL(line);
 | 
						|
    LABEL *lcont = NEW_LABEL(line);
 | 
						|
    const rb_iseq_t *rescue = NEW_CHILD_ISEQ(node->nd_resq,
 | 
						|
					     rb_str_concat(rb_str_new2("rescue in "), iseq->body->location.label),
 | 
						|
					     ISEQ_TYPE_RESCUE, line);
 | 
						|
 | 
						|
    lstart->rescued = LABEL_RESCUE_BEG;
 | 
						|
    lend->rescued = LABEL_RESCUE_END;
 | 
						|
    ADD_LABEL(ret, lstart);
 | 
						|
    CHECK(COMPILE(ret, "rescue head", node->nd_head));
 | 
						|
    ADD_LABEL(ret, lend);
 | 
						|
    if (node->nd_else) {
 | 
						|
	ADD_INSN(ret, line, pop);
 | 
						|
	CHECK(COMPILE(ret, "rescue else", node->nd_else));
 | 
						|
    }
 | 
						|
    ADD_INSN(ret, line, nop);
 | 
						|
    ADD_LABEL(ret, lcont);
 | 
						|
 | 
						|
    if (popped) {
 | 
						|
	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, NULL, lstart);
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_resbody(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
 | 
						|
{
 | 
						|
    const int line = nd_line(node);
 | 
						|
    const NODE *resq = node;
 | 
						|
    const 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_GETLOCAL(ret, line, LVAR_ERRINFO, 0);
 | 
						|
		    CHECK(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_GETLOCAL(ret, line, LVAR_ERRINFO, 0);
 | 
						|
		CHECK(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:
 | 
						|
		UNKNOWN_NODE("NODE_RESBODY", narg, COMPILE_NG);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    ADD_GETLOCAL(ret, line, LVAR_ERRINFO, 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);
 | 
						|
	CHECK(COMPILE(ret, "resbody body", resq->nd_body));
 | 
						|
	if (ISEQ_COMPILE_DATA(iseq)->option->tailcall_optimization) {
 | 
						|
	    ADD_INSN(ret, line, nop);
 | 
						|
	}
 | 
						|
	ADD_INSN(ret, line, leave);
 | 
						|
	ADD_LABEL(ret, label_miss);
 | 
						|
	resq = resq->nd_head;
 | 
						|
    }
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_ensure(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
 | 
						|
{
 | 
						|
    const int line = nd_line(node);
 | 
						|
    DECL_ANCHOR(ensr);
 | 
						|
    const rb_iseq_t *ensure = NEW_CHILD_ISEQ(node->nd_ensr,
 | 
						|
					     rb_str_concat(rb_str_new2 ("ensure in "), iseq->body->location.label),
 | 
						|
					     ISEQ_TYPE_ENSURE, line);
 | 
						|
    LABEL *lstart = NEW_LABEL(line);
 | 
						|
    LABEL *lend = NEW_LABEL(line);
 | 
						|
    LABEL *lcont = NEW_LABEL(line);
 | 
						|
    LINK_ELEMENT *last;
 | 
						|
    int last_leave = 0;
 | 
						|
    struct ensure_range er;
 | 
						|
    struct iseq_compile_data_ensure_node_stack enl;
 | 
						|
    struct ensure_range *erange;
 | 
						|
 | 
						|
    INIT_ANCHOR(ensr);
 | 
						|
    CHECK(COMPILE_POPPED(ensr, "ensure ensr", node->nd_ensr));
 | 
						|
    last = ensr->last;
 | 
						|
    last_leave = last && IS_INSN(last) && IS_INSN_ID(last, leave);
 | 
						|
 | 
						|
    er.begin = lstart;
 | 
						|
    er.end = lend;
 | 
						|
    er.next = 0;
 | 
						|
    push_ensure_entry(iseq, &enl, &er, node->nd_ensr);
 | 
						|
 | 
						|
    ADD_LABEL(ret, lstart);
 | 
						|
    CHECK(COMPILE_(ret, "ensure head", node->nd_head, (popped | last_leave)));
 | 
						|
    ADD_LABEL(ret, lend);
 | 
						|
    if (LIST_INSN_SIZE_ZERO(ensr)) {
 | 
						|
	ADD_INSN(ret, line, nop);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	ADD_SEQ(ret, ensr);
 | 
						|
	if (!popped && last_leave) {
 | 
						|
	    ADD_INSN(ret, line, putnil);
 | 
						|
	}
 | 
						|
    }
 | 
						|
    ADD_LABEL(ret, lcont);
 | 
						|
    if (last_leave) ADD_INSN(ret, line, pop);
 | 
						|
 | 
						|
    erange = ISEQ_COMPILE_DATA(iseq)->ensure_node_stack->erange;
 | 
						|
    if (lstart->link.next != &lend->link) {
 | 
						|
	while (erange) {
 | 
						|
	    ADD_CATCH_ENTRY(CATCH_TYPE_ENSURE, erange->begin, erange->end,
 | 
						|
			    ensure, lcont);
 | 
						|
	    erange = erange->next;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    ISEQ_COMPILE_DATA(iseq)->ensure_node_stack = enl.prev;
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
compile_return(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const node, int popped)
 | 
						|
{
 | 
						|
    const int line = nd_line(node);
 | 
						|
 | 
						|
    if (iseq) {
 | 
						|
	enum iseq_type type = iseq->body->type;
 | 
						|
	const rb_iseq_t *is = iseq;
 | 
						|
	enum iseq_type t = type;
 | 
						|
	const NODE *retval = node->nd_stts;
 | 
						|
	LABEL *splabel = 0;
 | 
						|
 | 
						|
	while (t == ISEQ_TYPE_RESCUE || t == ISEQ_TYPE_ENSURE) {
 | 
						|
	    if (!(is = is->body->parent_iseq)) break;
 | 
						|
	    t = is->body->type;
 | 
						|
	}
 | 
						|
	switch (t) {
 | 
						|
	  case ISEQ_TYPE_TOP:
 | 
						|
	  case ISEQ_TYPE_MAIN:
 | 
						|
	    if (is == iseq) {
 | 
						|
		/* plain top-level, leave directly */
 | 
						|
		type = ISEQ_TYPE_METHOD;
 | 
						|
	    }
 | 
						|
	    break;
 | 
						|
	  default:
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
 | 
						|
	if (type == ISEQ_TYPE_METHOD) {
 | 
						|
	    splabel = NEW_LABEL(0);
 | 
						|
	    ADD_LABEL(ret, splabel);
 | 
						|
	    ADD_ADJUST(ret, line, 0);
 | 
						|
	}
 | 
						|
 | 
						|
	CHECK(COMPILE(ret, "return nd_stts (return val)", retval));
 | 
						|
 | 
						|
	if (type == ISEQ_TYPE_METHOD) {
 | 
						|
	    add_ensure_iseq(ret, iseq, 1);
 | 
						|
	    ADD_TRACE(ret, RUBY_EVENT_RETURN);
 | 
						|
	    ADD_INSN(ret, line, leave);
 | 
						|
	    ADD_ADJUST_RESTORE(ret, splabel);
 | 
						|
 | 
						|
	    if (!popped) {
 | 
						|
		ADD_INSN(ret, line, putnil);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    ADD_INSN1(ret, line, throw, INT2FIX(TAG_RETURN));
 | 
						|
	    if (popped) {
 | 
						|
		ADD_INSN(ret, line, pop);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
    return COMPILE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped);
 | 
						|
/**
 | 
						|
  compile each node
 | 
						|
 | 
						|
  self:  InstructionSequence
 | 
						|
  node:  Ruby compiled node
 | 
						|
  popped: This node will be popped
 | 
						|
 */
 | 
						|
static int
 | 
						|
iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, const NODE *node, int popped)
 | 
						|
{
 | 
						|
    if (node == 0) {
 | 
						|
	if (!popped) {
 | 
						|
	    int lineno = ISEQ_COMPILE_DATA(iseq)->last_line;
 | 
						|
	    if (lineno == 0) lineno = FIX2INT(rb_iseq_first_lineno(iseq));
 | 
						|
	    debugs("node: NODE_NIL(implicit)\n");
 | 
						|
	    ADD_INSN(ret, lineno, putnil);
 | 
						|
	}
 | 
						|
	return COMPILE_OK;
 | 
						|
    }
 | 
						|
    return iseq_compile_each0(iseq, ret, node, popped);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, int popped)
 | 
						|
{
 | 
						|
    const int line = (int)nd_line(node);
 | 
						|
    const enum node_type type = nd_type(node);
 | 
						|
 | 
						|
    if (ISEQ_COMPILE_DATA(iseq)->last_line == line) {
 | 
						|
	/* ignore */
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	if (node->flags & NODE_FL_NEWLINE) {
 | 
						|
	    ISEQ_COMPILE_DATA(iseq)->last_line = line;
 | 
						|
	    ADD_TRACE_LINE_COVERAGE(ret, line);
 | 
						|
	    ADD_TRACE(ret, RUBY_EVENT_LINE);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    debug_node_start(node);
 | 
						|
#undef BEFORE_RETURN
 | 
						|
#define BEFORE_RETURN debug_node_end()
 | 
						|
 | 
						|
    switch (type) {
 | 
						|
      case NODE_BLOCK:{
 | 
						|
	while (node && nd_type(node) == NODE_BLOCK) {
 | 
						|
	    CHECK(COMPILE_(ret, "BLOCK body", node->nd_head,
 | 
						|
			   (node->nd_next ? 1 : popped)));
 | 
						|
	    node = node->nd_next;
 | 
						|
	}
 | 
						|
	if (node) {
 | 
						|
	    CHECK(COMPILE_(ret, "BLOCK next", node->nd_next, popped));
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_IF:
 | 
						|
      case NODE_UNLESS:
 | 
						|
	CHECK(compile_if(iseq, ret, node, popped, type));
 | 
						|
	break;
 | 
						|
      case NODE_CASE:
 | 
						|
	CHECK(compile_case(iseq, ret, node, popped));
 | 
						|
	break;
 | 
						|
      case NODE_CASE2:
 | 
						|
	CHECK(compile_case2(iseq, ret, node, popped));
 | 
						|
	break;
 | 
						|
      case NODE_WHILE:
 | 
						|
      case NODE_UNTIL:
 | 
						|
	CHECK(compile_loop(iseq, ret, node, popped, type));
 | 
						|
	break;
 | 
						|
      case NODE_FOR:
 | 
						|
      case NODE_ITER:
 | 
						|
	CHECK(compile_iter(iseq, ret, node, popped));
 | 
						|
	break;
 | 
						|
      case NODE_FOR_MASGN:
 | 
						|
	CHECK(compile_for_masgn(iseq, ret, node, popped));
 | 
						|
	break;
 | 
						|
      case NODE_BREAK:
 | 
						|
	CHECK(compile_break(iseq, ret, node, popped));
 | 
						|
	break;
 | 
						|
      case NODE_NEXT:
 | 
						|
	CHECK(compile_next(iseq, ret, node, popped));
 | 
						|
	break;
 | 
						|
      case NODE_REDO:
 | 
						|
	CHECK(compile_redo(iseq, ret, node, popped));
 | 
						|
	break;
 | 
						|
      case NODE_RETRY:
 | 
						|
	CHECK(compile_retry(iseq, ret, node, popped));
 | 
						|
	break;
 | 
						|
      case NODE_BEGIN:{
 | 
						|
	CHECK(COMPILE_(ret, "NODE_BEGIN", node->nd_body, popped));
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_RESCUE:
 | 
						|
	CHECK(compile_rescue(iseq, ret, node, popped));
 | 
						|
	break;
 | 
						|
      case NODE_RESBODY:
 | 
						|
	CHECK(compile_resbody(iseq, ret, node, popped));
 | 
						|
	break;
 | 
						|
      case NODE_ENSURE:
 | 
						|
	CHECK(compile_ensure(iseq, ret, node, popped));
 | 
						|
	break;
 | 
						|
 | 
						|
      case NODE_AND:
 | 
						|
      case NODE_OR:{
 | 
						|
	LABEL *end_label = NEW_LABEL(line);
 | 
						|
	CHECK(COMPILE(ret, "nd_1st", node->nd_1st));
 | 
						|
	if (!popped) {
 | 
						|
	    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 (!popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	CHECK(COMPILE_(ret, "nd_2nd", node->nd_2nd, popped));
 | 
						|
	ADD_LABEL(ret, end_label);
 | 
						|
	break;
 | 
						|
      }
 | 
						|
 | 
						|
      case NODE_MASGN:{
 | 
						|
	compile_massign(iseq, ret, node, popped);
 | 
						|
	break;
 | 
						|
      }
 | 
						|
 | 
						|
      case NODE_LASGN:{
 | 
						|
	ID id = node->nd_vid;
 | 
						|
	int idx = iseq->body->local_iseq->body->local_table_size - get_local_var_idx(iseq, id);
 | 
						|
 | 
						|
	debugs("lvar: %s idx: %d\n", rb_id2name(id), idx);
 | 
						|
	CHECK(COMPILE(ret, "rvalue", node->nd_value));
 | 
						|
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN(ret, line, dup);
 | 
						|
	}
 | 
						|
	ADD_SETLOCAL(ret, line, idx, get_lvar_level(iseq));
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_DASGN:
 | 
						|
      case NODE_DASGN_CURR:{
 | 
						|
	int idx, lv, ls;
 | 
						|
	ID id = node->nd_vid;
 | 
						|
	CHECK(COMPILE(ret, "dvalue", node->nd_value));
 | 
						|
	debugi("dassn id", rb_id2str(id) ? id : '*');
 | 
						|
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN(ret, line, dup);
 | 
						|
	}
 | 
						|
 | 
						|
	idx = get_dyna_var_idx(iseq, id, &lv, &ls);
 | 
						|
 | 
						|
	if (idx < 0) {
 | 
						|
	    COMPILE_ERROR(ERROR_ARGS "NODE_DASGN(_CURR): unknown id (%"PRIsVALUE")",
 | 
						|
			  rb_id2str(id));
 | 
						|
	    goto ng;
 | 
						|
	}
 | 
						|
	ADD_SETLOCAL(ret, line, ls - idx, lv);
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_GASGN:{
 | 
						|
	CHECK(COMPILE(ret, "lvalue", node->nd_value));
 | 
						|
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN(ret, line, dup);
 | 
						|
	}
 | 
						|
	ADD_INSN1(ret, line, setglobal,
 | 
						|
		  ((VALUE)node->nd_entry | 1));
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_IASGN:{
 | 
						|
	CHECK(COMPILE(ret, "lvalue", node->nd_value));
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN(ret, line, dup);
 | 
						|
	}
 | 
						|
	ADD_INSN2(ret, line, setinstancevariable,
 | 
						|
		  ID2SYM(node->nd_vid),
 | 
						|
		  get_ivar_ic_value(iseq,node->nd_vid));
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_CDECL:{
 | 
						|
	CHECK(COMPILE(ret, "lvalue", node->nd_value));
 | 
						|
 | 
						|
	if (!popped) {
 | 
						|
	    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:{
 | 
						|
	CHECK(COMPILE(ret, "cvasgn val", node->nd_value));
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN(ret, line, dup);
 | 
						|
	}
 | 
						|
	ADD_INSN1(ret, line, setclassvariable,
 | 
						|
		  ID2SYM(node->nd_vid));
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_OP_ASGN1: {
 | 
						|
	DECL_ANCHOR(args);
 | 
						|
	VALUE argc;
 | 
						|
	unsigned int flag = 0;
 | 
						|
	unsigned int asgnflag = 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 (!popped) {
 | 
						|
	    ADD_INSN(ret, line, putnil);
 | 
						|
	}
 | 
						|
	asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN1 recv", node);
 | 
						|
	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, NULL);
 | 
						|
	    CHECK(!NIL_P(argc));
 | 
						|
	    ADD_SEQ(ret, args);
 | 
						|
	}
 | 
						|
	ADD_INSN1(ret, line, dupn, FIXNUM_INC(argc, 1 + boff));
 | 
						|
	ADD_SEND_WITH_FLAG(ret, line, idAREF, argc, INT2FIX(flag));
 | 
						|
	flag |= asgnflag;
 | 
						|
 | 
						|
	if (id == idOROP || id == idANDOP) {
 | 
						|
	    /* a[x] ||= y  or  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 == idOROP) {
 | 
						|
		ADD_INSNL(ret, line, branchif, label);
 | 
						|
	    }
 | 
						|
	    else { /* idANDOP */
 | 
						|
		ADD_INSNL(ret, line, branchunless, label);
 | 
						|
	    }
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
 | 
						|
	    CHECK(COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body));
 | 
						|
	    if (!popped) {
 | 
						|
		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_WITH_FLAG(ret, line, idASET, argc, INT2FIX(flag));
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		if (boff > 0)
 | 
						|
		    ADD_INSN(ret, line, swap);
 | 
						|
		ADD_SEND_WITH_FLAG(ret, line, idASET, FIXNUM_INC(argc, 1), INT2FIX(flag));
 | 
						|
	    }
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	    ADD_INSNL(ret, line, jump, lfin);
 | 
						|
	    ADD_LABEL(ret, label);
 | 
						|
	    if (!popped) {
 | 
						|
		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 {
 | 
						|
	    CHECK(COMPILE(ret, "NODE_OP_ASGN1 args->body: ", node->nd_args->nd_body));
 | 
						|
	    ADD_SEND(ret, line, id, INT2FIX(1));
 | 
						|
	    if (!popped) {
 | 
						|
		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_WITH_FLAG(ret, line, idASET, argc, INT2FIX(flag));
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		if (boff > 0)
 | 
						|
		    ADD_INSN(ret, line, swap);
 | 
						|
		ADD_SEND_WITH_FLAG(ret, line, idASET, FIXNUM_INC(argc, 1), INT2FIX(flag));
 | 
						|
	    }
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_OP_ASGN2:{
 | 
						|
	ID atype = node->nd_next->nd_mid;
 | 
						|
	ID vid = node->nd_next->nd_vid, aid = rb_id_attrset(vid);
 | 
						|
	VALUE asgnflag;
 | 
						|
	LABEL *lfin = NEW_LABEL(line);
 | 
						|
	LABEL *lcfin = NEW_LABEL(line);
 | 
						|
	LABEL *lskip = 0;
 | 
						|
	/*
 | 
						|
	  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
 | 
						|
 | 
						|
	*/
 | 
						|
 | 
						|
	asgnflag = COMPILE_RECV(ret, "NODE_OP_ASGN2#recv", node);
 | 
						|
	if (node->nd_next->nd_aid) {
 | 
						|
	    lskip = NEW_LABEL(line);
 | 
						|
	    ADD_INSN(ret, line, dup);
 | 
						|
	    ADD_INSNL(ret, line, branchnil, lskip);
 | 
						|
	}
 | 
						|
	ADD_INSN(ret, line, dup);
 | 
						|
	ADD_SEND(ret, line, vid, INT2FIX(0));
 | 
						|
 | 
						|
	if (atype == idOROP || atype == idANDOP) {
 | 
						|
	    ADD_INSN(ret, line, dup);
 | 
						|
	    if (atype == idOROP) {
 | 
						|
		ADD_INSNL(ret, line, branchif, lcfin);
 | 
						|
	    }
 | 
						|
	    else { /* idANDOP */
 | 
						|
		ADD_INSNL(ret, line, branchunless, lcfin);
 | 
						|
	    }
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	    CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value));
 | 
						|
	    ADD_INSN(ret, line, swap);
 | 
						|
	    ADD_INSN1(ret, line, topn, INT2FIX(1));
 | 
						|
	    ADD_SEND_WITH_FLAG(ret, line, aid, INT2FIX(1), INT2FIX(asgnflag));
 | 
						|
	    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 (lskip) {
 | 
						|
		ADD_LABEL(ret, lskip);
 | 
						|
	    }
 | 
						|
	    if (popped) {
 | 
						|
		/* we can apply more optimize */
 | 
						|
		ADD_INSN(ret, line, pop);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    CHECK(COMPILE(ret, "NODE_OP_ASGN2 val", node->nd_value));
 | 
						|
	    ADD_SEND(ret, line, atype, INT2FIX(1));
 | 
						|
	    if (!popped) {
 | 
						|
		ADD_INSN(ret, line, swap);
 | 
						|
		ADD_INSN1(ret, line, topn, INT2FIX(1));
 | 
						|
	    }
 | 
						|
	    ADD_SEND_WITH_FLAG(ret, line, aid, INT2FIX(1), INT2FIX(asgnflag));
 | 
						|
	    if (lskip && popped) {
 | 
						|
		ADD_LABEL(ret, lskip);
 | 
						|
	    }
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	    if (lskip && !popped) {
 | 
						|
		ADD_LABEL(ret, lskip);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	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:
 | 
						|
	    CHECK(COMPILE(ret, "NODE_OP_CDECL/colon2#nd_head", node->nd_head->nd_head));
 | 
						|
	    break;
 | 
						|
	  default:
 | 
						|
	    COMPILE_ERROR(ERROR_ARGS "%s: invalid node in NODE_OP_CDECL",
 | 
						|
			  ruby_node_name(nd_type(node->nd_head)));
 | 
						|
	    goto ng;
 | 
						|
	}
 | 
						|
	mid = node->nd_head->nd_mid;
 | 
						|
	/* cref */
 | 
						|
	if (node->nd_aid == idOROP) {
 | 
						|
	    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 == idOROP || node->nd_aid == idANDOP) {
 | 
						|
	    lfin = NEW_LABEL(line);
 | 
						|
	    if (!popped) ADD_INSN(ret, line, dup); /* cref [obj] obj */
 | 
						|
	    if (node->nd_aid == idOROP)
 | 
						|
		ADD_INSNL(ret, line, branchif, lfin);
 | 
						|
	    else /* idANDOP */
 | 
						|
		ADD_INSNL(ret, line, branchunless, lfin);
 | 
						|
	    /* cref [obj] */
 | 
						|
	    if (!popped) ADD_INSN(ret, line, pop); /* cref */
 | 
						|
	    if (lassign) ADD_LABEL(ret, lassign);
 | 
						|
	    CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value));
 | 
						|
	    /* cref value */
 | 
						|
	    if (popped)
 | 
						|
		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 (!popped) ADD_INSN(ret, line, swap); /* [value] cref */
 | 
						|
	    ADD_INSN(ret, line, pop); /* [value] */
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    CHECK(COMPILE(ret, "NODE_OP_CDECL#nd_value", node->nd_value));
 | 
						|
	    /* cref obj value */
 | 
						|
	    ADD_CALL(ret, line, node->nd_aid, INT2FIX(1));
 | 
						|
	    /* cref value */
 | 
						|
	    ADD_INSN(ret, line, swap); /* value cref */
 | 
						|
	    if (!popped) {
 | 
						|
		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);
 | 
						|
	}
 | 
						|
 | 
						|
	CHECK(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);
 | 
						|
	CHECK(COMPILE(ret, "NODE_OP_ASGN_AND/OR#nd_value", node->nd_value));
 | 
						|
	ADD_LABEL(ret, lfin);
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    /* we can apply more optimize */
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_CALL:
 | 
						|
      case NODE_OPCALL:
 | 
						|
	/* optimization shortcut
 | 
						|
	 *   "literal".freeze -> opt_str_freeze("literal")
 | 
						|
	 */
 | 
						|
	if (node->nd_recv && nd_type(node->nd_recv) == NODE_STR &&
 | 
						|
	    (node->nd_mid == idFreeze || node->nd_mid == idUMinus) &&
 | 
						|
	    node->nd_args == NULL &&
 | 
						|
	    ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
 | 
						|
	    ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
 | 
						|
	    VALUE str = freeze_literal(iseq, node->nd_recv->nd_lit);
 | 
						|
	    if (node->nd_mid == idUMinus) {
 | 
						|
		ADD_INSN1(ret, line, opt_str_uminus, str);
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		ADD_INSN1(ret, line, opt_str_freeze, str);
 | 
						|
	    }
 | 
						|
	    if (popped) {
 | 
						|
		ADD_INSN(ret, line, pop);
 | 
						|
	    }
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
	/* optimization shortcut
 | 
						|
	 *   obj["literal"] -> opt_aref_with(obj, "literal")
 | 
						|
	 */
 | 
						|
	if (node->nd_mid == idAREF && !private_recv_p(node) && node->nd_args &&
 | 
						|
	    nd_type(node->nd_args) == NODE_ARRAY && node->nd_args->nd_alen == 1 &&
 | 
						|
	    nd_type(node->nd_args->nd_head) == NODE_STR &&
 | 
						|
	    ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
 | 
						|
	    ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
 | 
						|
	    VALUE str = freeze_literal(iseq, node->nd_args->nd_head->nd_lit);
 | 
						|
	    CHECK(COMPILE(ret, "recv", node->nd_recv));
 | 
						|
	    ADD_INSN3(ret, line, opt_aref_with,
 | 
						|
		      new_callinfo(iseq, idAREF, 1, 0, NULL, FALSE),
 | 
						|
		      NULL/* CALL_CACHE */, str);
 | 
						|
	    if (popped) {
 | 
						|
		ADD_INSN(ret, line, pop);
 | 
						|
	    }
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
      case NODE_QCALL:
 | 
						|
      case NODE_FCALL:
 | 
						|
      case NODE_VCALL:{		/* VCALL: variable or call */
 | 
						|
	/*
 | 
						|
	  call:  obj.method(...)
 | 
						|
	  fcall: func(...)
 | 
						|
	  vcall: func
 | 
						|
	*/
 | 
						|
	DECL_ANCHOR(recv);
 | 
						|
	DECL_ANCHOR(args);
 | 
						|
	LABEL *else_label = 0;
 | 
						|
	LABEL *end_label = 0;
 | 
						|
	VALUE branches = 0;
 | 
						|
	ID mid = node->nd_mid;
 | 
						|
	VALUE argc;
 | 
						|
	unsigned int flag = 0;
 | 
						|
	struct rb_call_info_kw_arg *keywords = NULL;
 | 
						|
	const rb_iseq_t *parent_block = ISEQ_COMPILE_DATA(iseq)->current_block;
 | 
						|
	ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
 | 
						|
 | 
						|
	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(iseq)->labels_table;
 | 
						|
		VALUE label_name;
 | 
						|
 | 
						|
		if (!labels_table) {
 | 
						|
		    labels_table = st_init_numtable();
 | 
						|
		    ISEQ_COMPILE_DATA(iseq)->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 = 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");
 | 
						|
		    goto ng;
 | 
						|
		}
 | 
						|
 | 
						|
 | 
						|
		if (mid == goto_id) {
 | 
						|
		    ADD_INSNL(ret, line, jump, label);
 | 
						|
		}
 | 
						|
		else {
 | 
						|
		    ADD_LABEL(ret, label);
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
#endif
 | 
						|
	/* receiver */
 | 
						|
	if (type == NODE_CALL || type == NODE_OPCALL || type == NODE_QCALL) {
 | 
						|
	    int idx, level;
 | 
						|
 | 
						|
	    if (mid == idCall &&
 | 
						|
		nd_type(node->nd_recv) == NODE_LVAR &&
 | 
						|
		iseq_block_param_id_p(iseq, node->nd_recv->nd_vid, &idx, &level)) {
 | 
						|
		ADD_INSN2(recv, nd_line(node->nd_recv), getblockparamproxy, INT2FIX(idx + VM_ENV_DATA_SIZE - 1), INT2FIX(level));
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		CHECK(COMPILE(recv, "recv", node->nd_recv));
 | 
						|
	    }
 | 
						|
 | 
						|
	    if (type == NODE_QCALL) {
 | 
						|
		else_label = NEW_LABEL(line);
 | 
						|
		end_label = NEW_LABEL(line);
 | 
						|
 | 
						|
		DECL_BRANCH_BASE(branches, nd_first_lineno(node), nd_first_column(node), nd_last_lineno(node), nd_last_column(node), "&.");
 | 
						|
		ADD_INSN(recv, line, dup);
 | 
						|
		ADD_INSNL(recv, line, branchnil, else_label);
 | 
						|
		ADD_TRACE_BRANCH_COVERAGE(recv, nd_first_lineno(node), nd_first_column(node), nd_last_lineno(node), nd_last_column(node), "then", branches);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	else if (type == NODE_FCALL || type == NODE_VCALL) {
 | 
						|
	    ADD_CALL_RECEIVER(recv, line);
 | 
						|
	}
 | 
						|
 | 
						|
	/* args */
 | 
						|
	if (type != NODE_VCALL) {
 | 
						|
	    argc = setup_args(iseq, args, node->nd_args, &flag, &keywords);
 | 
						|
	    CHECK(!NIL_P(argc));
 | 
						|
	}
 | 
						|
	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 ((int)type) {
 | 
						|
	  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, mid, argc, parent_block, INT2FIX(flag), keywords);
 | 
						|
 | 
						|
	if (else_label && end_label) {
 | 
						|
	    ADD_INSNL(ret, line, jump, end_label);
 | 
						|
	    ADD_LABEL(ret, else_label);
 | 
						|
	    ADD_TRACE_BRANCH_COVERAGE(ret, nd_first_lineno(node), nd_first_column(node), nd_last_lineno(node), nd_last_column(node), "else", branches);
 | 
						|
	    ADD_LABEL(ret, end_label);
 | 
						|
	}
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_SUPER:
 | 
						|
      case NODE_ZSUPER:{
 | 
						|
	DECL_ANCHOR(args);
 | 
						|
	int argc;
 | 
						|
	unsigned int flag = 0;
 | 
						|
	struct rb_call_info_kw_arg *keywords = NULL;
 | 
						|
	const rb_iseq_t *parent_block = ISEQ_COMPILE_DATA(iseq)->current_block;
 | 
						|
 | 
						|
	INIT_ANCHOR(args);
 | 
						|
	ISEQ_COMPILE_DATA(iseq)->current_block = NULL;
 | 
						|
	if (type == NODE_SUPER) {
 | 
						|
	    VALUE vargc = setup_args(iseq, args, node->nd_args, &flag, &keywords);
 | 
						|
	    CHECK(!NIL_P(vargc));
 | 
						|
	    argc = FIX2INT(vargc);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    /* NODE_ZSUPER */
 | 
						|
	    int i;
 | 
						|
	    const rb_iseq_t *liseq = iseq->body->local_iseq;
 | 
						|
	    int lvar_level = get_lvar_level(iseq);
 | 
						|
 | 
						|
	    argc = liseq->body->param.lead_num;
 | 
						|
 | 
						|
	    /* normal arguments */
 | 
						|
	    for (i = 0; i < liseq->body->param.lead_num; i++) {
 | 
						|
		int idx = liseq->body->local_table_size - i;
 | 
						|
		ADD_GETLOCAL(args, line, idx, lvar_level);
 | 
						|
	    }
 | 
						|
 | 
						|
	    if (liseq->body->param.flags.has_opt) {
 | 
						|
		/* optional arguments */
 | 
						|
		int j;
 | 
						|
		for (j = 0; j < liseq->body->param.opt_num; j++) {
 | 
						|
		    int idx = liseq->body->local_table_size - (i + j);
 | 
						|
		    ADD_GETLOCAL(args, line, idx, lvar_level);
 | 
						|
		}
 | 
						|
		i += j;
 | 
						|
		argc = i;
 | 
						|
	    }
 | 
						|
	    if (liseq->body->param.flags.has_rest) {
 | 
						|
		/* rest argument */
 | 
						|
		int idx = liseq->body->local_table_size - liseq->body->param.rest_start;
 | 
						|
 | 
						|
		ADD_GETLOCAL(args, line, idx, lvar_level);
 | 
						|
		ADD_INSN1(args, line, splatarray, Qfalse);
 | 
						|
 | 
						|
		argc = liseq->body->param.rest_start + 1;
 | 
						|
		flag |= VM_CALL_ARGS_SPLAT;
 | 
						|
	    }
 | 
						|
	    if (liseq->body->param.flags.has_post) {
 | 
						|
		/* post arguments */
 | 
						|
		int post_len = liseq->body->param.post_num;
 | 
						|
		int post_start = liseq->body->param.post_start;
 | 
						|
 | 
						|
		if (liseq->body->param.flags.has_rest) {
 | 
						|
		    int j;
 | 
						|
		    for (j=0; j<post_len; j++) {
 | 
						|
			int idx = liseq->body->local_table_size - (post_start + j);
 | 
						|
			ADD_GETLOCAL(args, line, idx, 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->body->local_table_size - (post_start + j);
 | 
						|
			ADD_GETLOCAL(args, line, idx, lvar_level);
 | 
						|
		    }
 | 
						|
		    argc = post_len + post_start;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
 | 
						|
	    if (liseq->body->param.flags.has_kw) { /* TODO: support keywords */
 | 
						|
		int local_size = liseq->body->local_table_size;
 | 
						|
		argc++;
 | 
						|
 | 
						|
		ADD_INSN1(args, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | 
						|
 | 
						|
		if (liseq->body->param.flags.has_kwrest) {
 | 
						|
		    int idx = liseq->body->local_table_size - liseq->body->param.keyword->rest_start;
 | 
						|
		    ADD_GETLOCAL(args, line, idx, lvar_level);
 | 
						|
		    ADD_SEND (args, line, rb_intern("dup"), INT2FIX(0));
 | 
						|
		}
 | 
						|
		else {
 | 
						|
		    ADD_INSN1(args, line, newhash, INT2FIX(0));
 | 
						|
		}
 | 
						|
		for (i = 0; i < liseq->body->param.keyword->num; ++i) {
 | 
						|
		    ID id = liseq->body->param.keyword->table[i];
 | 
						|
		    int idx = local_size - get_local_var_idx(liseq, id);
 | 
						|
		    ADD_INSN1(args, line, putobject, ID2SYM(id));
 | 
						|
		    ADD_GETLOCAL(args, line, idx, lvar_level);
 | 
						|
		}
 | 
						|
		ADD_SEND(args, line, id_core_hash_merge_ptr, INT2FIX(i * 2 + 1));
 | 
						|
		if (liseq->body->param.flags.has_rest) {
 | 
						|
		    ADD_INSN1(args, line, newarray, INT2FIX(1));
 | 
						|
		    ADD_INSN (args, line, concatarray);
 | 
						|
		    --argc;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	    else if (liseq->body->param.flags.has_kwrest) {
 | 
						|
		int idx = liseq->body->local_table_size - liseq->body->param.keyword->rest_start;
 | 
						|
		ADD_GETLOCAL(args, line, idx, lvar_level);
 | 
						|
 | 
						|
		ADD_SEND (args, line, rb_intern("dup"), INT2FIX(0));
 | 
						|
		if (liseq->body->param.flags.has_rest) {
 | 
						|
		    ADD_INSN1(args, line, newarray, INT2FIX(1));
 | 
						|
		    ADD_INSN (args, line, concatarray);
 | 
						|
		}
 | 
						|
		else {
 | 
						|
		    argc++;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
	/* dummy receiver */
 | 
						|
	ADD_INSN1(ret, line, putobject, type == NODE_ZSUPER ? Qfalse : Qtrue);
 | 
						|
	ADD_SEQ(ret, args);
 | 
						|
	ADD_INSN3(ret, line, invokesuper,
 | 
						|
		  new_callinfo(iseq, 0, argc, flag | VM_CALL_SUPER | VM_CALL_FCALL, keywords, parent_block != NULL),
 | 
						|
		  Qnil, /* CALL_CACHE */
 | 
						|
		  parent_block);
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_ARRAY:{
 | 
						|
	CHECK(compile_array(iseq, ret, node, COMPILE_ARRAY_TYPE_ARRAY, NULL, NULL, popped) >= 0);
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_ZARRAY:{
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN1(ret, line, newarray, INT2FIX(0));
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_VALUES:{
 | 
						|
	const NODE *n = node;
 | 
						|
	if (popped) {
 | 
						|
	    COMPILE_ERROR(ERROR_ARGS "NODE_VALUES: must not be popped");
 | 
						|
	}
 | 
						|
	while (n) {
 | 
						|
	    CHECK(COMPILE(ret, "values item", n->nd_head));
 | 
						|
	    n = n->nd_next;
 | 
						|
	}
 | 
						|
	ADD_INSN1(ret, line, newarray, INT2FIX(node->nd_alen));
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_HASH:{
 | 
						|
	DECL_ANCHOR(list);
 | 
						|
	enum node_type type = node->nd_head ? nd_type(node->nd_head) : NODE_ZARRAY;
 | 
						|
 | 
						|
	INIT_ANCHOR(list);
 | 
						|
	switch (type) {
 | 
						|
	  case NODE_ARRAY:
 | 
						|
	    CHECK(compile_array(iseq, list, node->nd_head, COMPILE_ARRAY_TYPE_HASH, NULL, NULL, popped) >= 0);
 | 
						|
	    ADD_SEQ(ret, list);
 | 
						|
	    break;
 | 
						|
 | 
						|
	  case NODE_ZARRAY:
 | 
						|
	    if (popped) break;
 | 
						|
	    ADD_INSN1(ret, line, newhash, INT2FIX(0));
 | 
						|
	    break;
 | 
						|
 | 
						|
	  default:
 | 
						|
	    COMPILE_ERROR(ERROR_ARGS_AT(node->nd_head) "can't make hash with this node: %s",
 | 
						|
			  ruby_node_name(type));
 | 
						|
	    goto ng;
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_RETURN:
 | 
						|
	CHECK(compile_return(iseq, ret, node, popped));
 | 
						|
	break;
 | 
						|
      case NODE_YIELD:{
 | 
						|
	DECL_ANCHOR(args);
 | 
						|
	VALUE argc;
 | 
						|
	unsigned int flag = 0;
 | 
						|
	struct rb_call_info_kw_arg *keywords = NULL;
 | 
						|
 | 
						|
	INIT_ANCHOR(args);
 | 
						|
	if (iseq->body->type == ISEQ_TYPE_TOP ||
 | 
						|
	    iseq->body->type == ISEQ_TYPE_MAIN) {
 | 
						|
	    COMPILE_ERROR(ERROR_ARGS "Invalid yield");
 | 
						|
	    goto ng;
 | 
						|
	}
 | 
						|
 | 
						|
	if (node->nd_head) {
 | 
						|
	    argc = setup_args(iseq, args, node->nd_head, &flag, &keywords);
 | 
						|
	    CHECK(!NIL_P(argc));
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    argc = INT2FIX(0);
 | 
						|
	}
 | 
						|
 | 
						|
	ADD_SEQ(ret, args);
 | 
						|
	ADD_INSN1(ret, line, invokeblock, new_callinfo(iseq, 0, FIX2INT(argc), flag, keywords, FALSE));
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_LVAR:{
 | 
						|
	if (!popped) {
 | 
						|
	    ID id = node->nd_vid;
 | 
						|
	    int idx = iseq->body->local_iseq->body->local_table_size - get_local_var_idx(iseq, id);
 | 
						|
 | 
						|
	    debugs("id: %s idx: %d\n", rb_id2name(id), idx);
 | 
						|
	    ADD_GETLOCAL(ret, line, idx, get_lvar_level(iseq));
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_DVAR:{
 | 
						|
	int lv, idx, ls;
 | 
						|
	debugi("nd_vid", node->nd_vid);
 | 
						|
	if (!popped) {
 | 
						|
	    idx = get_dyna_var_idx(iseq, node->nd_vid, &lv, &ls);
 | 
						|
	    if (idx < 0) {
 | 
						|
		COMPILE_ERROR(ERROR_ARGS "unknown dvar (%"PRIsVALUE")",
 | 
						|
			      rb_id2str(node->nd_vid));
 | 
						|
		goto ng;
 | 
						|
	    }
 | 
						|
	    ADD_GETLOCAL(ret, line, ls - idx, lv);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_GVAR:{
 | 
						|
	ADD_INSN1(ret, line, getglobal,
 | 
						|
		  ((VALUE)node->nd_entry | 1));
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_IVAR:{
 | 
						|
	debugi("nd_vid", node->nd_vid);
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN2(ret, line, getinstancevariable,
 | 
						|
		      ID2SYM(node->nd_vid),
 | 
						|
		      get_ivar_ic_value(iseq,node->nd_vid));
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_CONST:{
 | 
						|
	debugi("nd_vid", node->nd_vid);
 | 
						|
 | 
						|
	if (ISEQ_COMPILE_DATA(iseq)->option->inline_const_cache) {
 | 
						|
	    LABEL *lend = NEW_LABEL(line);
 | 
						|
	    int ic_index = iseq->body->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 (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_CVAR:{
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN1(ret, line, getclassvariable,
 | 
						|
		      ID2SYM(node->nd_vid));
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_NTH_REF:{
 | 
						|
        if (!popped) {
 | 
						|
	    if (!node->nd_nth) {
 | 
						|
		ADD_INSN(ret, line, putnil);
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	    ADD_INSN2(ret, line, getspecial, INT2FIX(1) /* '~'  */,
 | 
						|
		      INT2FIX(node->nd_nth << 1));
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_BACK_REF:{
 | 
						|
	if (!popped) {
 | 
						|
	    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:
 | 
						|
	    CHECK(COMPILE(recv, "receiver", node->nd_recv));
 | 
						|
	    CHECK(COMPILE(val, "value", node->nd_value));
 | 
						|
	    break;
 | 
						|
	  case NODE_MATCH3:
 | 
						|
	    CHECK(COMPILE(recv, "receiver", node->nd_value));
 | 
						|
	    CHECK(COMPILE(val, "value", node->nd_recv));
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
 | 
						|
	if (ISEQ_COMPILE_DATA(iseq)->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_INSN2(ret, line, opt_regexpmatch2, new_callinfo(iseq, idEqTilde, 1, 0, NULL, FALSE), Qnil);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    ADD_SEQ(ret, recv);
 | 
						|
	    ADD_SEQ(ret, val);
 | 
						|
	    ADD_SEND(ret, line, idEqTilde, INT2FIX(1));
 | 
						|
	}
 | 
						|
 | 
						|
	if (node->nd_args) {
 | 
						|
	    compile_named_capture_assign(iseq, ret, node->nd_args);
 | 
						|
	}
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_LIT:{
 | 
						|
	debugp_param("lit", node->nd_lit);
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN1(ret, line, putobject, node->nd_lit);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_STR:{
 | 
						|
	debugp_param("nd_lit", node->nd_lit);
 | 
						|
	if (!popped) {
 | 
						|
	    VALUE lit = node->nd_lit;
 | 
						|
	    if (!ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) {
 | 
						|
		lit = freeze_literal(iseq, lit);
 | 
						|
		ADD_INSN1(ret, line, putstring, lit);
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
 | 
						|
		    VALUE debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX(line));
 | 
						|
		    lit = rb_str_dup(lit);
 | 
						|
		    rb_ivar_set(lit, id_debug_created_info, rb_obj_freeze(debug_info));
 | 
						|
		    lit = rb_str_freeze(lit);
 | 
						|
		}
 | 
						|
		else {
 | 
						|
		    lit = rb_fstring(lit);
 | 
						|
		}
 | 
						|
		ADD_INSN1(ret, line, putobject, lit);
 | 
						|
		iseq_add_mark_object_compile_time(iseq, lit);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_DSTR:{
 | 
						|
	compile_dstr(iseq, ret, node);
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    if (ISEQ_COMPILE_DATA(iseq)->option->frozen_string_literal) {
 | 
						|
		VALUE debug_info = Qnil;
 | 
						|
		if (ISEQ_COMPILE_DATA(iseq)->option->debug_frozen_string_literal || RTEST(ruby_debug)) {
 | 
						|
		    debug_info = rb_ary_new_from_args(2, rb_iseq_path(iseq), INT2FIX(line));
 | 
						|
		    iseq_add_mark_object_compile_time(iseq, rb_obj_freeze(debug_info));
 | 
						|
		}
 | 
						|
		ADD_INSN1(ret, line, freezestring, debug_info);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_XSTR:{
 | 
						|
	ADD_CALL_RECEIVER(ret, line);
 | 
						|
	ADD_INSN1(ret, line, putobject, freeze_literal(iseq, node->nd_lit));
 | 
						|
	ADD_CALL(ret, line, idBackquote, INT2FIX(1));
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_DXSTR:{
 | 
						|
	ADD_CALL_RECEIVER(ret, line);
 | 
						|
	compile_dstr(iseq, ret, node);
 | 
						|
	ADD_CALL(ret, line, idBackquote, INT2FIX(1));
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_EVSTR:{
 | 
						|
	CHECK(COMPILE(ret, "nd_body", node->nd_body));
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	else if (!all_string_result_p(node->nd_body)) {
 | 
						|
	    const unsigned int flag = VM_CALL_FCALL;
 | 
						|
	    LABEL *isstr = NEW_LABEL(line);
 | 
						|
	    ADD_INSN(ret, line, dup);
 | 
						|
	    ADD_INSN2(ret, line, branchiftype, INT2FIX(T_STRING), isstr);
 | 
						|
	    LABEL_REF(isstr);
 | 
						|
	    ADD_INSN(ret, line, dup);
 | 
						|
	    ADD_SEND_R(ret, line, idTo_s, INT2FIX(0), NULL, INT2FIX(flag), NULL);
 | 
						|
	    ADD_INSN(ret, line, tostring);
 | 
						|
	    ADD_LABEL(ret, isstr);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_DREGX:{
 | 
						|
	compile_dregx(iseq, ret, node);
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_ONCE:{
 | 
						|
	int ic_index = iseq->body->is_size++;
 | 
						|
	const rb_iseq_t *block_iseq;
 | 
						|
	block_iseq = NEW_CHILD_ISEQ(node->nd_body, make_name_for_block(iseq), ISEQ_TYPE_PLAIN, line);
 | 
						|
 | 
						|
	ADD_INSN2(ret, line, once, block_iseq, INT2FIX(ic_index));
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_ARGSCAT:{
 | 
						|
	if (popped) {
 | 
						|
	    CHECK(COMPILE(ret, "argscat head", node->nd_head));
 | 
						|
	    ADD_INSN1(ret, line, splatarray, Qfalse);
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	    CHECK(COMPILE(ret, "argscat body", node->nd_body));
 | 
						|
	    ADD_INSN1(ret, line, splatarray, Qfalse);
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    CHECK(COMPILE(ret, "argscat head", node->nd_head));
 | 
						|
	    CHECK(COMPILE(ret, "argscat body", node->nd_body));
 | 
						|
	    ADD_INSN(ret, line, concatarray);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_ARGSPUSH:{
 | 
						|
	if (popped) {
 | 
						|
	    CHECK(COMPILE(ret, "arsgpush head", node->nd_head));
 | 
						|
	    ADD_INSN1(ret, line, splatarray, Qfalse);
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	    CHECK(COMPILE_(ret, "argspush body", node->nd_body, popped));
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    CHECK(COMPILE(ret, "arsgpush head", node->nd_head));
 | 
						|
	    CHECK(COMPILE_(ret, "argspush body", node->nd_body, popped));
 | 
						|
	    ADD_INSN1(ret, line, newarray, INT2FIX(1));
 | 
						|
	    ADD_INSN(ret, line, concatarray);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_SPLAT:{
 | 
						|
	CHECK(COMPILE(ret, "splat", node->nd_head));
 | 
						|
	ADD_INSN1(ret, line, splatarray, Qtrue);
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_DEFN:{
 | 
						|
	const rb_iseq_t *method_iseq = NEW_ISEQ(node->nd_defn,
 | 
						|
						rb_id2str(node->nd_mid),
 | 
						|
						ISEQ_TYPE_METHOD, line);
 | 
						|
 | 
						|
	debugp_param("defn/iseq", rb_iseqw_new(method_iseq));
 | 
						|
 | 
						|
	ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | 
						|
	ADD_INSN1(ret, line, putobject, ID2SYM(node->nd_mid));
 | 
						|
	ADD_INSN1(ret, line, putiseq, method_iseq);
 | 
						|
	ADD_SEND (ret, line, id_core_define_method, INT2FIX(2));
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_DEFS:{
 | 
						|
	const rb_iseq_t * singleton_method = NEW_ISEQ(node->nd_defn,
 | 
						|
						      rb_id2str(node->nd_mid),
 | 
						|
						      ISEQ_TYPE_METHOD, line);
 | 
						|
 | 
						|
	debugp_param("defs/iseq", rb_iseqw_new(singleton_method));
 | 
						|
 | 
						|
	ADD_INSN1(ret, line, putspecialobject, INT2FIX(VM_SPECIAL_OBJECT_VMCORE));
 | 
						|
	CHECK(COMPILE(ret, "defs: recv", node->nd_recv));
 | 
						|
	ADD_INSN1(ret, line, putobject, ID2SYM(node->nd_mid));
 | 
						|
	ADD_INSN1(ret, line, putiseq, singleton_method);
 | 
						|
	ADD_SEND (ret, line, id_core_define_singleton_method, INT2FIX(3));
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    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));
 | 
						|
	CHECK(COMPILE(ret, "alias arg1", node->nd_1st));
 | 
						|
	CHECK(COMPILE(ret, "alias arg2", node->nd_2nd));
 | 
						|
	ADD_SEND(ret, line, id_core_set_method_alias, INT2FIX(3));
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    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->nd_alias));
 | 
						|
	ADD_INSN1(ret, line, putobject, ID2SYM(node->nd_orig));
 | 
						|
	ADD_SEND(ret, line, id_core_set_variable_alias, INT2FIX(2));
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    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));
 | 
						|
	CHECK(COMPILE(ret, "undef arg", node->nd_undef));
 | 
						|
	ADD_SEND(ret, line, id_core_undef_method, INT2FIX(2));
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_CLASS:{
 | 
						|
	const rb_iseq_t *class_iseq = NEW_CHILD_ISEQ(node->nd_body,
 | 
						|
						     rb_sprintf("<class:%"PRIsVALUE">", rb_id2str(node->nd_cpath->nd_mid)),
 | 
						|
						     ISEQ_TYPE_CLASS, line);
 | 
						|
	const int flags = VM_DEFINECLASS_TYPE_CLASS |
 | 
						|
	    (node->nd_super ? VM_DEFINECLASS_FLAG_HAS_SUPERCLASS : 0) |
 | 
						|
	    compile_cpath(ret, iseq, node->nd_cpath);
 | 
						|
 | 
						|
	CHECK(COMPILE(ret, "super", node->nd_super));
 | 
						|
	ADD_INSN3(ret, line, defineclass, ID2SYM(node->nd_cpath->nd_mid), class_iseq, INT2FIX(flags));
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_MODULE:{
 | 
						|
        const rb_iseq_t *module_iseq = NEW_CHILD_ISEQ(node->nd_body,
 | 
						|
						      rb_sprintf("<module:%"PRIsVALUE">", rb_id2str(node->nd_cpath->nd_mid)),
 | 
						|
						      ISEQ_TYPE_CLASS, line);
 | 
						|
	const int flags = VM_DEFINECLASS_TYPE_MODULE |
 | 
						|
	    compile_cpath(ret, iseq, node->nd_cpath);
 | 
						|
 | 
						|
	ADD_INSN (ret, line, putnil); /* dummy */
 | 
						|
	ADD_INSN3(ret, line, defineclass, ID2SYM(node->nd_cpath->nd_mid), module_iseq, INT2FIX(flags));
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_SCLASS:{
 | 
						|
	ID singletonclass;
 | 
						|
	const rb_iseq_t *singleton_class = NEW_ISEQ(node->nd_body, rb_fstring_cstr("singleton class"),
 | 
						|
						    ISEQ_TYPE_CLASS, line);
 | 
						|
 | 
						|
	CHECK(COMPILE(ret, "sclass#recv", node->nd_recv));
 | 
						|
	ADD_INSN (ret, line, putnil);
 | 
						|
	CONST_ID(singletonclass, "singletonclass");
 | 
						|
	ADD_INSN3(ret, line, defineclass,
 | 
						|
		  ID2SYM(singletonclass), singleton_class,
 | 
						|
		  INT2FIX(VM_DEFINECLASS_TYPE_SINGLETON_CLASS));
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    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->body->is_size++;
 | 
						|
 | 
						|
	    DECL_ANCHOR(pref);
 | 
						|
	    DECL_ANCHOR(body);
 | 
						|
 | 
						|
	    INIT_ANCHOR(pref);
 | 
						|
	    INIT_ANCHOR(body);
 | 
						|
	    CHECK(compile_const_prefix(iseq, node, pref, body));
 | 
						|
	    if (LIST_INSN_SIZE_ZERO(pref)) {
 | 
						|
		if (ISEQ_COMPILE_DATA(iseq)->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(iseq)->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);
 | 
						|
	    CHECK(COMPILE(ret, "colon2#nd_head", node->nd_head));
 | 
						|
	    ADD_CALL(ret, line, node->nd_mid, INT2FIX(1));
 | 
						|
	}
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_COLON3:{
 | 
						|
	LABEL *lend = NEW_LABEL(line);
 | 
						|
	int ic_index = iseq->body->is_size++;
 | 
						|
 | 
						|
	debugi("colon3#nd_mid", node->nd_mid);
 | 
						|
 | 
						|
	/* add cache insn */
 | 
						|
	if (ISEQ_COMPILE_DATA(iseq)->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(iseq)->option->inline_const_cache) {
 | 
						|
	    ADD_INSN1(ret, line, setinlinecache, INT2FIX(ic_index));
 | 
						|
	    ADD_LABEL(ret, lend);
 | 
						|
	}
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_DOT2:
 | 
						|
      case NODE_DOT3:{
 | 
						|
	int excl = type == NODE_DOT3;
 | 
						|
	VALUE flag = INT2FIX(excl);
 | 
						|
	const NODE *b = node->nd_beg;
 | 
						|
	const NODE *e = node->nd_end;
 | 
						|
	if (number_literal_p(b) && number_literal_p(e)) {
 | 
						|
	    if (!popped) {
 | 
						|
		VALUE val = rb_range_new(b->nd_lit, e->nd_lit, excl);
 | 
						|
		iseq_add_mark_object_compile_time(iseq, val);
 | 
						|
		ADD_INSN1(ret, line, putobject, val);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    CHECK(COMPILE_(ret, "min", b, popped));
 | 
						|
	    CHECK(COMPILE_(ret, "max", e, popped));
 | 
						|
	    if (!popped) {
 | 
						|
		ADD_INSN1(ret, line, newrange, flag);
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_FLIP2:
 | 
						|
      case NODE_FLIP3:{
 | 
						|
	LABEL *lend = NEW_LABEL(line);
 | 
						|
	LABEL *ltrue = NEW_LABEL(line);
 | 
						|
	LABEL *lfalse = NEW_LABEL(line);
 | 
						|
	CHECK(compile_flip_flop(iseq, ret, node, type == NODE_FLIP2,
 | 
						|
				ltrue, lfalse));
 | 
						|
	ADD_LABEL(ret, ltrue);
 | 
						|
	ADD_INSN1(ret, line, putobject, Qtrue);
 | 
						|
	ADD_INSNL(ret, line, jump, lend);
 | 
						|
	ADD_LABEL(ret, lfalse);
 | 
						|
	ADD_INSN1(ret, line, putobject, Qfalse);
 | 
						|
	ADD_LABEL(ret, lend);
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_SELF:{
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN(ret, line, putself);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_NIL:{
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN(ret, line, putnil);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_TRUE:{
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN1(ret, line, putobject, Qtrue);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_FALSE:{
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN1(ret, line, putobject, Qfalse);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_ERRINFO:{
 | 
						|
	if (!popped) {
 | 
						|
	    if (iseq->body->type == ISEQ_TYPE_RESCUE) {
 | 
						|
		ADD_GETLOCAL(ret, line, LVAR_ERRINFO, 0);
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		const rb_iseq_t *ip = iseq;
 | 
						|
		int level = 0;
 | 
						|
		while (ip) {
 | 
						|
		    if (ip->body->type == ISEQ_TYPE_RESCUE) {
 | 
						|
			break;
 | 
						|
		    }
 | 
						|
		    ip = ip->body->parent_iseq;
 | 
						|
		    level++;
 | 
						|
		}
 | 
						|
		if (ip) {
 | 
						|
		    ADD_GETLOCAL(ret, line, LVAR_ERRINFO, level);
 | 
						|
		}
 | 
						|
		else {
 | 
						|
		    ADD_INSN(ret, line, putnil);
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_DEFINED:
 | 
						|
	if (!popped) {
 | 
						|
	    CHECK(compile_defined_expr(iseq, ret, node, Qtrue));
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      case NODE_POSTEXE:{
 | 
						|
	/* compiled to:
 | 
						|
	 *   ONCE{ rb_mRubyVMFrozenCore::core#set_postexe{ ... } }
 | 
						|
	 */
 | 
						|
	int is_index = iseq->body->is_size++;
 | 
						|
	const rb_iseq_t *once_iseq =
 | 
						|
	    new_child_iseq_ifunc(iseq, IFUNC_NEW(build_postexe_iseq, node->nd_body, 0),
 | 
						|
				 rb_fstring(make_name_for_block(iseq)), iseq, ISEQ_TYPE_BLOCK, line);
 | 
						|
 | 
						|
	ADD_INSN2(ret, line, once, once_iseq, INT2FIX(is_index));
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_KW_ARG:
 | 
						|
	{
 | 
						|
	    LABEL *end_label = NEW_LABEL(nd_line(node));
 | 
						|
	    const NODE *default_value = node->nd_body->nd_value;
 | 
						|
 | 
						|
	    if (default_value == (const NODE *)-1) {
 | 
						|
		/* required argument. do nothing */
 | 
						|
		COMPILE_ERROR(ERROR_ARGS "unreachable");
 | 
						|
		goto ng;
 | 
						|
	    }
 | 
						|
	    else if (nd_type(default_value) == NODE_LIT ||
 | 
						|
		     nd_type(default_value) == NODE_NIL ||
 | 
						|
		     nd_type(default_value) == NODE_TRUE ||
 | 
						|
		     nd_type(default_value) == NODE_FALSE) {
 | 
						|
		COMPILE_ERROR(ERROR_ARGS "unreachable");
 | 
						|
		goto ng;
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		/* if keywordcheck(_kw_bits, nth_keyword)
 | 
						|
		 *   kw = default_value
 | 
						|
		 * end
 | 
						|
		 */
 | 
						|
		int kw_bits_idx = iseq->body->local_table_size - iseq->body->param.keyword->bits_start;
 | 
						|
		int keyword_idx = iseq->body->param.keyword->num;
 | 
						|
 | 
						|
		ADD_INSN2(ret, line, checkkeyword, INT2FIX(kw_bits_idx + VM_ENV_DATA_SIZE - 1), INT2FIX(keyword_idx));
 | 
						|
		ADD_INSNL(ret, line, branchif, end_label);
 | 
						|
		CHECK(COMPILE_POPPED(ret, "keyword default argument", node->nd_body));
 | 
						|
		ADD_LABEL(ret, end_label);
 | 
						|
	    }
 | 
						|
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
      case NODE_DSYM:{
 | 
						|
	compile_dstr(iseq, ret, node);
 | 
						|
	if (!popped) {
 | 
						|
	    ADD_INSN(ret, line, intern);
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_ATTRASGN:{
 | 
						|
	DECL_ANCHOR(recv);
 | 
						|
	DECL_ANCHOR(args);
 | 
						|
	unsigned int flag = 0;
 | 
						|
	ID mid = node->nd_mid;
 | 
						|
	LABEL *lskip = 0;
 | 
						|
	VALUE argc;
 | 
						|
 | 
						|
	/* optimization shortcut
 | 
						|
	 *   obj["literal"] = value -> opt_aset_with(obj, "literal", value)
 | 
						|
	 */
 | 
						|
	if (mid == idASET && !private_recv_p(node) && node->nd_args &&
 | 
						|
	    nd_type(node->nd_args) == NODE_ARRAY && node->nd_args->nd_alen == 2 &&
 | 
						|
	    nd_type(node->nd_args->nd_head) == NODE_STR &&
 | 
						|
	    ISEQ_COMPILE_DATA(iseq)->current_block == NULL &&
 | 
						|
	    ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction)
 | 
						|
	{
 | 
						|
	    VALUE str = freeze_literal(iseq, node->nd_args->nd_head->nd_lit);
 | 
						|
	    CHECK(COMPILE(ret, "recv", node->nd_recv));
 | 
						|
	    CHECK(COMPILE(ret, "value", node->nd_args->nd_next->nd_head));
 | 
						|
	    if (!popped) {
 | 
						|
		ADD_INSN(ret, line, swap);
 | 
						|
		ADD_INSN1(ret, line, topn, INT2FIX(1));
 | 
						|
	    }
 | 
						|
	    ADD_INSN3(ret, line, opt_aset_with,
 | 
						|
		      new_callinfo(iseq, idASET, 2, 0, NULL, FALSE),
 | 
						|
		      NULL/* CALL_CACHE */, str);
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
 | 
						|
	INIT_ANCHOR(recv);
 | 
						|
	INIT_ANCHOR(args);
 | 
						|
	argc = setup_args(iseq, args, node->nd_args, &flag, NULL);
 | 
						|
	CHECK(!NIL_P(argc));
 | 
						|
 | 
						|
	flag |= COMPILE_RECV(recv, "recv", node);
 | 
						|
 | 
						|
	debugp_param("argc", argc);
 | 
						|
	debugp_param("nd_mid", ID2SYM(mid));
 | 
						|
 | 
						|
	if (!rb_is_attrset_id(mid)) {
 | 
						|
	    /* safe nav attr */
 | 
						|
	    mid = rb_id_attrset(mid);
 | 
						|
	    ADD_INSN(recv, line, dup);
 | 
						|
	    lskip = NEW_LABEL(line);
 | 
						|
	    ADD_INSNL(recv, line, branchnil, lskip);
 | 
						|
	}
 | 
						|
	if (!popped) {
 | 
						|
	    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, 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, 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_WITH_FLAG(ret, line, mid, argc, INT2FIX(flag));
 | 
						|
	if (lskip) ADD_LABEL(ret, lskip);
 | 
						|
	ADD_INSN(ret, line, pop);
 | 
						|
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      case NODE_LAMBDA:{
 | 
						|
	/* compile same as lambda{...} */
 | 
						|
	const rb_iseq_t *block = NEW_CHILD_ISEQ(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, idLambda, argc, block);
 | 
						|
 | 
						|
	if (popped) {
 | 
						|
	    ADD_INSN(ret, line, pop);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
      }
 | 
						|
      default:
 | 
						|
	UNKNOWN_NODE("iseq_compile_each", node, COMPILE_NG);
 | 
						|
      ng:
 | 
						|
	debug_node_end();
 | 
						|
	return COMPILE_NG;
 | 
						|
    }
 | 
						|
 | 
						|
    /* remove tracecoverage instruction if there is no relevant instruction */
 | 
						|
    if (IS_TRACE(ret->last) && ((TRACE*) ret->last)->event == RUBY_EVENT_LINE) {
 | 
						|
	LINK_ELEMENT *insn = ret->last->prev;
 | 
						|
	if (IS_INSN(insn) &&
 | 
						|
	    IS_INSN_ID(insn, tracecoverage) &&
 | 
						|
	    FIX2LONG(OPERAND_AT(insn, 0)) == RUBY_EVENT_COVERAGE_LINE
 | 
						|
	) {
 | 
						|
	    ELEM_REMOVE(insn); /* remove tracecovearge */
 | 
						|
	    RARRAY_ASET(ISEQ_LINE_COVERAGE(iseq), line - 1, Qnil);
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    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 VALUE
 | 
						|
opobj_inspect(VALUE obj)
 | 
						|
{
 | 
						|
    struct RBasic *r = (struct RBasic *) obj;
 | 
						|
    if (!SPECIAL_CONST_P(r)  && r->klass == 0) {
 | 
						|
	switch (BUILTIN_TYPE(r)) {
 | 
						|
	  case T_STRING:
 | 
						|
	    obj = rb_str_new_cstr(RSTRING_PTR(obj));
 | 
						|
	    break;
 | 
						|
	  case T_ARRAY:
 | 
						|
	    obj = rb_ary_dup(obj);
 | 
						|
	    break;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    return rb_inspect(obj);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
static VALUE
 | 
						|
insn_data_to_s_detail(INSN *iobj)
 | 
						|
{
 | 
						|
    VALUE str = rb_sprintf("%-20s ", 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];
 | 
						|
 | 
						|
	    switch (type) {
 | 
						|
	      case TS_OFFSET:	/* label(destination position) */
 | 
						|
		{
 | 
						|
		    LABEL *lobj = (LABEL *)OPERAND_AT(iobj, j);
 | 
						|
		    rb_str_catf(str, LABEL_FORMAT, 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 = (VALUE)iseq;
 | 
						|
		    }
 | 
						|
		    rb_str_concat(str, opobj_inspect(val));
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	      case TS_LINDEX:
 | 
						|
	      case TS_NUM:	/* ulong */
 | 
						|
	      case TS_VALUE:	/* VALUE */
 | 
						|
		{
 | 
						|
		    VALUE v = OPERAND_AT(iobj, j);
 | 
						|
		    rb_str_concat(str, opobj_inspect(v));
 | 
						|
		    break;
 | 
						|
		}
 | 
						|
	      case TS_ID:	/* ID */
 | 
						|
		rb_str_concat(str, opobj_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_append(str, rb_id2str(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 */
 | 
						|
		{
 | 
						|
		    struct rb_call_info *ci = (struct rb_call_info *)OPERAND_AT(iobj, j);
 | 
						|
		    rb_str_cat2(str, "<callinfo:");
 | 
						|
		    if (ci->mid) rb_str_catf(str, "%"PRIsVALUE, rb_id2str(ci->mid));
 | 
						|
		    rb_str_catf(str, ", %d>", ci->orig_argc);
 | 
						|
		    break;
 | 
						|
		}
 | 
						|
	      case TS_CALLCACHE: /* call cache */
 | 
						|
		{
 | 
						|
		    rb_str_catf(str, "<call cache>");
 | 
						|
		    break;
 | 
						|
		}
 | 
						|
	      case TS_CDHASH:	/* case/when condition cache */
 | 
						|
		rb_str_cat2(str, "<ch>");
 | 
						|
		break;
 | 
						|
	      case TS_FUNCPTR:
 | 
						|
		{
 | 
						|
		    const void *func = (const void *)OPERAND_AT(iobj, j);
 | 
						|
#ifdef HAVE_DLADDR
 | 
						|
		    Dl_info info;
 | 
						|
		    if (dladdr(func, &info) && info.dli_sname) {
 | 
						|
			rb_str_cat2(str, info.dli_sname);
 | 
						|
			break;
 | 
						|
		    }
 | 
						|
#endif
 | 
						|
		    rb_str_catf(str, "<%p>", func);
 | 
						|
		}
 | 
						|
		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(const LINK_ELEMENT *link)
 | 
						|
{
 | 
						|
    dump_disasm_list_with_cursor(link, NULL, NULL);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
dump_disasm_list_with_cursor(const LINK_ELEMENT *link, const LINK_ELEMENT *curr, const LABEL *dest)
 | 
						|
{
 | 
						|
    int pos = 0;
 | 
						|
    INSN *iobj;
 | 
						|
    LABEL *lobj;
 | 
						|
    VALUE str;
 | 
						|
 | 
						|
    printf("-- raw disasm--------\n");
 | 
						|
 | 
						|
    while (link) {
 | 
						|
	if (curr) printf(curr == link ? "*" : " ");
 | 
						|
	switch (link->type) {
 | 
						|
	  case ISEQ_ELEMENT_INSN:
 | 
						|
	    {
 | 
						|
		iobj = (INSN *)link;
 | 
						|
		str = insn_data_to_s_detail(iobj);
 | 
						|
		printf("%04d %-65s(%4u)\n", pos, StringValueCStr(str), iobj->insn_info.line_no);
 | 
						|
		pos += insn_data_length(iobj);
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  case ISEQ_ELEMENT_LABEL:
 | 
						|
	    {
 | 
						|
		lobj = (LABEL *)link;
 | 
						|
		printf(LABEL_FORMAT" [sp: %d]%s\n", lobj->label_no, lobj->sp,
 | 
						|
		       dest == lobj ? " <---" : "");
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  case ISEQ_ELEMENT_TRACE:
 | 
						|
	    {
 | 
						|
		TRACE *trace = (TRACE *)link;
 | 
						|
		printf("trace: %0x\n", trace->event);
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  case ISEQ_ELEMENT_ADJUST:
 | 
						|
	    {
 | 
						|
		ADJUST *adjust = (ADJUST *)link;
 | 
						|
		printf("adjust: [label: %d]\n", adjust->label ? adjust->label->label_no : -1);
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	  default:
 | 
						|
	    /* ignore */
 | 
						|
	    rb_raise(rb_eSyntaxError, "dump_disasm_list error: %ld\n", FIX2LONG(link->type));
 | 
						|
	}
 | 
						|
	link = link->next;
 | 
						|
    }
 | 
						|
    printf("---------------------\n");
 | 
						|
    fflush(stdout);
 | 
						|
}
 | 
						|
 | 
						|
const char *
 | 
						|
rb_insns_name(int i)
 | 
						|
{
 | 
						|
    return insn_name(i);
 | 
						|
}
 | 
						|
 | 
						|
VALUE
 | 
						|
rb_insns_name_array(void)
 | 
						|
{
 | 
						|
    VALUE ary = rb_ary_new_capa(VM_INSTRUCTION_SIZE);
 | 
						|
    int i;
 | 
						|
    for (i = 0; i < VM_INSTRUCTION_SIZE; i++) {
 | 
						|
	rb_ary_push(ary, rb_fstring_cstr(insn_name(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_to_symbol_type(obj);
 | 
						|
 | 
						|
    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;
 | 
						|
    }
 | 
						|
    LABEL_REF(label);
 | 
						|
    return label;
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
get_exception_sym2type(VALUE sym)
 | 
						|
{
 | 
						|
#undef rb_intern
 | 
						|
#define rb_intern(str) rb_intern_const(str)
 | 
						|
    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;
 | 
						|
    rb_raise(rb_eSyntaxError, "invalid exception symbol: %+"PRIsVALUE, sym);
 | 
						|
    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++) {
 | 
						|
	const rb_iseq_t *eiseq;
 | 
						|
	VALUE v, type;
 | 
						|
	const VALUE *ptr;
 | 
						|
	LABEL *lstart, *lend, *lcont;
 | 
						|
	unsigned int sp;
 | 
						|
 | 
						|
	v = rb_to_array_type(RARRAY_AREF(exception, i));
 | 
						|
	if (RARRAY_LEN(v) != 6) {
 | 
						|
	    rb_raise(rb_eSyntaxError, "wrong exception entry");
 | 
						|
	}
 | 
						|
	ptr  = RARRAY_CONST_PTR(v);
 | 
						|
	type = get_exception_sym2type(ptr[0]);
 | 
						|
	if (ptr[1] == Qnil) {
 | 
						|
	    eiseq = NULL;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    eiseq = rb_iseqw_to_iseq(rb_iseq_load(ptr[1], (VALUE)iseq, 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     = NUM2UINT(ptr[5]);
 | 
						|
 | 
						|
	/* TODO: Dirty Hack!  Fix me */
 | 
						|
	if (type == CATCH_TYPE_RESCUE ||
 | 
						|
	    type == CATCH_TYPE_BREAK ||
 | 
						|
	    type == CATCH_TYPE_NEXT) {
 | 
						|
	    ++sp;
 | 
						|
	}
 | 
						|
 | 
						|
	lcont->sp = sp;
 | 
						|
 | 
						|
	ADD_CATCH_ENTRY(type, lstart, lend, eiseq, lcont);
 | 
						|
 | 
						|
	RB_GC_GUARD(v);
 | 
						|
    }
 | 
						|
    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 const rb_iseq_t *
 | 
						|
iseq_build_load_iseq(const rb_iseq_t *iseq, VALUE op)
 | 
						|
{
 | 
						|
    VALUE iseqw;
 | 
						|
    const rb_iseq_t *loaded_iseq;
 | 
						|
 | 
						|
    if (RB_TYPE_P(op, T_ARRAY)) {
 | 
						|
	iseqw = rb_iseq_load(op, (VALUE)iseq, Qnil);
 | 
						|
    }
 | 
						|
    else if (CLASS_OF(op) == rb_cISeq) {
 | 
						|
	iseqw = op;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	rb_raise(rb_eSyntaxError, "ISEQ is required");
 | 
						|
    }
 | 
						|
 | 
						|
    loaded_iseq = rb_iseqw_to_iseq(iseqw);
 | 
						|
    iseq_add_mark_object(iseq, (VALUE)loaded_iseq);
 | 
						|
    return loaded_iseq;
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
iseq_build_callinfo_from_hash(rb_iseq_t *iseq, VALUE op)
 | 
						|
{
 | 
						|
    ID mid = 0;
 | 
						|
    int orig_argc = 0;
 | 
						|
    unsigned int flag = 0;
 | 
						|
    struct rb_call_info_kw_arg *kw_arg = 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 vkw_arg = rb_hash_aref(op, ID2SYM(rb_intern("kw_arg")));
 | 
						|
 | 
						|
	if (!NIL_P(vmid)) mid = SYM2ID(vmid);
 | 
						|
	if (!NIL_P(vflag)) flag = NUM2UINT(vflag);
 | 
						|
	if (!NIL_P(vorig_argc)) orig_argc = FIX2INT(vorig_argc);
 | 
						|
 | 
						|
	if (!NIL_P(vkw_arg)) {
 | 
						|
	    int i;
 | 
						|
	    int len = RARRAY_LENINT(vkw_arg);
 | 
						|
	    size_t n = rb_call_info_kw_arg_bytes(len);
 | 
						|
 | 
						|
	    kw_arg = xmalloc(n);
 | 
						|
	    kw_arg->keyword_len = len;
 | 
						|
	    for (i = 0; i < len; i++) {
 | 
						|
		VALUE kw = RARRAY_AREF(vkw_arg, i);
 | 
						|
		SYM2ID(kw);	/* make immortal */
 | 
						|
		kw_arg->keywords[i] = kw;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    return (VALUE)new_callinfo(iseq, mid, orig_argc, flag, kw_arg, (flag & VM_CALL_ARGS_SIMPLE) == 0);
 | 
						|
}
 | 
						|
 | 
						|
static rb_event_flag_t
 | 
						|
event_name_to_flag(VALUE sym)
 | 
						|
{
 | 
						|
#define CHECK_EVENT(ev) if (sym == ID2SYM(rb_intern(#ev))) return ev;
 | 
						|
		CHECK_EVENT(RUBY_EVENT_LINE);
 | 
						|
		CHECK_EVENT(RUBY_EVENT_CLASS);
 | 
						|
		CHECK_EVENT(RUBY_EVENT_END);
 | 
						|
		CHECK_EVENT(RUBY_EVENT_CALL);
 | 
						|
		CHECK_EVENT(RUBY_EVENT_RETURN);
 | 
						|
		CHECK_EVENT(RUBY_EVENT_B_CALL);
 | 
						|
		CHECK_EVENT(RUBY_EVENT_B_RETURN);
 | 
						|
#undef CHECK_EVENT
 | 
						|
    return RUBY_EVENT_NONE;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
 | 
						|
			 VALUE body, VALUE labels_wrapper)
 | 
						|
{
 | 
						|
    /* TODO: body should be frozen */
 | 
						|
    const VALUE *ptr = RARRAY_CONST_PTR(body);
 | 
						|
    long i, len = RARRAY_LEN(body);
 | 
						|
    struct st_table *labels_table = DATA_PTR(labels_wrapper);
 | 
						|
    int j;
 | 
						|
    int line_no = 0;
 | 
						|
    int ret = COMPILE_OK;
 | 
						|
 | 
						|
    /*
 | 
						|
     * 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)) {
 | 
						|
	    rb_event_flag_t event;
 | 
						|
	    if ((event = event_name_to_flag(obj)) != RUBY_EVENT_NONE) {
 | 
						|
		ADD_TRACE(anchor, event);
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		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 */
 | 
						|
		COMPILE_ERROR(iseq, line_no,
 | 
						|
			      "unknown instruction: %+"PRIsVALUE, insn);
 | 
						|
		ret = COMPILE_NG;
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
 | 
						|
	    if (argc != insn_len((VALUE)insn_id)-1) {
 | 
						|
		COMPILE_ERROR(iseq, line_no,
 | 
						|
			      "operand size mismatch");
 | 
						|
		ret = COMPILE_NG;
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
 | 
						|
	    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] = (VALUE)iseq_build_load_iseq(iseq, op);
 | 
						|
			    }
 | 
						|
			    else {
 | 
						|
				argv[j] = 0;
 | 
						|
			    }
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		      case TS_GENTRY:
 | 
						|
			op = rb_to_symbol_type(op);
 | 
						|
			argv[j] = (VALUE)rb_global_entry(SYM2ID(op));
 | 
						|
			break;
 | 
						|
		      case TS_IC:
 | 
						|
			argv[j] = op;
 | 
						|
			if (NUM2UINT(op) >= iseq->body->is_size) {
 | 
						|
			    iseq->body->is_size = NUM2INT(op) + 1;
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		      case TS_CALLINFO:
 | 
						|
			argv[j] = iseq_build_callinfo_from_hash(iseq, op);
 | 
						|
			break;
 | 
						|
		      case TS_CALLCACHE:
 | 
						|
			argv[j] = Qfalse;
 | 
						|
			break;
 | 
						|
		      case TS_ID:
 | 
						|
			argv[j] = rb_to_symbol_type(op);
 | 
						|
			break;
 | 
						|
		      case TS_CDHASH:
 | 
						|
			{
 | 
						|
			    int i;
 | 
						|
			    VALUE map = rb_hash_new_with_size(RARRAY_LEN(op)/2);
 | 
						|
 | 
						|
			    rb_hash_tbl_raw(map)->type = &cdhash_type;
 | 
						|
			    op = rb_to_array_type(op);
 | 
						|
			    for (i=0; i<RARRAY_LEN(op); i+=2) {
 | 
						|
				VALUE key = RARRAY_AREF(op, i);
 | 
						|
				VALUE sym = RARRAY_AREF(op, i+1);
 | 
						|
				LABEL *label =
 | 
						|
				  register_label(iseq, labels_table, sym);
 | 
						|
				rb_hash_aset(map, key, (VALUE)label | 1);
 | 
						|
			    }
 | 
						|
			    RB_GC_GUARD(op);
 | 
						|
			    argv[j] = map;
 | 
						|
			    rb_iseq_add_mark_object(iseq, map);
 | 
						|
			}
 | 
						|
			break;
 | 
						|
		      case TS_FUNCPTR:
 | 
						|
			{
 | 
						|
#if SIZEOF_VALUE <= SIZEOF_LONG
 | 
						|
			    long funcptr = NUM2LONG(op);
 | 
						|
#else
 | 
						|
			    LONG_LONG funcptr = NUM2LL(op);
 | 
						|
#endif
 | 
						|
			    argv[j] = (VALUE)funcptr;
 | 
						|
			}
 | 
						|
			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");
 | 
						|
	}
 | 
						|
    }
 | 
						|
    DATA_PTR(labels_wrapper) = 0;
 | 
						|
    validate_labels(iseq, labels_table);
 | 
						|
    if (!ret) return ret;
 | 
						|
    return iseq_setup(iseq, anchor);
 | 
						|
}
 | 
						|
 | 
						|
#define CHECK_ARRAY(v)   rb_to_array_type(v)
 | 
						|
#define CHECK_SYMBOL(v)  rb_to_symbol_type(v)
 | 
						|
 | 
						|
static int
 | 
						|
int_param(int *dst, VALUE param, VALUE sym)
 | 
						|
{
 | 
						|
    VALUE val = rb_hash_aref(param, sym);
 | 
						|
    if (FIXNUM_P(val)) {
 | 
						|
	*dst = FIX2INT(val);
 | 
						|
	return TRUE;
 | 
						|
    }
 | 
						|
    else if (!NIL_P(val)) {
 | 
						|
	rb_raise(rb_eTypeError, "invalid %+"PRIsVALUE" Fixnum: %+"PRIsVALUE,
 | 
						|
		 sym, val);
 | 
						|
    }
 | 
						|
    return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
static const struct rb_iseq_param_keyword *
 | 
						|
iseq_build_kw(rb_iseq_t *iseq, VALUE params, VALUE keywords)
 | 
						|
{
 | 
						|
    int i, j;
 | 
						|
    int len = RARRAY_LENINT(keywords);
 | 
						|
    int default_len;
 | 
						|
    VALUE key, sym, default_val;
 | 
						|
    VALUE *dvs;
 | 
						|
    ID *ids;
 | 
						|
    struct rb_iseq_param_keyword *keyword = ZALLOC(struct rb_iseq_param_keyword);
 | 
						|
 | 
						|
    iseq->body->param.flags.has_kw = TRUE;
 | 
						|
 | 
						|
    keyword->num = len;
 | 
						|
#define SYM(s) ID2SYM(rb_intern(#s))
 | 
						|
    (void)int_param(&keyword->bits_start, params, SYM(kwbits));
 | 
						|
    i = keyword->bits_start - keyword->num;
 | 
						|
    ids = (ID *)&iseq->body->local_table[i];
 | 
						|
#undef SYM
 | 
						|
 | 
						|
    /* required args */
 | 
						|
    for (i = 0; i < len; i++) {
 | 
						|
	VALUE val = RARRAY_AREF(keywords, i);
 | 
						|
 | 
						|
	if (!SYMBOL_P(val)) {
 | 
						|
	    goto default_values;
 | 
						|
	}
 | 
						|
	ids[i] = SYM2ID(val);
 | 
						|
	keyword->required_num++;
 | 
						|
    }
 | 
						|
 | 
						|
  default_values: /* note: we intentionally preserve `i' from previous loop */
 | 
						|
    default_len = len - i;
 | 
						|
    if (default_len == 0) {
 | 
						|
	keyword->table = ids;
 | 
						|
	return keyword;
 | 
						|
    }
 | 
						|
 | 
						|
    dvs = ALLOC_N(VALUE, (unsigned int)default_len);
 | 
						|
 | 
						|
    for (j = 0; i < len; i++, j++) {
 | 
						|
	key = RARRAY_AREF(keywords, i);
 | 
						|
	CHECK_ARRAY(key);
 | 
						|
 | 
						|
	switch (RARRAY_LEN(key)) {
 | 
						|
	  case 1:
 | 
						|
	    sym = RARRAY_AREF(key, 0);
 | 
						|
	    default_val = Qundef;
 | 
						|
	    break;
 | 
						|
	  case 2:
 | 
						|
	    sym = RARRAY_AREF(key, 0);
 | 
						|
	    default_val = RARRAY_AREF(key, 1);
 | 
						|
	    break;
 | 
						|
	  default:
 | 
						|
	    rb_raise(rb_eTypeError, "keyword default has unsupported len %+"PRIsVALUE, key);
 | 
						|
	}
 | 
						|
	ids[i] = SYM2ID(sym);
 | 
						|
	dvs[j] = default_val;
 | 
						|
    }
 | 
						|
 | 
						|
    keyword->table = ids;
 | 
						|
    keyword->default_values = dvs;
 | 
						|
 | 
						|
    return keyword;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
rb_iseq_build_from_ary(rb_iseq_t *iseq, VALUE misc, VALUE locals, VALUE params,
 | 
						|
			 VALUE exception, VALUE body)
 | 
						|
{
 | 
						|
#define SYM(s) ID2SYM(rb_intern(#s))
 | 
						|
    int i, len;
 | 
						|
    unsigned int arg_size, local_size, stack_max;
 | 
						|
    ID *tbl;
 | 
						|
    struct st_table *labels_table = st_init_numtable();
 | 
						|
    VALUE labels_wrapper = Data_Wrap_Struct(0, 0, st_free_table, labels_table);
 | 
						|
    VALUE arg_opt_labels = rb_hash_aref(params, SYM(opt));
 | 
						|
    VALUE keywords = rb_hash_aref(params, SYM(keyword));
 | 
						|
    VALUE sym_arg_rest = ID2SYM(rb_intern("#arg_rest"));
 | 
						|
    DECL_ANCHOR(anchor);
 | 
						|
    INIT_ANCHOR(anchor);
 | 
						|
 | 
						|
    len = RARRAY_LENINT(locals);
 | 
						|
    iseq->body->local_table_size = len;
 | 
						|
    iseq->body->local_table = tbl = len > 0 ? (ID *)ALLOC_N(ID, iseq->body->local_table_size) : NULL;
 | 
						|
 | 
						|
    for (i = 0; i < len; i++) {
 | 
						|
	VALUE lv = RARRAY_AREF(locals, i);
 | 
						|
 | 
						|
	if (sym_arg_rest == lv) {
 | 
						|
	    tbl[i] = 0;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    tbl[i] = FIXNUM_P(lv) ? (ID)FIX2LONG(lv) : SYM2ID(CHECK_SYMBOL(lv));
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
#define INT_PARAM(F) int_param(&iseq->body->param.F, params, SYM(F))
 | 
						|
    if (INT_PARAM(lead_num)) {
 | 
						|
	iseq->body->param.flags.has_lead = TRUE;
 | 
						|
    }
 | 
						|
    if (INT_PARAM(post_num)) iseq->body->param.flags.has_post = TRUE;
 | 
						|
    if (INT_PARAM(post_start)) iseq->body->param.flags.has_post = TRUE;
 | 
						|
    if (INT_PARAM(rest_start)) iseq->body->param.flags.has_rest = TRUE;
 | 
						|
    if (INT_PARAM(block_start)) iseq->body->param.flags.has_block = TRUE;
 | 
						|
#undef INT_PARAM
 | 
						|
    {
 | 
						|
#define INT_PARAM(F) F = (int_param(&x, misc, SYM(F)) ? (unsigned int)x : 0)
 | 
						|
	int x;
 | 
						|
	INT_PARAM(arg_size);
 | 
						|
	INT_PARAM(local_size);
 | 
						|
	INT_PARAM(stack_max);
 | 
						|
#undef INT_PARAM
 | 
						|
    }
 | 
						|
 | 
						|
    if (RB_TYPE_P(arg_opt_labels, T_ARRAY)) {
 | 
						|
	len = RARRAY_LENINT(arg_opt_labels);
 | 
						|
	iseq->body->param.flags.has_opt = !!(len - 1 >= 0);
 | 
						|
 | 
						|
	if (iseq->body->param.flags.has_opt) {
 | 
						|
	    VALUE *opt_table = ALLOC_N(VALUE, len);
 | 
						|
 | 
						|
	    for (i = 0; i < len; i++) {
 | 
						|
		VALUE ent = RARRAY_AREF(arg_opt_labels, i);
 | 
						|
		LABEL *label = register_label(iseq, labels_table, ent);
 | 
						|
		opt_table[i] = (VALUE)label;
 | 
						|
	    }
 | 
						|
 | 
						|
	    iseq->body->param.opt_num = len - 1;
 | 
						|
	    iseq->body->param.opt_table = opt_table;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    else if (!NIL_P(arg_opt_labels)) {
 | 
						|
	rb_raise(rb_eTypeError, ":opt param is not an array: %+"PRIsVALUE,
 | 
						|
		 arg_opt_labels);
 | 
						|
    }
 | 
						|
 | 
						|
    if (RB_TYPE_P(keywords, T_ARRAY)) {
 | 
						|
	iseq->body->param.keyword = iseq_build_kw(iseq, params, keywords);
 | 
						|
    }
 | 
						|
    else if (!NIL_P(keywords)) {
 | 
						|
	rb_raise(rb_eTypeError, ":keywords param is not an array: %+"PRIsVALUE,
 | 
						|
		 keywords);
 | 
						|
    }
 | 
						|
 | 
						|
    if (Qtrue == rb_hash_aref(params, SYM(ambiguous_param0))) {
 | 
						|
	iseq->body->param.flags.ambiguous_param0 = TRUE;
 | 
						|
    }
 | 
						|
 | 
						|
    if (int_param(&i, params, SYM(kwrest))) {
 | 
						|
	struct rb_iseq_param_keyword *keyword = (struct rb_iseq_param_keyword *)iseq->body->param.keyword;
 | 
						|
	if (keyword == NULL) {
 | 
						|
	    iseq->body->param.keyword = keyword = ZALLOC(struct rb_iseq_param_keyword);
 | 
						|
	}
 | 
						|
	keyword->rest_start = i;
 | 
						|
	iseq->body->param.flags.has_kwrest = TRUE;
 | 
						|
    }
 | 
						|
#undef SYM
 | 
						|
    iseq_calc_param_size(iseq);
 | 
						|
 | 
						|
    /* exception */
 | 
						|
    iseq_build_from_ary_exception(iseq, labels_table, exception);
 | 
						|
 | 
						|
    /* body */
 | 
						|
    iseq_build_from_ary_body(iseq, anchor, body, labels_wrapper);
 | 
						|
 | 
						|
    iseq->body->param.size = arg_size;
 | 
						|
    iseq->body->local_table_size = local_size;
 | 
						|
    iseq->body->stack_max = stack_max;
 | 
						|
}
 | 
						|
 | 
						|
/* for parser */
 | 
						|
 | 
						|
int
 | 
						|
rb_dvar_defined(ID id, const struct rb_block *base_block)
 | 
						|
{
 | 
						|
    const rb_iseq_t *iseq;
 | 
						|
 | 
						|
    if (base_block && (iseq = vm_block_iseq(base_block)) != NULL) {
 | 
						|
	while (iseq->body->type == ISEQ_TYPE_BLOCK ||
 | 
						|
	       iseq->body->type == ISEQ_TYPE_RESCUE ||
 | 
						|
	       iseq->body->type == ISEQ_TYPE_ENSURE ||
 | 
						|
	       iseq->body->type == ISEQ_TYPE_EVAL ||
 | 
						|
	       iseq->body->type == ISEQ_TYPE_MAIN
 | 
						|
	       ) {
 | 
						|
	    unsigned int i;
 | 
						|
 | 
						|
	    for (i = 0; i < iseq->body->local_table_size; i++) {
 | 
						|
		if (iseq->body->local_table[i] == id) {
 | 
						|
		    return 1;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	    iseq = iseq->body->parent_iseq;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
rb_local_defined(ID id, const struct rb_block *base_block)
 | 
						|
{
 | 
						|
    const rb_iseq_t *iseq;
 | 
						|
 | 
						|
    if (base_block && (iseq = vm_block_iseq(base_block)) != NULL) {
 | 
						|
	unsigned int i;
 | 
						|
	iseq = iseq->body->local_iseq;
 | 
						|
 | 
						|
	for (i=0; i<iseq->body->local_table_size; i++) {
 | 
						|
	    if (iseq->body->local_table[i] == id) {
 | 
						|
		return 1;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
caller_location(VALUE *path, VALUE *realpath)
 | 
						|
{
 | 
						|
    const rb_execution_context_t *ec = GET_EC();
 | 
						|
    const rb_control_frame_t *const cfp =
 | 
						|
        rb_vm_get_ruby_level_next_cfp(ec, ec->cfp);
 | 
						|
 | 
						|
    if (cfp) {
 | 
						|
	int line = rb_vm_get_sourceline(cfp);
 | 
						|
	*path = rb_iseq_path(cfp->iseq);
 | 
						|
	*realpath = rb_iseq_realpath(cfp->iseq);
 | 
						|
	return line;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	*path = rb_fstring_cstr("<compiled>");
 | 
						|
	*realpath = *path;
 | 
						|
	return 1;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
typedef struct {
 | 
						|
    VALUE arg;
 | 
						|
    rb_insn_func_t func;
 | 
						|
    int line;
 | 
						|
} accessor_args;
 | 
						|
 | 
						|
static const rb_iseq_t *
 | 
						|
method_for_self(VALUE name, VALUE arg, rb_insn_func_t func,
 | 
						|
		VALUE (*build)(rb_iseq_t *, LINK_ANCHOR *const, VALUE))
 | 
						|
{
 | 
						|
    VALUE path, realpath;
 | 
						|
    accessor_args acc;
 | 
						|
 | 
						|
    acc.arg = arg;
 | 
						|
    acc.func = func;
 | 
						|
    acc.line = caller_location(&path, &realpath);
 | 
						|
    return rb_iseq_new_ifunc(IFUNC_NEW(build, (VALUE)&acc, 0),
 | 
						|
			     rb_sym2str(name), path, realpath,
 | 
						|
			     INT2FIX(acc.line), 0, ISEQ_TYPE_METHOD, 0);
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
for_self_aref(rb_iseq_t *iseq, LINK_ANCHOR *const ret, VALUE a)
 | 
						|
{
 | 
						|
    const accessor_args *const args = (void *)a;
 | 
						|
    const int line = args->line;
 | 
						|
 | 
						|
    iseq_set_local_table(iseq, 0);
 | 
						|
    iseq->body->param.lead_num = 0;
 | 
						|
    iseq->body->param.size = 0;
 | 
						|
 | 
						|
    ADD_INSN1(ret, line, putobject, args->arg);
 | 
						|
    ADD_INSN1(ret, line, opt_call_c_function, (VALUE)args->func);
 | 
						|
    return Qnil;
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
for_self_aset(rb_iseq_t *iseq, LINK_ANCHOR *const ret, VALUE a)
 | 
						|
{
 | 
						|
    const accessor_args *const args = (void *)a;
 | 
						|
    const int line = args->line;
 | 
						|
    static const ID vars[] = {1, idUScore};
 | 
						|
 | 
						|
    iseq_set_local_table(iseq, vars);
 | 
						|
    iseq->body->param.lead_num = 1;
 | 
						|
    iseq->body->param.size = 1;
 | 
						|
 | 
						|
    ADD_GETLOCAL(ret, line, numberof(vars)-1, 0);
 | 
						|
    ADD_INSN1(ret, line, putobject, args->arg);
 | 
						|
    ADD_INSN1(ret, line, opt_call_c_function, (VALUE)args->func);
 | 
						|
    ADD_INSN(ret, line, pop);
 | 
						|
    return Qnil;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * func (index) -> (value)
 | 
						|
 */
 | 
						|
const rb_iseq_t *
 | 
						|
rb_method_for_self_aref(VALUE name, VALUE arg, rb_insn_func_t func)
 | 
						|
{
 | 
						|
    return method_for_self(name, arg, func, for_self_aref);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * func (index, value) -> (index, value)
 | 
						|
 */
 | 
						|
const rb_iseq_t *
 | 
						|
rb_method_for_self_aset(VALUE name, VALUE arg, rb_insn_func_t func)
 | 
						|
{
 | 
						|
    return method_for_self(name, arg, func, for_self_aset);
 | 
						|
}
 | 
						|
 | 
						|
/* ISeq binary format */
 | 
						|
 | 
						|
typedef unsigned int ibf_offset_t;
 | 
						|
#define IBF_OFFSET(ptr) ((ibf_offset_t)(VALUE)(ptr))
 | 
						|
 | 
						|
struct ibf_header {
 | 
						|
    char magic[4]; /* YARB */
 | 
						|
    unsigned int major_version;
 | 
						|
    unsigned int minor_version;
 | 
						|
    unsigned int size;
 | 
						|
    unsigned int extra_size;
 | 
						|
 | 
						|
    unsigned int iseq_list_size;
 | 
						|
    unsigned int id_list_size;
 | 
						|
    unsigned int object_list_size;
 | 
						|
 | 
						|
    ibf_offset_t iseq_list_offset;
 | 
						|
    ibf_offset_t id_list_offset;
 | 
						|
    ibf_offset_t object_list_offset;
 | 
						|
};
 | 
						|
 | 
						|
struct ibf_dump {
 | 
						|
    VALUE str;
 | 
						|
    VALUE iseq_list;      /* [iseq0 offset, ...] */
 | 
						|
    VALUE obj_list;       /* [objs] */
 | 
						|
    st_table *iseq_table; /* iseq -> iseq number */
 | 
						|
    st_table *id_table;   /* id -> id number */
 | 
						|
};
 | 
						|
 | 
						|
rb_iseq_t * iseq_alloc(void);
 | 
						|
 | 
						|
struct ibf_load {
 | 
						|
    const RUBY_ALIGNAS(SIZEOF_VALUE) char *buff;
 | 
						|
    const struct ibf_header *header;
 | 
						|
    ID *id_list;     /* [id0, ...] */
 | 
						|
    VALUE iseq_list; /* [iseq0, ...] */
 | 
						|
    VALUE obj_list;  /* [obj0, ...] */
 | 
						|
    VALUE loader_obj;
 | 
						|
    VALUE str;
 | 
						|
    rb_iseq_t *iseq;
 | 
						|
};
 | 
						|
 | 
						|
static ibf_offset_t
 | 
						|
ibf_dump_pos(struct ibf_dump *dump)
 | 
						|
{
 | 
						|
    return (unsigned int)rb_str_strlen(dump->str);
 | 
						|
}
 | 
						|
 | 
						|
static ibf_offset_t
 | 
						|
ibf_dump_write(struct ibf_dump *dump, const void *buff, unsigned long size)
 | 
						|
{
 | 
						|
    ibf_offset_t pos = ibf_dump_pos(dump);
 | 
						|
    rb_str_cat(dump->str, (const char *)buff, size);
 | 
						|
    /* TODO: overflow check */
 | 
						|
    return pos;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_overwrite(struct ibf_dump *dump, void *buff, unsigned int size, long offset)
 | 
						|
{
 | 
						|
    VALUE str = dump->str;
 | 
						|
    char *ptr = RSTRING_PTR(str);
 | 
						|
    if ((unsigned long)(size + offset) > (unsigned long)RSTRING_LEN(str))
 | 
						|
	rb_bug("ibf_dump_overwrite: overflow");
 | 
						|
    memcpy(ptr + offset, buff, size);
 | 
						|
}
 | 
						|
 | 
						|
static void *
 | 
						|
ibf_load_alloc(const struct ibf_load *load, ibf_offset_t offset, int size)
 | 
						|
{
 | 
						|
    void *buff = ruby_xmalloc(size);
 | 
						|
    memcpy(buff, load->buff + offset, size);
 | 
						|
    return buff;
 | 
						|
}
 | 
						|
 | 
						|
#define IBF_W(b, type, n) (type *)(VALUE)ibf_dump_write(dump, (b), sizeof(type) * (n))
 | 
						|
#define IBF_WV(variable)   ibf_dump_write(dump, &(variable), sizeof(variable))
 | 
						|
#define IBF_WP(b, type, n) ibf_dump_write(dump, (b), sizeof(type) * (n))
 | 
						|
#define IBF_R(val, type, n) (type *)ibf_load_alloc(load, IBF_OFFSET(val), sizeof(type) * (n))
 | 
						|
 | 
						|
static int
 | 
						|
ibf_table_lookup(struct st_table *table, st_data_t key)
 | 
						|
{
 | 
						|
    st_data_t val;
 | 
						|
 | 
						|
    if (st_lookup(table, key, &val)) {
 | 
						|
	return (int)val;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	return -1;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
ibf_table_index(struct st_table *table, st_data_t key)
 | 
						|
{
 | 
						|
    int index = ibf_table_lookup(table, key);
 | 
						|
 | 
						|
    if (index < 0) { /* not found */
 | 
						|
	index = (int)table->num_entries;
 | 
						|
	st_insert(table, key, (st_data_t)index);
 | 
						|
    }
 | 
						|
 | 
						|
    return index;
 | 
						|
}
 | 
						|
 | 
						|
/* dump/load generic */
 | 
						|
 | 
						|
static VALUE ibf_load_object(const struct ibf_load *load, VALUE object_index);
 | 
						|
static rb_iseq_t *ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq);
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_dump_object(struct ibf_dump *dump, VALUE obj)
 | 
						|
{
 | 
						|
    long index = RARRAY_LEN(dump->obj_list);
 | 
						|
    long i;
 | 
						|
    for (i=0; i<index; i++) {
 | 
						|
	if (RARRAY_AREF(dump->obj_list, i) == obj) return (VALUE)i; /* dedup */
 | 
						|
    }
 | 
						|
    rb_ary_push(dump->obj_list, obj);
 | 
						|
    return (VALUE)index;
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_dump_id(struct ibf_dump *dump, ID id)
 | 
						|
{
 | 
						|
    return (VALUE)ibf_table_index(dump->id_table, (st_data_t)id);
 | 
						|
}
 | 
						|
 | 
						|
static ID
 | 
						|
ibf_load_id(const struct ibf_load *load, const ID id_index)
 | 
						|
{
 | 
						|
    ID id;
 | 
						|
 | 
						|
    if (id_index == 0) {
 | 
						|
	id = 0;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	id = load->id_list[(long)id_index];
 | 
						|
 | 
						|
	if (id == 0) {
 | 
						|
	    long *indices = (long *)(load->buff + load->header->id_list_offset);
 | 
						|
	    VALUE str = ibf_load_object(load, indices[id_index]);
 | 
						|
	    id = NIL_P(str) ? 0 : rb_intern_str(str); /* str == nil -> internal junk id */
 | 
						|
	    load->id_list[(long)id_index] = id;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    return id;
 | 
						|
}
 | 
						|
 | 
						|
/* dump/load: code */
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_dump_callinfo(struct ibf_dump *dump, const struct rb_call_info *ci)
 | 
						|
{
 | 
						|
    return (ci->flag & VM_CALL_KWARG) ? Qtrue : Qfalse;
 | 
						|
}
 | 
						|
 | 
						|
static ibf_offset_t ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq);
 | 
						|
 | 
						|
static rb_iseq_t *
 | 
						|
ibf_dump_iseq(struct ibf_dump *dump, const rb_iseq_t *iseq)
 | 
						|
{
 | 
						|
    if (iseq == NULL) {
 | 
						|
	return (rb_iseq_t *)-1;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	int iseq_index = ibf_table_lookup(dump->iseq_table, (st_data_t)iseq);
 | 
						|
	if (iseq_index < 0) {
 | 
						|
	    iseq_index = ibf_table_index(dump->iseq_table, (st_data_t)iseq);
 | 
						|
	    rb_ary_store(dump->iseq_list, iseq_index, LONG2NUM(ibf_dump_iseq_each(dump, rb_iseq_check(iseq))));
 | 
						|
	}
 | 
						|
	return (rb_iseq_t *)(VALUE)iseq_index;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_dump_gentry(struct ibf_dump *dump, const struct rb_global_entry *entry)
 | 
						|
{
 | 
						|
    return (VALUE)ibf_dump_id(dump, entry->id);
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_load_gentry(const struct ibf_load *load, const struct rb_global_entry *entry)
 | 
						|
{
 | 
						|
    ID gid = ibf_load_id(load, (ID)(VALUE)entry);
 | 
						|
    return (VALUE)rb_global_entry(gid);
 | 
						|
}
 | 
						|
 | 
						|
static VALUE *
 | 
						|
ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq)
 | 
						|
{
 | 
						|
    const int iseq_size = iseq->body->iseq_size;
 | 
						|
    int code_index;
 | 
						|
    VALUE *code;
 | 
						|
    const VALUE *orig_code = rb_iseq_original_iseq(iseq);
 | 
						|
 | 
						|
    code = ALLOCA_N(VALUE, iseq_size);
 | 
						|
 | 
						|
    for (code_index=0; code_index<iseq_size;) {
 | 
						|
	const VALUE insn = orig_code[code_index];
 | 
						|
	const char *types = insn_op_types(insn);
 | 
						|
	int op_index;
 | 
						|
 | 
						|
	code[code_index++] = (VALUE)insn;
 | 
						|
 | 
						|
	for (op_index=0; types[op_index]; op_index++, code_index++) {
 | 
						|
	    VALUE op = orig_code[code_index];
 | 
						|
	    switch (types[op_index]) {
 | 
						|
	      case TS_CDHASH:
 | 
						|
	      case TS_VALUE:
 | 
						|
		code[code_index] = ibf_dump_object(dump, op);
 | 
						|
		break;
 | 
						|
	      case TS_ISEQ:
 | 
						|
		code[code_index] = (VALUE)ibf_dump_iseq(dump, (const rb_iseq_t *)op);
 | 
						|
		break;
 | 
						|
	      case TS_IC:
 | 
						|
		{
 | 
						|
		    unsigned int i;
 | 
						|
		    for (i=0; i<iseq->body->is_size; i++) {
 | 
						|
			if (op == (VALUE)&iseq->body->is_entries[i]) {
 | 
						|
			    break;
 | 
						|
			}
 | 
						|
		    }
 | 
						|
		    code[code_index] = i;
 | 
						|
		}
 | 
						|
		break;
 | 
						|
	      case TS_CALLINFO:
 | 
						|
		code[code_index] = ibf_dump_callinfo(dump, (const struct rb_call_info *)op);
 | 
						|
		break;
 | 
						|
	      case TS_CALLCACHE:
 | 
						|
		code[code_index] = 0;
 | 
						|
		break;
 | 
						|
	      case TS_ID:
 | 
						|
		code[code_index] = ibf_dump_id(dump, (ID)op);
 | 
						|
		break;
 | 
						|
	      case TS_GENTRY:
 | 
						|
		code[code_index] = ibf_dump_gentry(dump, (const struct rb_global_entry *)op);
 | 
						|
		break;
 | 
						|
	      case TS_FUNCPTR:
 | 
						|
		rb_raise(rb_eRuntimeError, "TS_FUNCPTR is not supported");
 | 
						|
		break;
 | 
						|
	      default:
 | 
						|
		code[code_index] = op;
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	assert(insn_len(insn) == op_index+1);
 | 
						|
    }
 | 
						|
 | 
						|
    return IBF_W(code, VALUE, iseq_size);
 | 
						|
}
 | 
						|
 | 
						|
static VALUE *
 | 
						|
ibf_load_code(const struct ibf_load *load, const rb_iseq_t *iseq, const struct rb_iseq_constant_body *body)
 | 
						|
{
 | 
						|
    const int iseq_size = body->iseq_size;
 | 
						|
    int code_index;
 | 
						|
    VALUE *code = IBF_R(body->iseq_encoded, VALUE, iseq_size);
 | 
						|
 | 
						|
    struct rb_call_info *ci_entries = iseq->body->ci_entries;
 | 
						|
    struct rb_call_info_with_kwarg *ci_kw_entries = (struct rb_call_info_with_kwarg *)&iseq->body->ci_entries[iseq->body->ci_size];
 | 
						|
    struct rb_call_cache *cc_entries = iseq->body->cc_entries;
 | 
						|
    union iseq_inline_storage_entry *is_entries = iseq->body->is_entries;
 | 
						|
 | 
						|
    for (code_index=0; code_index<iseq_size;) {
 | 
						|
	const VALUE insn = code[code_index++];
 | 
						|
	const char *types = insn_op_types(insn);
 | 
						|
	int op_index;
 | 
						|
 | 
						|
	for (op_index=0; types[op_index]; op_index++, code_index++) {
 | 
						|
	    VALUE op = code[code_index];
 | 
						|
 | 
						|
	    switch (types[op_index]) {
 | 
						|
	      case TS_CDHASH:
 | 
						|
	      case TS_VALUE:
 | 
						|
		code[code_index] = ibf_load_object(load, op);
 | 
						|
		break;
 | 
						|
	      case TS_ISEQ:
 | 
						|
		code[code_index] = (VALUE)ibf_load_iseq(load, (const rb_iseq_t *)op);
 | 
						|
		break;
 | 
						|
	      case TS_IC:
 | 
						|
		code[code_index] = (VALUE)&is_entries[(int)op];
 | 
						|
		break;
 | 
						|
	      case TS_CALLINFO:
 | 
						|
		code[code_index] = op ? (VALUE)ci_kw_entries++ : (VALUE)ci_entries++; /* op is Qtrue (kw) or Qfalse (!kw) */
 | 
						|
		break;
 | 
						|
	      case TS_CALLCACHE:
 | 
						|
		code[code_index] = (VALUE)cc_entries++;
 | 
						|
		break;
 | 
						|
	      case TS_ID:
 | 
						|
		code[code_index] = ibf_load_id(load, (ID)op);
 | 
						|
		break;
 | 
						|
	      case TS_GENTRY:
 | 
						|
		code[code_index] = ibf_load_gentry(load, (const struct rb_global_entry *)op);
 | 
						|
		break;
 | 
						|
	      case TS_FUNCPTR:
 | 
						|
		rb_raise(rb_eRuntimeError, "TS_FUNCPTR is not supported");
 | 
						|
		break;
 | 
						|
	      default:
 | 
						|
		/* code[code_index] = op; */
 | 
						|
		break;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	if (insn_len(insn) != op_index+1) {
 | 
						|
	    rb_raise(rb_eRuntimeError, "operand size mismatch");
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    return code;
 | 
						|
}
 | 
						|
 | 
						|
static VALUE *
 | 
						|
ibf_dump_param_opt_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
 | 
						|
{
 | 
						|
    int opt_num = iseq->body->param.opt_num;
 | 
						|
 | 
						|
    if (opt_num > 0) {
 | 
						|
	return IBF_W(iseq->body->param.opt_table, VALUE, opt_num + 1);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	return NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static VALUE *
 | 
						|
ibf_load_param_opt_table(const struct ibf_load *load, const struct rb_iseq_constant_body *body)
 | 
						|
{
 | 
						|
    int opt_num = body->param.opt_num;
 | 
						|
 | 
						|
    if (opt_num > 0) {
 | 
						|
	ibf_offset_t offset = IBF_OFFSET(body->param.opt_table);
 | 
						|
	VALUE *table = ALLOC_N(VALUE, opt_num+1);
 | 
						|
	MEMCPY(table, load->buff + offset, VALUE, opt_num+1);
 | 
						|
	return table;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	return NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static struct rb_iseq_param_keyword *
 | 
						|
ibf_dump_param_keyword(struct ibf_dump *dump, const rb_iseq_t *iseq)
 | 
						|
{
 | 
						|
    const struct rb_iseq_param_keyword *kw = iseq->body->param.keyword;
 | 
						|
 | 
						|
    if (kw) {
 | 
						|
	struct rb_iseq_param_keyword dump_kw = *kw;
 | 
						|
	int dv_num = kw->num - kw->required_num;
 | 
						|
	ID *ids = kw->num > 0 ? ALLOCA_N(ID, kw->num) : NULL;
 | 
						|
	VALUE *dvs = dv_num > 0 ? ALLOCA_N(VALUE, dv_num) : NULL;
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i=0; i<kw->num; i++) ids[i] = (ID)ibf_dump_id(dump, kw->table[i]);
 | 
						|
	for (i=0; i<dv_num; i++) dvs[i] = (VALUE)ibf_dump_object(dump, kw->default_values[i]);
 | 
						|
 | 
						|
	dump_kw.table = IBF_W(ids, ID, kw->num);
 | 
						|
	dump_kw.default_values = IBF_W(dvs, VALUE, dv_num);
 | 
						|
	return IBF_W(&dump_kw, struct rb_iseq_param_keyword, 1);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	return NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static const struct rb_iseq_param_keyword *
 | 
						|
ibf_load_param_keyword(const struct ibf_load *load, const struct rb_iseq_constant_body *body)
 | 
						|
{
 | 
						|
    if (body->param.keyword) {
 | 
						|
	struct rb_iseq_param_keyword *kw = IBF_R(body->param.keyword, struct rb_iseq_param_keyword, 1);
 | 
						|
	ID *ids = IBF_R(kw->table, ID, kw->num);
 | 
						|
	int dv_num = kw->num - kw->required_num;
 | 
						|
	VALUE *dvs = IBF_R(kw->default_values, VALUE, dv_num);
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i=0; i<kw->num; i++) {
 | 
						|
	    ids[i] = ibf_load_id(load, ids[i]);
 | 
						|
	}
 | 
						|
	for (i=0; i<dv_num; i++) {
 | 
						|
	    dvs[i] = ibf_load_object(load, dvs[i]);
 | 
						|
	}
 | 
						|
 | 
						|
	kw->table = ids;
 | 
						|
	kw->default_values = dvs;
 | 
						|
	return kw;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	return NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static struct iseq_insn_info_entry *
 | 
						|
ibf_dump_insns_info_body(struct ibf_dump *dump, const rb_iseq_t *iseq)
 | 
						|
{
 | 
						|
    return IBF_W(iseq->body->insns_info.body, struct iseq_insn_info_entry, iseq->body->insns_info.size);
 | 
						|
}
 | 
						|
 | 
						|
static struct iseq_insn_info_entry *
 | 
						|
ibf_load_insns_info_body(const struct ibf_load *load, const struct rb_iseq_constant_body *body)
 | 
						|
{
 | 
						|
    return IBF_R(body->insns_info.body, struct iseq_insn_info_entry, body->insns_info.size);
 | 
						|
}
 | 
						|
 | 
						|
static unsigned int *
 | 
						|
ibf_dump_insns_info_positions(struct ibf_dump *dump, const rb_iseq_t *iseq)
 | 
						|
{
 | 
						|
    return IBF_W(iseq->body->insns_info.positions, unsigned int, iseq->body->insns_info.size);
 | 
						|
}
 | 
						|
 | 
						|
static unsigned int *
 | 
						|
ibf_load_insns_info_positions(const struct ibf_load *load, const struct rb_iseq_constant_body *body)
 | 
						|
{
 | 
						|
    return IBF_R(body->insns_info.positions, unsigned int, body->insns_info.size);
 | 
						|
}
 | 
						|
 | 
						|
static ID *
 | 
						|
ibf_dump_local_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
 | 
						|
{
 | 
						|
    const int size = iseq->body->local_table_size;
 | 
						|
    ID *table = ALLOCA_N(ID, size);
 | 
						|
    int i;
 | 
						|
 | 
						|
    for (i=0; i<size; i++) {
 | 
						|
	table[i] = ibf_dump_id(dump, iseq->body->local_table[i]);
 | 
						|
    }
 | 
						|
 | 
						|
    return IBF_W(table, ID, size);
 | 
						|
}
 | 
						|
 | 
						|
static ID *
 | 
						|
ibf_load_local_table(const struct ibf_load *load, const struct rb_iseq_constant_body *body)
 | 
						|
{
 | 
						|
    const int size = body->local_table_size;
 | 
						|
 | 
						|
    if (size > 0) {
 | 
						|
	ID *table = IBF_R(body->local_table, ID, size);
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i=0; i<size; i++) {
 | 
						|
	    table[i] = ibf_load_id(load, table[i]);
 | 
						|
	}
 | 
						|
	return table;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	return NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static struct iseq_catch_table *
 | 
						|
ibf_dump_catch_table(struct ibf_dump *dump, const rb_iseq_t *iseq)
 | 
						|
{
 | 
						|
    const struct iseq_catch_table *table = iseq->body->catch_table;
 | 
						|
 | 
						|
    if (table) {
 | 
						|
	int byte_size = iseq_catch_table_bytes(iseq->body->catch_table->size);
 | 
						|
	struct iseq_catch_table *dump_table = (struct iseq_catch_table *)ALLOCA_N(char, byte_size);
 | 
						|
	unsigned int i;
 | 
						|
	dump_table->size = table->size;
 | 
						|
	for (i=0; i<table->size; i++) {
 | 
						|
	    dump_table->entries[i] = table->entries[i];
 | 
						|
	    dump_table->entries[i].iseq = ibf_dump_iseq(dump, table->entries[i].iseq);
 | 
						|
	}
 | 
						|
	return (struct iseq_catch_table *)(VALUE)ibf_dump_write(dump, dump_table, byte_size);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	return NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static struct iseq_catch_table *
 | 
						|
ibf_load_catch_table(const struct ibf_load *load, const struct rb_iseq_constant_body *body)
 | 
						|
{
 | 
						|
    if (body->catch_table) {
 | 
						|
	struct iseq_catch_table *table;
 | 
						|
	unsigned int i;
 | 
						|
	unsigned int size;
 | 
						|
	size = *(unsigned int *)(load->buff + IBF_OFFSET(body->catch_table));
 | 
						|
	table = ibf_load_alloc(load, IBF_OFFSET(body->catch_table), iseq_catch_table_bytes(size));
 | 
						|
	for (i=0; i<size; i++) {
 | 
						|
	    table->entries[i].iseq = ibf_load_iseq(load, table->entries[i].iseq);
 | 
						|
	}
 | 
						|
	return table;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	return NULL;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static struct rb_call_info *
 | 
						|
ibf_dump_ci_entries(struct ibf_dump *dump, const rb_iseq_t *iseq)
 | 
						|
{
 | 
						|
    const unsigned int ci_size = iseq->body->ci_size;
 | 
						|
    const unsigned int ci_kw_size = iseq->body->ci_kw_size;
 | 
						|
    const struct rb_call_info *ci_entries = iseq->body->ci_entries;
 | 
						|
    struct rb_call_info *dump_ci_entries;
 | 
						|
    struct rb_call_info_with_kwarg *dump_ci_kw_entries;
 | 
						|
    int byte_size = ci_size * sizeof(struct rb_call_info) +
 | 
						|
                    ci_kw_size * sizeof(struct rb_call_info_with_kwarg);
 | 
						|
    unsigned int i;
 | 
						|
 | 
						|
    dump_ci_entries = (struct rb_call_info *)ALLOCA_N(char, byte_size);
 | 
						|
    dump_ci_kw_entries = (struct rb_call_info_with_kwarg *)&dump_ci_entries[ci_size];
 | 
						|
    memcpy(dump_ci_entries, ci_entries, byte_size);
 | 
						|
 | 
						|
    for (i=0; i<ci_size; i++) { /* conver ID for each ci */
 | 
						|
	dump_ci_entries[i].mid = ibf_dump_id(dump, dump_ci_entries[i].mid);
 | 
						|
    }
 | 
						|
    for (i=0; i<ci_kw_size; i++) {
 | 
						|
	const struct rb_call_info_kw_arg *kw_arg = dump_ci_kw_entries[i].kw_arg;
 | 
						|
	int j;
 | 
						|
	VALUE *keywords = ALLOCA_N(VALUE, kw_arg->keyword_len);
 | 
						|
	for (j=0; j<kw_arg->keyword_len; j++) {
 | 
						|
	    keywords[j] = (VALUE)ibf_dump_object(dump, kw_arg->keywords[j]); /* kw_arg->keywords[n] is Symbol */
 | 
						|
	}
 | 
						|
	dump_ci_kw_entries[i].kw_arg = (struct rb_call_info_kw_arg *)(VALUE)ibf_dump_write(dump, &kw_arg->keyword_len, sizeof(int));
 | 
						|
	ibf_dump_write(dump, keywords, sizeof(VALUE) * kw_arg->keyword_len);
 | 
						|
 | 
						|
	dump_ci_kw_entries[i].ci.mid = ibf_dump_id(dump, dump_ci_kw_entries[i].ci.mid);
 | 
						|
    }
 | 
						|
    return (struct rb_call_info *)(VALUE)ibf_dump_write(dump, dump_ci_entries, byte_size);
 | 
						|
}
 | 
						|
 | 
						|
static struct rb_call_info *
 | 
						|
ibf_load_ci_entries(const struct ibf_load *load, const struct rb_iseq_constant_body *body)
 | 
						|
{
 | 
						|
    unsigned int i;
 | 
						|
    const unsigned int ci_size = body->ci_size;
 | 
						|
    const unsigned int ci_kw_size = body->ci_kw_size;
 | 
						|
    struct rb_call_info *ci_entries = ibf_load_alloc(load, IBF_OFFSET(body->ci_entries),
 | 
						|
						     sizeof(struct rb_call_info) * body->ci_size +
 | 
						|
						     sizeof(struct rb_call_info_with_kwarg) * body->ci_kw_size);
 | 
						|
    struct rb_call_info_with_kwarg *ci_kw_entries = (struct rb_call_info_with_kwarg *)&ci_entries[ci_size];
 | 
						|
 | 
						|
    for (i=0; i<ci_size; i++) {
 | 
						|
	ci_entries[i].mid = ibf_load_id(load, ci_entries[i].mid);
 | 
						|
    }
 | 
						|
    for (i=0; i<ci_kw_size; i++) {
 | 
						|
	int j;
 | 
						|
	ibf_offset_t kw_arg_offset = IBF_OFFSET(ci_kw_entries[i].kw_arg);
 | 
						|
	const int keyword_len = *(int *)(load->buff + kw_arg_offset);
 | 
						|
	const VALUE *keywords = (VALUE *)(load->buff + kw_arg_offset + sizeof(int));
 | 
						|
	struct rb_call_info_kw_arg *kw_arg = ruby_xmalloc(sizeof(struct rb_call_info_kw_arg) + sizeof(VALUE) * (keyword_len - 1));
 | 
						|
	kw_arg->keyword_len = keyword_len;
 | 
						|
	for (j=0; j<kw_arg->keyword_len; j++) {
 | 
						|
	    kw_arg->keywords[j] = (VALUE)ibf_load_object(load, keywords[j]);
 | 
						|
	}
 | 
						|
	ci_kw_entries[i].kw_arg = kw_arg;
 | 
						|
	ci_kw_entries[i].ci.mid = ibf_load_id(load, ci_kw_entries[i].ci.mid);
 | 
						|
    }
 | 
						|
 | 
						|
    return ci_entries;
 | 
						|
}
 | 
						|
 | 
						|
static ibf_offset_t
 | 
						|
ibf_dump_iseq_each(struct ibf_dump *dump, const rb_iseq_t *iseq)
 | 
						|
{
 | 
						|
    struct rb_iseq_constant_body dump_body;
 | 
						|
    dump_body = *iseq->body;
 | 
						|
 | 
						|
    dump_body.location.pathobj = ibf_dump_object(dump, dump_body.location.pathobj); /* TODO: freeze */
 | 
						|
    dump_body.location.base_label = ibf_dump_object(dump, dump_body.location.base_label);
 | 
						|
    dump_body.location.label = ibf_dump_object(dump, dump_body.location.label);
 | 
						|
 | 
						|
    dump_body.iseq_encoded =         ibf_dump_code(dump, iseq);
 | 
						|
    dump_body.param.opt_table =      ibf_dump_param_opt_table(dump, iseq);
 | 
						|
    dump_body.param.keyword =        ibf_dump_param_keyword(dump, iseq);
 | 
						|
    dump_body.insns_info.body =      ibf_dump_insns_info_body(dump, iseq);
 | 
						|
#if VM_INSN_INFO_TABLE_IMPL == 2
 | 
						|
    rb_iseq_insns_info_decode_positions(iseq);
 | 
						|
#endif
 | 
						|
    dump_body.insns_info.positions = ibf_dump_insns_info_positions(dump, iseq);
 | 
						|
#if VM_INSN_INFO_TABLE_IMPL == 2
 | 
						|
    rb_iseq_insns_info_encode_positions(iseq);
 | 
						|
#endif
 | 
						|
    dump_body.local_table =          ibf_dump_local_table(dump, iseq);
 | 
						|
    dump_body.catch_table =          ibf_dump_catch_table(dump, iseq);
 | 
						|
    dump_body.parent_iseq =          ibf_dump_iseq(dump, iseq->body->parent_iseq);
 | 
						|
    dump_body.local_iseq =           ibf_dump_iseq(dump, iseq->body->local_iseq);
 | 
						|
    dump_body.is_entries =           NULL;
 | 
						|
    dump_body.ci_entries =           ibf_dump_ci_entries(dump, iseq);
 | 
						|
    dump_body.cc_entries =           NULL;
 | 
						|
    dump_body.mark_ary =             ISEQ_FLIP_CNT(iseq);
 | 
						|
 | 
						|
    return ibf_dump_write(dump, &dump_body, sizeof(dump_body));
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_load_location_str(const struct ibf_load *load, VALUE str_index)
 | 
						|
{
 | 
						|
    VALUE str = ibf_load_object(load, str_index);
 | 
						|
    if (str != Qnil) {
 | 
						|
	str = rb_fstring(str);
 | 
						|
    }
 | 
						|
    return str;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_load_iseq_each(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t offset)
 | 
						|
{
 | 
						|
    struct rb_iseq_constant_body *load_body = iseq->body = ZALLOC(struct rb_iseq_constant_body);
 | 
						|
    const struct rb_iseq_constant_body *body = (struct rb_iseq_constant_body *)(load->buff + offset);
 | 
						|
 | 
						|
    /* memcpy(load_body, load->buff + offset, sizeof(*load_body)); */
 | 
						|
    load_body->type = body->type;
 | 
						|
    load_body->stack_max = body->stack_max;
 | 
						|
    load_body->iseq_size = body->iseq_size;
 | 
						|
    load_body->param = body->param;
 | 
						|
    load_body->local_table_size = body->local_table_size;
 | 
						|
    load_body->is_size = body->is_size;
 | 
						|
    load_body->ci_size = body->ci_size;
 | 
						|
    load_body->ci_kw_size = body->ci_kw_size;
 | 
						|
    load_body->insns_info.size = body->insns_info.size;
 | 
						|
 | 
						|
    RB_OBJ_WRITE(iseq, &load_body->mark_ary, iseq_mark_ary_create((int)body->mark_ary));
 | 
						|
 | 
						|
    {
 | 
						|
	VALUE realpath = Qnil, path = ibf_load_object(load, body->location.pathobj);
 | 
						|
	if (RB_TYPE_P(path, T_STRING)) {
 | 
						|
	    realpath = path = rb_fstring(path);
 | 
						|
	}
 | 
						|
	else if (RB_TYPE_P(path, T_ARRAY)) {
 | 
						|
	    VALUE pathobj = path;
 | 
						|
	    if (RARRAY_LEN(pathobj) != 2) {
 | 
						|
		rb_raise(rb_eRuntimeError, "path object size mismatch");
 | 
						|
	    }
 | 
						|
	    path = rb_fstring(RARRAY_AREF(pathobj, 0));
 | 
						|
	    realpath = rb_fstring(RARRAY_AREF(pathobj, 1));
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    rb_raise(rb_eRuntimeError, "unexpected path object");
 | 
						|
	}
 | 
						|
	rb_iseq_pathobj_set(iseq, path, realpath);
 | 
						|
    }
 | 
						|
 | 
						|
    RB_OBJ_WRITE(iseq, &load_body->location.base_label,    ibf_load_location_str(load, body->location.base_label));
 | 
						|
    RB_OBJ_WRITE(iseq, &load_body->location.label,         ibf_load_location_str(load, body->location.label));
 | 
						|
    load_body->location.first_lineno = body->location.first_lineno;
 | 
						|
    load_body->location.code_location = body->location.code_location;
 | 
						|
 | 
						|
    load_body->is_entries           = ZALLOC_N(union iseq_inline_storage_entry, body->is_size);
 | 
						|
    load_body->ci_entries           = ibf_load_ci_entries(load, body);
 | 
						|
    load_body->cc_entries           = ZALLOC_N(struct rb_call_cache, body->ci_size + body->ci_kw_size);
 | 
						|
    load_body->param.opt_table      = ibf_load_param_opt_table(load, body);
 | 
						|
    load_body->param.keyword        = ibf_load_param_keyword(load, body);
 | 
						|
    load_body->insns_info.body      = ibf_load_insns_info_body(load, body);
 | 
						|
    load_body->insns_info.positions = ibf_load_insns_info_positions(load, body);
 | 
						|
#if VM_INSN_INFO_TABLE_IMPL == 2
 | 
						|
    rb_iseq_insns_info_encode_positions(iseq);
 | 
						|
#endif
 | 
						|
    load_body->local_table          = ibf_load_local_table(load, body);
 | 
						|
    load_body->catch_table          = ibf_load_catch_table(load, body);
 | 
						|
    load_body->parent_iseq          = ibf_load_iseq(load, body->parent_iseq);
 | 
						|
    load_body->local_iseq           = ibf_load_iseq(load, body->local_iseq);
 | 
						|
 | 
						|
    load_body->iseq_encoded         = ibf_load_code(load, iseq, body);
 | 
						|
 | 
						|
    rb_iseq_translate_threaded_code(iseq);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_iseq_list(struct ibf_dump *dump, struct ibf_header *header)
 | 
						|
{
 | 
						|
    const long size = RARRAY_LEN(dump->iseq_list);
 | 
						|
    ibf_offset_t *list = ALLOCA_N(ibf_offset_t, size);
 | 
						|
    long i;
 | 
						|
 | 
						|
    for (i=0; i<size; i++) {
 | 
						|
	list[i] = (ibf_offset_t)NUM2LONG(rb_ary_entry(dump->iseq_list, i));
 | 
						|
    }
 | 
						|
 | 
						|
    header->iseq_list_offset = ibf_dump_write(dump, list, sizeof(ibf_offset_t) * size);
 | 
						|
    header->iseq_list_size = (unsigned int)size;
 | 
						|
}
 | 
						|
 | 
						|
struct ibf_dump_id_list_i_arg {
 | 
						|
    struct ibf_dump *dump;
 | 
						|
    long *list;
 | 
						|
    int current_i;
 | 
						|
};
 | 
						|
 | 
						|
static int
 | 
						|
ibf_dump_id_list_i(st_data_t key, st_data_t val, st_data_t ptr)
 | 
						|
{
 | 
						|
    struct ibf_dump_id_list_i_arg *arg = (struct ibf_dump_id_list_i_arg *)ptr;
 | 
						|
    int i = (int)val;
 | 
						|
    ID id = (ID)key;
 | 
						|
    assert(arg->current_i == i);
 | 
						|
    arg->current_i++;
 | 
						|
 | 
						|
    if (rb_id2name(id)) {
 | 
						|
	arg->list[i] = (long)ibf_dump_object(arg->dump, rb_id2str(id));
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	arg->list[i] = 0;
 | 
						|
    }
 | 
						|
 | 
						|
    return ST_CONTINUE;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_id_list(struct ibf_dump *dump, struct ibf_header *header)
 | 
						|
{
 | 
						|
    const long size = dump->id_table->num_entries;
 | 
						|
    struct ibf_dump_id_list_i_arg arg;
 | 
						|
    arg.list = ALLOCA_N(long, size);
 | 
						|
    arg.dump = dump;
 | 
						|
    arg.current_i = 0;
 | 
						|
 | 
						|
    st_foreach(dump->id_table, ibf_dump_id_list_i, (st_data_t)&arg);
 | 
						|
 | 
						|
    header->id_list_offset = ibf_dump_write(dump, arg.list, sizeof(long) * size);
 | 
						|
    header->id_list_size = (unsigned int)size;
 | 
						|
}
 | 
						|
 | 
						|
#define IBF_OBJECT_INTERNAL FL_PROMOTED0
 | 
						|
 | 
						|
/*
 | 
						|
 * Binary format
 | 
						|
 * - ibf_object_header
 | 
						|
 * - ibf_object_xxx (xxx is type)
 | 
						|
 */
 | 
						|
 | 
						|
struct ibf_object_header {
 | 
						|
    unsigned int type: 5;
 | 
						|
    unsigned int special_const: 1;
 | 
						|
    unsigned int frozen: 1;
 | 
						|
    unsigned int internal: 1;
 | 
						|
};
 | 
						|
 | 
						|
enum ibf_object_class_index {
 | 
						|
    IBF_OBJECT_CLASS_OBJECT,
 | 
						|
    IBF_OBJECT_CLASS_ARRAY,
 | 
						|
    IBF_OBJECT_CLASS_STANDARD_ERROR
 | 
						|
};
 | 
						|
 | 
						|
struct ibf_object_string {
 | 
						|
    long encindex;
 | 
						|
    long len;
 | 
						|
    char ptr[FLEX_ARY_LEN];
 | 
						|
};
 | 
						|
 | 
						|
struct ibf_object_regexp {
 | 
						|
    long srcstr;
 | 
						|
    char option;
 | 
						|
};
 | 
						|
 | 
						|
struct ibf_object_array {
 | 
						|
    long len;
 | 
						|
    long ary[FLEX_ARY_LEN];
 | 
						|
};
 | 
						|
 | 
						|
struct ibf_object_hash {
 | 
						|
    long len;
 | 
						|
    long keyval[FLEX_ARY_LEN];
 | 
						|
};
 | 
						|
 | 
						|
struct ibf_object_struct_range {
 | 
						|
    long class_index;
 | 
						|
    long len;
 | 
						|
    long beg;
 | 
						|
    long end;
 | 
						|
    int excl;
 | 
						|
};
 | 
						|
 | 
						|
struct ibf_object_bignum {
 | 
						|
    ssize_t slen;
 | 
						|
    BDIGIT digits[FLEX_ARY_LEN];
 | 
						|
};
 | 
						|
 | 
						|
enum ibf_object_data_type {
 | 
						|
    IBF_OBJECT_DATA_ENCODING
 | 
						|
};
 | 
						|
 | 
						|
struct ibf_object_complex_rational {
 | 
						|
    long a, b;
 | 
						|
};
 | 
						|
 | 
						|
struct ibf_object_symbol {
 | 
						|
    long str;
 | 
						|
};
 | 
						|
 | 
						|
#define IBF_OBJHEADER(offset)     (struct ibf_object_header *)(load->buff + (offset))
 | 
						|
#define IBF_OBJBODY(type, offset) (type *)(load->buff + sizeof(struct ibf_object_header) + (offset))
 | 
						|
 | 
						|
NORETURN(static void ibf_dump_object_unsupported(struct ibf_dump *dump, VALUE obj));
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_object_unsupported(struct ibf_dump *dump, VALUE obj)
 | 
						|
{
 | 
						|
    rb_obj_info_dump(obj);
 | 
						|
    rb_bug("ibf_dump_object_unsupported: unsupported");
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_load_object_unsupported(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
 | 
						|
{
 | 
						|
    rb_bug("unsupported");
 | 
						|
    return Qnil;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_object_class(struct ibf_dump *dump, VALUE obj)
 | 
						|
{
 | 
						|
    enum ibf_object_class_index cindex;
 | 
						|
    if (obj == rb_cObject) {
 | 
						|
	cindex = IBF_OBJECT_CLASS_OBJECT;
 | 
						|
    }
 | 
						|
    else if (obj == rb_cArray) {
 | 
						|
	cindex = IBF_OBJECT_CLASS_ARRAY;
 | 
						|
    }
 | 
						|
    else if (obj == rb_eStandardError) {
 | 
						|
	cindex = IBF_OBJECT_CLASS_STANDARD_ERROR;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	rb_obj_info_dump(obj);
 | 
						|
	rb_p(obj);
 | 
						|
	rb_bug("unsupported class");
 | 
						|
    }
 | 
						|
    ibf_dump_write(dump, &cindex, sizeof(cindex));
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_load_object_class(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
 | 
						|
{
 | 
						|
    enum ibf_object_class_index *cindexp = IBF_OBJBODY(enum ibf_object_class_index, offset);
 | 
						|
    enum ibf_object_class_index cindex = *cindexp;
 | 
						|
 | 
						|
    switch (cindex) {
 | 
						|
      case IBF_OBJECT_CLASS_OBJECT:
 | 
						|
	return rb_cObject;
 | 
						|
      case IBF_OBJECT_CLASS_ARRAY:
 | 
						|
	return rb_cArray;
 | 
						|
      case IBF_OBJECT_CLASS_STANDARD_ERROR:
 | 
						|
	return rb_eStandardError;
 | 
						|
    }
 | 
						|
 | 
						|
    rb_bug("ibf_load_object_class: unknown class (%d)", (int)cindex);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_object_float(struct ibf_dump *dump, VALUE obj)
 | 
						|
{
 | 
						|
    double dbl = RFLOAT_VALUE(obj);
 | 
						|
    ibf_dump_write(dump, &dbl, sizeof(dbl));
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_load_object_float(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
 | 
						|
{
 | 
						|
    double *dblp = IBF_OBJBODY(double, offset);
 | 
						|
    return DBL2NUM(*dblp);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_object_string(struct ibf_dump *dump, VALUE obj)
 | 
						|
{
 | 
						|
    long encindex = (long)rb_enc_get_index(obj);
 | 
						|
    long len = RSTRING_LEN(obj);
 | 
						|
    const char *ptr = RSTRING_PTR(obj);
 | 
						|
 | 
						|
    if (encindex > RUBY_ENCINDEX_BUILTIN_MAX) {
 | 
						|
	rb_encoding *enc = rb_enc_from_index((int)encindex);
 | 
						|
	const char *enc_name = rb_enc_name(enc);
 | 
						|
	encindex = RUBY_ENCINDEX_BUILTIN_MAX + ibf_dump_object(dump, rb_str_new2(enc_name));
 | 
						|
    }
 | 
						|
 | 
						|
    IBF_WV(encindex);
 | 
						|
    IBF_WV(len);
 | 
						|
    IBF_WP(ptr, char, len);
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_load_object_string(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
 | 
						|
{
 | 
						|
    const struct ibf_object_string *string = IBF_OBJBODY(struct ibf_object_string, offset);
 | 
						|
    VALUE str = rb_str_new(string->ptr, string->len);
 | 
						|
    int encindex = (int)string->encindex;
 | 
						|
 | 
						|
    if (encindex > RUBY_ENCINDEX_BUILTIN_MAX) {
 | 
						|
	VALUE enc_name_str = ibf_load_object(load, encindex - RUBY_ENCINDEX_BUILTIN_MAX);
 | 
						|
	encindex = rb_enc_find_index(RSTRING_PTR(enc_name_str));
 | 
						|
    }
 | 
						|
    rb_enc_associate_index(str, encindex);
 | 
						|
 | 
						|
    if (header->internal) rb_obj_hide(str);
 | 
						|
    if (header->frozen)   str = rb_fstring(str);
 | 
						|
 | 
						|
    return str;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_object_regexp(struct ibf_dump *dump, VALUE obj)
 | 
						|
{
 | 
						|
    struct ibf_object_regexp regexp;
 | 
						|
    regexp.srcstr = RREGEXP_SRC(obj);
 | 
						|
    regexp.option = (char)rb_reg_options(obj);
 | 
						|
    regexp.srcstr = (long)ibf_dump_object(dump, regexp.srcstr);
 | 
						|
    IBF_WV(regexp);
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_load_object_regexp(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
 | 
						|
{
 | 
						|
    const struct ibf_object_regexp *regexp = IBF_OBJBODY(struct ibf_object_regexp, offset);
 | 
						|
    VALUE srcstr = ibf_load_object(load, regexp->srcstr);
 | 
						|
    VALUE reg = rb_reg_compile(srcstr, (int)regexp->option, NULL, 0);
 | 
						|
 | 
						|
    if (header->internal) rb_obj_hide(reg);
 | 
						|
    if (header->frozen)   rb_obj_freeze(reg);
 | 
						|
 | 
						|
    return reg;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_object_array(struct ibf_dump *dump, VALUE obj)
 | 
						|
{
 | 
						|
    long i, len = (int)RARRAY_LEN(obj);
 | 
						|
    IBF_WV(len);
 | 
						|
    for (i=0; i<len; i++) {
 | 
						|
	long index = (long)ibf_dump_object(dump, RARRAY_AREF(obj, i));
 | 
						|
	IBF_WV(index);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_load_object_array(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
 | 
						|
{
 | 
						|
    const struct ibf_object_array *array = IBF_OBJBODY(struct ibf_object_array, offset);
 | 
						|
    VALUE ary = rb_ary_new_capa(array->len);
 | 
						|
    int i;
 | 
						|
 | 
						|
    for (i=0; i<array->len; i++) {
 | 
						|
	rb_ary_push(ary, ibf_load_object(load, array->ary[i]));
 | 
						|
    }
 | 
						|
 | 
						|
    if (header->internal) rb_obj_hide(ary);
 | 
						|
    if (header->frozen)   rb_obj_freeze(ary);
 | 
						|
 | 
						|
    return ary;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
ibf_dump_object_hash_i(st_data_t key, st_data_t val, st_data_t ptr)
 | 
						|
{
 | 
						|
    struct ibf_dump *dump = (struct ibf_dump *)ptr;
 | 
						|
    long key_index = (long)ibf_dump_object(dump, (VALUE)key);
 | 
						|
    long val_index = (long)ibf_dump_object(dump, (VALUE)val);
 | 
						|
    IBF_WV(key_index);
 | 
						|
    IBF_WV(val_index);
 | 
						|
    return ST_CONTINUE;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_object_hash(struct ibf_dump *dump, VALUE obj)
 | 
						|
{
 | 
						|
    long len = RHASH_SIZE(obj);
 | 
						|
    IBF_WV(len);
 | 
						|
    if (len > 0) st_foreach(RHASH(obj)->ntbl, ibf_dump_object_hash_i, (st_data_t)dump);
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_load_object_hash(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
 | 
						|
{
 | 
						|
    const struct ibf_object_hash *hash = IBF_OBJBODY(struct ibf_object_hash, offset);
 | 
						|
    VALUE obj = rb_hash_new_with_size(hash->len);
 | 
						|
    int i;
 | 
						|
 | 
						|
    for (i=0; i<hash->len; i++) {
 | 
						|
	VALUE key = ibf_load_object(load, hash->keyval[i*2  ]);
 | 
						|
	VALUE val = ibf_load_object(load, hash->keyval[i*2+1]);
 | 
						|
	rb_hash_aset(obj, key, val);
 | 
						|
    }
 | 
						|
    rb_hash_rehash(obj);
 | 
						|
 | 
						|
    if (header->internal) rb_obj_hide(obj);
 | 
						|
    if (header->frozen)   rb_obj_freeze(obj);
 | 
						|
 | 
						|
    return obj;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_object_struct(struct ibf_dump *dump, VALUE obj)
 | 
						|
{
 | 
						|
    if (rb_obj_is_kind_of(obj, rb_cRange)) {
 | 
						|
	struct ibf_object_struct_range range;
 | 
						|
	VALUE beg, end;
 | 
						|
	range.len = 3;
 | 
						|
	range.class_index = 0;
 | 
						|
 | 
						|
	rb_range_values(obj, &beg, &end, &range.excl);
 | 
						|
	range.beg = (long)ibf_dump_object(dump, beg);
 | 
						|
	range.end = (long)ibf_dump_object(dump, end);
 | 
						|
 | 
						|
	IBF_WV(range);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	rb_bug("ibf_dump_object_struct: unsupported class");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_load_object_struct(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
 | 
						|
{
 | 
						|
    const struct ibf_object_struct_range *range = IBF_OBJBODY(struct ibf_object_struct_range, offset);
 | 
						|
    VALUE beg = ibf_load_object(load, range->beg);
 | 
						|
    VALUE end = ibf_load_object(load, range->end);
 | 
						|
    VALUE obj = rb_range_new(beg, end, range->excl);
 | 
						|
    if (header->internal) rb_obj_hide(obj);
 | 
						|
    if (header->frozen)   rb_obj_freeze(obj);
 | 
						|
    return obj;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_object_bignum(struct ibf_dump *dump, VALUE obj)
 | 
						|
{
 | 
						|
    ssize_t len = BIGNUM_LEN(obj);
 | 
						|
    ssize_t slen = BIGNUM_SIGN(obj) > 0 ? len : len * -1;
 | 
						|
    BDIGIT *d = BIGNUM_DIGITS(obj);
 | 
						|
 | 
						|
    IBF_WV(slen);
 | 
						|
    IBF_WP(d, BDIGIT, len);
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_load_object_bignum(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
 | 
						|
{
 | 
						|
    const struct ibf_object_bignum *bignum = IBF_OBJBODY(struct ibf_object_bignum, offset);
 | 
						|
    int sign = bignum->slen > 0;
 | 
						|
    ssize_t len = sign > 0 ? bignum->slen : -1 * bignum->slen;
 | 
						|
    VALUE obj = rb_integer_unpack(bignum->digits, len * 2, 2, 0,
 | 
						|
				  INTEGER_PACK_LITTLE_ENDIAN | (sign == 0 ? INTEGER_PACK_NEGATIVE : 0));
 | 
						|
    if (header->internal) rb_obj_hide(obj);
 | 
						|
    if (header->frozen)   rb_obj_freeze(obj);
 | 
						|
    return obj;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_object_data(struct ibf_dump *dump, VALUE obj)
 | 
						|
{
 | 
						|
    if (rb_data_is_encoding(obj)) {
 | 
						|
	rb_encoding *enc = rb_to_encoding(obj);
 | 
						|
	const char *name = rb_enc_name(enc);
 | 
						|
	enum ibf_object_data_type type = IBF_OBJECT_DATA_ENCODING;
 | 
						|
	long len = strlen(name) + 1;
 | 
						|
	IBF_WV(type);
 | 
						|
	IBF_WV(len);
 | 
						|
	IBF_WP(name, char, strlen(name) + 1);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	ibf_dump_object_unsupported(dump, obj);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_load_object_data(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
 | 
						|
{
 | 
						|
    const enum ibf_object_data_type *typep = IBF_OBJBODY(enum ibf_object_data_type, offset);
 | 
						|
    /* const long *lenp = IBF_OBJBODY(long, offset + sizeof(enum ibf_object_data_type)); */
 | 
						|
    const char *data = IBF_OBJBODY(char, offset + sizeof(enum ibf_object_data_type) + sizeof(long));
 | 
						|
 | 
						|
    switch (*typep) {
 | 
						|
      case IBF_OBJECT_DATA_ENCODING:
 | 
						|
	{
 | 
						|
	    VALUE encobj = rb_enc_from_encoding(rb_enc_find(data));
 | 
						|
	    return encobj;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    return ibf_load_object_unsupported(load, header, offset);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_object_complex_rational(struct ibf_dump *dump, VALUE obj)
 | 
						|
{
 | 
						|
    long real = (long)ibf_dump_object(dump, RCOMPLEX(obj)->real);
 | 
						|
    long imag = (long)ibf_dump_object(dump, RCOMPLEX(obj)->imag);
 | 
						|
 | 
						|
    IBF_WV(real);
 | 
						|
    IBF_WV(imag);
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_load_object_complex_rational(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
 | 
						|
{
 | 
						|
    const struct ibf_object_complex_rational *nums = IBF_OBJBODY(struct ibf_object_complex_rational, offset);
 | 
						|
    VALUE a = ibf_load_object(load, nums->a);
 | 
						|
    VALUE b = ibf_load_object(load, nums->b);
 | 
						|
    VALUE obj = header->type == T_COMPLEX ?
 | 
						|
      rb_complex_new(a, b) : rb_rational_new(a, b);
 | 
						|
 | 
						|
    if (header->internal) rb_obj_hide(obj);
 | 
						|
    if (header->frozen)   rb_obj_freeze(obj);
 | 
						|
    return obj;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_object_symbol(struct ibf_dump *dump, VALUE obj)
 | 
						|
{
 | 
						|
    VALUE str = rb_sym2str(obj);
 | 
						|
    long str_index = (long)ibf_dump_object(dump, str);
 | 
						|
    IBF_WV(str_index);
 | 
						|
}
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_load_object_symbol(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t offset)
 | 
						|
{
 | 
						|
    /* const struct ibf_object_header *header = IBF_OBJHEADER(offset); */
 | 
						|
    const struct ibf_object_symbol *symbol = IBF_OBJBODY(struct ibf_object_symbol, offset);
 | 
						|
    VALUE str = ibf_load_object(load, symbol->str);
 | 
						|
    ID id = rb_intern_str(str);
 | 
						|
    return ID2SYM(id);
 | 
						|
}
 | 
						|
 | 
						|
typedef void (*ibf_dump_object_function)(struct ibf_dump *dump, VALUE obj);
 | 
						|
static ibf_dump_object_function dump_object_functions[RUBY_T_MASK+1] = {
 | 
						|
    ibf_dump_object_unsupported, /* T_NONE */
 | 
						|
    ibf_dump_object_unsupported, /* T_OBJECT */
 | 
						|
    ibf_dump_object_class,       /* T_CLASS */
 | 
						|
    ibf_dump_object_unsupported, /* T_MODULE */
 | 
						|
    ibf_dump_object_float,       /* T_FLOAT */
 | 
						|
    ibf_dump_object_string,      /* T_STRING */
 | 
						|
    ibf_dump_object_regexp,      /* T_REGEXP */
 | 
						|
    ibf_dump_object_array,       /* T_ARRAY */
 | 
						|
    ibf_dump_object_hash,        /* T_HASH */
 | 
						|
    ibf_dump_object_struct,      /* T_STRUCT */
 | 
						|
    ibf_dump_object_bignum,      /* T_BIGNUM */
 | 
						|
    ibf_dump_object_unsupported, /* T_FILE */
 | 
						|
    ibf_dump_object_data,        /* T_DATA */
 | 
						|
    ibf_dump_object_unsupported, /* T_MATCH */
 | 
						|
    ibf_dump_object_complex_rational, /* T_COMPLEX */
 | 
						|
    ibf_dump_object_complex_rational, /* T_RATIONAL */
 | 
						|
    ibf_dump_object_unsupported, /* 0x10 */
 | 
						|
    ibf_dump_object_unsupported, /* 0x11 T_NIL */
 | 
						|
    ibf_dump_object_unsupported, /* 0x12 T_TRUE */
 | 
						|
    ibf_dump_object_unsupported, /* 0x13 T_FALSE */
 | 
						|
    ibf_dump_object_symbol,      /* 0x14 T_SYMBOL */
 | 
						|
    ibf_dump_object_unsupported, /* T_FIXNUM */
 | 
						|
    ibf_dump_object_unsupported, /* T_UNDEF */
 | 
						|
    ibf_dump_object_unsupported, /* 0x17 */
 | 
						|
    ibf_dump_object_unsupported, /* 0x18 */
 | 
						|
    ibf_dump_object_unsupported, /* 0x19 */
 | 
						|
    ibf_dump_object_unsupported, /* T_IMEMO 0x1a */
 | 
						|
    ibf_dump_object_unsupported, /* T_NODE 0x1b */
 | 
						|
    ibf_dump_object_unsupported, /* T_ICLASS 0x1c */
 | 
						|
    ibf_dump_object_unsupported, /* T_ZOMBIE 0x1d */
 | 
						|
    ibf_dump_object_unsupported, /* 0x1e */
 | 
						|
    ibf_dump_object_unsupported  /* 0x1f */
 | 
						|
};
 | 
						|
 | 
						|
static ibf_offset_t
 | 
						|
lbf_dump_object_object(struct ibf_dump *dump, VALUE obj)
 | 
						|
{
 | 
						|
    struct ibf_object_header obj_header;
 | 
						|
    ibf_offset_t current_offset = ibf_dump_pos(dump);
 | 
						|
    obj_header.type = TYPE(obj);
 | 
						|
 | 
						|
    if (SPECIAL_CONST_P(obj)) {
 | 
						|
	if (RB_TYPE_P(obj, T_SYMBOL) ||
 | 
						|
	    RB_TYPE_P(obj, T_FLOAT)) {
 | 
						|
	    obj_header.internal = FALSE;
 | 
						|
	    goto dump_object;
 | 
						|
	}
 | 
						|
	obj_header.special_const = TRUE;
 | 
						|
	obj_header.frozen = TRUE;
 | 
						|
	obj_header.internal = TRUE;
 | 
						|
	IBF_WV(obj_header);
 | 
						|
	IBF_WV(obj);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	obj_header.internal = (RBASIC_CLASS(obj) == 0) ? TRUE : FALSE;
 | 
						|
      dump_object:
 | 
						|
	obj_header.special_const = FALSE;
 | 
						|
	obj_header.frozen = FL_TEST(obj, FL_FREEZE) ? TRUE : FALSE;
 | 
						|
	IBF_WV(obj_header);
 | 
						|
	(*dump_object_functions[obj_header.type])(dump, obj);
 | 
						|
    }
 | 
						|
 | 
						|
    return current_offset;
 | 
						|
}
 | 
						|
 | 
						|
typedef VALUE (*ibf_load_object_function)(const struct ibf_load *load, const struct ibf_object_header *header, ibf_offset_t);
 | 
						|
static ibf_load_object_function load_object_functions[RUBY_T_MASK+1] = {
 | 
						|
    ibf_load_object_unsupported, /* T_NONE */
 | 
						|
    ibf_load_object_unsupported, /* T_OBJECT */
 | 
						|
    ibf_load_object_class,       /* T_CLASS */
 | 
						|
    ibf_load_object_unsupported, /* T_MODULE */
 | 
						|
    ibf_load_object_float,       /* T_FLOAT */
 | 
						|
    ibf_load_object_string,      /* T_STRING */
 | 
						|
    ibf_load_object_regexp,      /* T_REGEXP */
 | 
						|
    ibf_load_object_array,       /* T_ARRAY */
 | 
						|
    ibf_load_object_hash,        /* T_HASH */
 | 
						|
    ibf_load_object_struct,      /* T_STRUCT */
 | 
						|
    ibf_load_object_bignum,      /* T_BIGNUM */
 | 
						|
    ibf_load_object_unsupported, /* T_FILE */
 | 
						|
    ibf_load_object_data,        /* T_DATA */
 | 
						|
    ibf_load_object_unsupported, /* T_MATCH */
 | 
						|
    ibf_load_object_complex_rational, /* T_COMPLEX */
 | 
						|
    ibf_load_object_complex_rational, /* T_RATIONAL */
 | 
						|
    ibf_load_object_unsupported, /* 0x10 */
 | 
						|
    ibf_load_object_unsupported, /* T_NIL */
 | 
						|
    ibf_load_object_unsupported, /* T_TRUE */
 | 
						|
    ibf_load_object_unsupported, /* T_FALSE */
 | 
						|
    ibf_load_object_symbol,
 | 
						|
    ibf_load_object_unsupported, /* T_FIXNUM */
 | 
						|
    ibf_load_object_unsupported, /* T_UNDEF */
 | 
						|
    ibf_load_object_unsupported, /* 0x17 */
 | 
						|
    ibf_load_object_unsupported, /* 0x18 */
 | 
						|
    ibf_load_object_unsupported, /* 0x19 */
 | 
						|
    ibf_load_object_unsupported, /* T_IMEMO 0x1a */
 | 
						|
    ibf_load_object_unsupported, /* T_NODE 0x1b */
 | 
						|
    ibf_load_object_unsupported, /* T_ICLASS 0x1c */
 | 
						|
    ibf_load_object_unsupported, /* T_ZOMBIE 0x1d */
 | 
						|
    ibf_load_object_unsupported, /* 0x1e */
 | 
						|
    ibf_load_object_unsupported  /* 0x1f */
 | 
						|
};
 | 
						|
 | 
						|
static VALUE
 | 
						|
ibf_load_object(const struct ibf_load *load, VALUE object_index)
 | 
						|
{
 | 
						|
    if (object_index == 0) {
 | 
						|
	return Qnil;
 | 
						|
    }
 | 
						|
    else if (object_index >= load->header->object_list_size) {
 | 
						|
	rb_raise(rb_eIndexError, "object index out of range: %"PRIdVALUE, object_index);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	VALUE obj = rb_ary_entry(load->obj_list, (long)object_index);
 | 
						|
	if (obj == Qnil) { /* TODO: avoid multiple Qnil load */
 | 
						|
	    ibf_offset_t *offsets = (ibf_offset_t *)(load->header->object_list_offset + load->buff);
 | 
						|
	    ibf_offset_t offset = offsets[object_index];
 | 
						|
	    const struct ibf_object_header *header = IBF_OBJHEADER(offset);
 | 
						|
 | 
						|
	    if (header->special_const) {
 | 
						|
		VALUE *vp = IBF_OBJBODY(VALUE, offset);
 | 
						|
		obj = *vp;
 | 
						|
	    }
 | 
						|
	    else {
 | 
						|
		obj = (*load_object_functions[header->type])(load, header, offset);
 | 
						|
	    }
 | 
						|
 | 
						|
	    rb_ary_store(load->obj_list, (long)object_index, obj);
 | 
						|
	}
 | 
						|
	iseq_add_mark_object(load->iseq, obj);
 | 
						|
	return obj;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_object_list(struct ibf_dump *dump, struct ibf_header *header)
 | 
						|
{
 | 
						|
    VALUE listv;
 | 
						|
    ibf_offset_t *list = ALLOCV_N(ibf_offset_t, listv, RARRAY_LEN(dump->obj_list));
 | 
						|
    int i, size;
 | 
						|
 | 
						|
    for (i=0; i<RARRAY_LEN(dump->obj_list); i++) {
 | 
						|
	VALUE obj = RARRAY_AREF(dump->obj_list, i);
 | 
						|
	ibf_offset_t offset = lbf_dump_object_object(dump, obj);
 | 
						|
	list[i] = offset;
 | 
						|
    }
 | 
						|
    size = i;
 | 
						|
    header->object_list_offset = ibf_dump_pos(dump);
 | 
						|
 | 
						|
    for (i=0; i<size; i++) {
 | 
						|
	ibf_offset_t offset = list[i];
 | 
						|
	IBF_WV(offset);
 | 
						|
    }
 | 
						|
    ALLOCV_END(listv);
 | 
						|
 | 
						|
    header->object_list_size = size;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_mark(void *ptr)
 | 
						|
{
 | 
						|
    struct ibf_dump *dump = (struct ibf_dump *)ptr;
 | 
						|
    rb_gc_mark(dump->str);
 | 
						|
    rb_gc_mark(dump->iseq_list);
 | 
						|
    rb_gc_mark(dump->obj_list);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_free(void *ptr)
 | 
						|
{
 | 
						|
    struct ibf_dump *dump = (struct ibf_dump *)ptr;
 | 
						|
    if (dump->iseq_table) {
 | 
						|
	st_free_table(dump->iseq_table);
 | 
						|
	dump->iseq_table = 0;
 | 
						|
    }
 | 
						|
    if (dump->id_table) {
 | 
						|
	st_free_table(dump->id_table);
 | 
						|
	dump->id_table = 0;
 | 
						|
    }
 | 
						|
    ruby_xfree(dump);
 | 
						|
}
 | 
						|
 | 
						|
static size_t
 | 
						|
ibf_dump_memsize(const void *ptr)
 | 
						|
{
 | 
						|
    struct ibf_dump *dump = (struct ibf_dump *)ptr;
 | 
						|
    size_t size = sizeof(*dump);
 | 
						|
    if (dump->iseq_table) size += st_memsize(dump->iseq_table);
 | 
						|
    if (dump->id_table) size += st_memsize(dump->id_table);
 | 
						|
    return size;
 | 
						|
}
 | 
						|
 | 
						|
static const rb_data_type_t ibf_dump_type = {
 | 
						|
    "ibf_dump",
 | 
						|
    {ibf_dump_mark, ibf_dump_free, ibf_dump_memsize,},
 | 
						|
    0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
 | 
						|
};
 | 
						|
 | 
						|
static void
 | 
						|
ibf_dump_setup(struct ibf_dump *dump, VALUE dumper_obj)
 | 
						|
{
 | 
						|
    RB_OBJ_WRITE(dumper_obj, &dump->str, rb_str_new(0, 0));
 | 
						|
    RB_OBJ_WRITE(dumper_obj, &dump->iseq_list, rb_ary_tmp_new(0));
 | 
						|
    RB_OBJ_WRITE(dumper_obj, &dump->obj_list, rb_ary_tmp_new(1));
 | 
						|
    rb_ary_push(dump->obj_list, Qnil); /* 0th is nil */
 | 
						|
    dump->iseq_table = st_init_numtable(); /* need free */
 | 
						|
    dump->id_table = st_init_numtable();   /* need free */
 | 
						|
 | 
						|
    ibf_table_index(dump->id_table, 0); /* id_index:0 is 0 */
 | 
						|
}
 | 
						|
 | 
						|
VALUE
 | 
						|
iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt)
 | 
						|
{
 | 
						|
    struct ibf_dump *dump;
 | 
						|
    struct ibf_header header = {{0}};
 | 
						|
    VALUE dump_obj;
 | 
						|
    VALUE str;
 | 
						|
 | 
						|
    if (iseq->body->parent_iseq != NULL ||
 | 
						|
	iseq->body->local_iseq != iseq) {
 | 
						|
	rb_raise(rb_eRuntimeError, "should be top of iseq");
 | 
						|
    }
 | 
						|
    if (RTEST(ISEQ_COVERAGE(iseq))) {
 | 
						|
	rb_raise(rb_eRuntimeError, "should not compile with coverage");
 | 
						|
    }
 | 
						|
 | 
						|
    dump_obj = TypedData_Make_Struct(0, struct ibf_dump, &ibf_dump_type, dump);
 | 
						|
    ibf_dump_setup(dump, dump_obj);
 | 
						|
 | 
						|
    ibf_dump_write(dump, &header, sizeof(header));
 | 
						|
    ibf_dump_write(dump, RUBY_PLATFORM, strlen(RUBY_PLATFORM) + 1);
 | 
						|
    ibf_dump_iseq(dump, iseq);
 | 
						|
 | 
						|
    header.magic[0] = 'Y'; /* YARB */
 | 
						|
    header.magic[1] = 'A';
 | 
						|
    header.magic[2] = 'R';
 | 
						|
    header.magic[3] = 'B';
 | 
						|
    header.major_version = ISEQ_MAJOR_VERSION;
 | 
						|
    header.minor_version = ISEQ_MINOR_VERSION;
 | 
						|
    ibf_dump_iseq_list(dump, &header);
 | 
						|
    ibf_dump_id_list(dump, &header);
 | 
						|
    ibf_dump_object_list(dump, &header);
 | 
						|
    header.size = ibf_dump_pos(dump);
 | 
						|
 | 
						|
    if (RTEST(opt)) {
 | 
						|
	VALUE opt_str = opt;
 | 
						|
	const char *ptr = StringValuePtr(opt_str);
 | 
						|
	header.extra_size = RSTRING_LENINT(opt_str);
 | 
						|
	ibf_dump_write(dump, ptr, header.extra_size);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	header.extra_size = 0;
 | 
						|
    }
 | 
						|
 | 
						|
    ibf_dump_overwrite(dump, &header, sizeof(header), 0);
 | 
						|
 | 
						|
    str = dump->str;
 | 
						|
    ibf_dump_free(dump);
 | 
						|
    DATA_PTR(dump_obj) = NULL;
 | 
						|
    RB_GC_GUARD(dump_obj);
 | 
						|
    return str;
 | 
						|
}
 | 
						|
 | 
						|
static const ibf_offset_t *
 | 
						|
ibf_iseq_list(const struct ibf_load *load)
 | 
						|
{
 | 
						|
    return (ibf_offset_t *)(load->buff + load->header->iseq_list_offset);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
ibf_load_iseq_complete(rb_iseq_t *iseq)
 | 
						|
{
 | 
						|
    struct ibf_load *load = RTYPEDDATA_DATA(iseq->aux.loader.obj);
 | 
						|
    rb_iseq_t *prev_src_iseq = load->iseq;
 | 
						|
    load->iseq = iseq;
 | 
						|
    ibf_load_iseq_each(load, iseq, ibf_iseq_list(load)[iseq->aux.loader.index]);
 | 
						|
    ISEQ_COMPILE_DATA_CLEAR(iseq);
 | 
						|
    FL_UNSET(iseq, ISEQ_NOT_LOADED_YET);
 | 
						|
    load->iseq = prev_src_iseq;
 | 
						|
}
 | 
						|
 | 
						|
#if USE_LAZY_LOAD
 | 
						|
const rb_iseq_t *
 | 
						|
rb_iseq_complete(const rb_iseq_t *iseq)
 | 
						|
{
 | 
						|
    ibf_load_iseq_complete((rb_iseq_t *)iseq);
 | 
						|
    return iseq;
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
static rb_iseq_t *
 | 
						|
ibf_load_iseq(const struct ibf_load *load, const rb_iseq_t *index_iseq)
 | 
						|
{
 | 
						|
    int iseq_index = (int)(VALUE)index_iseq;
 | 
						|
 | 
						|
    if (iseq_index == -1) {
 | 
						|
	return NULL;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
	VALUE iseqv = rb_ary_entry(load->iseq_list, iseq_index);
 | 
						|
 | 
						|
	if (iseqv != Qnil) {
 | 
						|
	    return (rb_iseq_t *)iseqv;
 | 
						|
	}
 | 
						|
	else {
 | 
						|
	    rb_iseq_t *iseq = iseq_imemo_alloc();
 | 
						|
	    FL_SET(iseq, ISEQ_NOT_LOADED_YET);
 | 
						|
	    iseq->aux.loader.obj = load->loader_obj;
 | 
						|
	    iseq->aux.loader.index = iseq_index;
 | 
						|
	    rb_ary_store(load->iseq_list, iseq_index, (VALUE)iseq);
 | 
						|
 | 
						|
#if !USE_LAZY_LOAD
 | 
						|
	    ibf_load_iseq_complete(iseq);
 | 
						|
#endif /* !USE_LAZY_LOAD */
 | 
						|
 | 
						|
	    if (load->iseq) {
 | 
						|
		iseq_add_mark_object(load->iseq, (VALUE)iseq);
 | 
						|
	    }
 | 
						|
	    return iseq;
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_load_setup(struct ibf_load *load, VALUE loader_obj, VALUE str)
 | 
						|
{
 | 
						|
    rb_check_safe_obj(str);
 | 
						|
 | 
						|
    if (RSTRING_LENINT(str) < (int)sizeof(struct ibf_header)) {
 | 
						|
	rb_raise(rb_eRuntimeError, "broken binary format");
 | 
						|
    }
 | 
						|
    RB_OBJ_WRITE(loader_obj, &load->str, str);
 | 
						|
    load->loader_obj = loader_obj;
 | 
						|
    load->buff = StringValuePtr(str);
 | 
						|
    load->header = (struct ibf_header *)load->buff;
 | 
						|
    RB_OBJ_WRITE(loader_obj, &load->iseq_list, rb_ary_tmp_new(0));
 | 
						|
    RB_OBJ_WRITE(loader_obj, &load->obj_list, rb_ary_tmp_new(0));
 | 
						|
    load->id_list = ZALLOC_N(ID, load->header->id_list_size);
 | 
						|
    load->iseq = NULL;
 | 
						|
 | 
						|
    if (RSTRING_LENINT(str) < (int)load->header->size) {
 | 
						|
	rb_raise(rb_eRuntimeError, "broken binary format");
 | 
						|
    }
 | 
						|
    if (strncmp(load->header->magic, "YARB", 4) != 0) {
 | 
						|
	rb_raise(rb_eRuntimeError, "unknown binary format");
 | 
						|
    }
 | 
						|
    if (load->header->major_version != ISEQ_MAJOR_VERSION ||
 | 
						|
	load->header->minor_version != ISEQ_MINOR_VERSION) {
 | 
						|
	rb_raise(rb_eRuntimeError, "unmatched version file (%u.%u for %u.%u)",
 | 
						|
		 load->header->major_version, load->header->minor_version, ISEQ_MAJOR_VERSION, ISEQ_MINOR_VERSION);
 | 
						|
    }
 | 
						|
    if (strcmp(load->buff + sizeof(struct ibf_header), RUBY_PLATFORM) != 0) {
 | 
						|
	rb_raise(rb_eRuntimeError, "unmatched platform");
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_loader_mark(void *ptr)
 | 
						|
{
 | 
						|
    struct ibf_load *load = (struct ibf_load *)ptr;
 | 
						|
    rb_gc_mark(load->str);
 | 
						|
    rb_gc_mark(load->iseq_list);
 | 
						|
    rb_gc_mark(load->obj_list);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ibf_loader_free(void *ptr)
 | 
						|
{
 | 
						|
    struct ibf_load *load = (struct ibf_load *)ptr;
 | 
						|
    ruby_xfree(load->id_list);
 | 
						|
    ruby_xfree(load);
 | 
						|
}
 | 
						|
 | 
						|
static size_t
 | 
						|
ibf_loader_memsize(const void *ptr)
 | 
						|
{
 | 
						|
    struct ibf_load *load = (struct ibf_load *)ptr;
 | 
						|
    return sizeof(struct ibf_load) + load->header->id_list_size * sizeof(ID);
 | 
						|
}
 | 
						|
 | 
						|
static const rb_data_type_t ibf_load_type = {
 | 
						|
    "ibf_loader",
 | 
						|
    {ibf_loader_mark, ibf_loader_free, ibf_loader_memsize,},
 | 
						|
    0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
 | 
						|
};
 | 
						|
 | 
						|
const rb_iseq_t *
 | 
						|
iseq_ibf_load(VALUE str)
 | 
						|
{
 | 
						|
    struct ibf_load *load;
 | 
						|
    const rb_iseq_t *iseq;
 | 
						|
    VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
 | 
						|
 | 
						|
    ibf_load_setup(load, loader_obj, str);
 | 
						|
    iseq = ibf_load_iseq(load, 0);
 | 
						|
 | 
						|
    RB_GC_GUARD(loader_obj);
 | 
						|
    return iseq;
 | 
						|
}
 | 
						|
 | 
						|
VALUE
 | 
						|
iseq_ibf_load_extra_data(VALUE str)
 | 
						|
{
 | 
						|
    struct ibf_load *load;
 | 
						|
    VALUE loader_obj = TypedData_Make_Struct(0, struct ibf_load, &ibf_load_type, load);
 | 
						|
    VALUE extra_str;
 | 
						|
 | 
						|
    ibf_load_setup(load, loader_obj, str);
 | 
						|
    extra_str = rb_str_new(load->buff + load->header->size, load->header->extra_size);
 | 
						|
    RB_GC_GUARD(loader_obj);
 | 
						|
    return extra_str;
 | 
						|
}
 |