diff --git a/ChangeLog b/ChangeLog index ef8e1b60a05a91..1df6829c15b566 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Mon Sep 24 17:36:51 2012 Nobuyoshi Nakada + + * compile.c (defined_expr), insns.def (defined): share single frozen + strings. [EXPERIMENTAL] [ruby-core:47558][Feature #7035] + + * iseq.c (rb_iseq_defined_string): make expression strings. + Mon Sep 24 11:22:36 2012 NARUSE, Yui * tool/merger.rb: add --ticket option to add ticket number. diff --git a/compile.c b/compile.c index fc6453ad1a8528..2f8cbb51cd11a4 100644 --- a/compile.c +++ b/compile.c @@ -2701,23 +2701,23 @@ static int defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE *node, LABEL **lfinish, VALUE needstr) { - const char *estr = 0; + enum defined_type expr_type = 0; enum node_type type; switch (type = nd_type(node)) { /* easy literals */ case NODE_NIL: - estr = "nil"; + expr_type = DEFINED_NIL; break; case NODE_SELF: - estr = "self"; + expr_type = DEFINED_SELF; break; case NODE_TRUE: - estr = "true"; + expr_type = DEFINED_TRUE; break; case NODE_FALSE: - estr = "false"; + expr_type = DEFINED_FALSE; break; case NODE_ARRAY:{ @@ -2738,13 +2738,13 @@ defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *ret, case NODE_AND: case NODE_OR: default: - estr = "expression"; + expr_type = DEFINED_EXPR; break; /* variables */ case NODE_LVAR: case NODE_DVAR: - estr = "local-variable"; + expr_type = DEFINED_LVAR; break; case NODE_IVAR: @@ -2866,16 +2866,14 @@ defined_expr(rb_iseq_t *iseq, LINK_ANCHOR *ret, case NODE_CDECL: case NODE_CVDECL: case NODE_CVASGN: - estr = "assignment"; + expr_type = DEFINED_ASGN; break; } - if (estr != 0) { + if (expr_type) { if (needstr != Qfalse) { - VALUE str = rb_str_new2(estr); - hide_obj(str); - ADD_INSN1(ret, nd_line(node), putstring, str); - iseq_add_mark_object_compile_time(iseq, str); + 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); diff --git a/insns.def b/insns.def index e55ec30a072438..94e1a24203b132 100644 --- a/insns.def +++ b/insns.def @@ -716,7 +716,7 @@ defined (VALUE val) { VALUE klass; - const char *expr_type = 0; + enum defined_type expr_type = 0; enum defined_type type = (enum defined_type)op_type; val = Qnil; @@ -724,7 +724,7 @@ defined switch (type) { case DEFINED_IVAR: if (rb_ivar_defined(GET_SELF(), SYM2ID(obj))) { - expr_type = "instance-variable"; + expr_type = DEFINED_IVAR; } break; case DEFINED_IVAR2: @@ -732,7 +732,7 @@ defined break; case DEFINED_GVAR: if (rb_gvar_defined(rb_global_entry(SYM2ID(obj)))) { - expr_type = "global-variable"; + expr_type = DEFINED_GVAR; } break; case DEFINED_CVAR: @@ -740,20 +740,20 @@ defined NODE *cref = rb_vm_get_cref(GET_ISEQ(), GET_EP()); klass = vm_get_cvar_base(cref, GET_CFP()); if (rb_cvar_defined(klass, SYM2ID(obj))) { - expr_type = "class variable"; + expr_type = DEFINED_CVAR; } break; } case DEFINED_CONST: klass = v; if (vm_get_ev_const(th, GET_ISEQ(), klass, SYM2ID(obj), 1)) { - expr_type = "constant"; + expr_type = DEFINED_CONST; } break; case DEFINED_FUNC: klass = CLASS_OF(v); if (rb_method_boundp(klass, SYM2ID(obj), 0)) { - expr_type = "method"; + expr_type = DEFINED_METHOD; } break; case DEFINED_METHOD:{ @@ -765,7 +765,7 @@ defined if (!((me->flag & NOEX_PROTECTED) && !rb_obj_is_kind_of(GET_SELF(), rb_class_real(klass)))) { - expr_type = "method"; + expr_type = DEFINED_METHOD; } } } @@ -776,13 +776,13 @@ defined args[0] = obj; args[1] = Qfalse; r = rb_check_funcall(v, rb_intern("respond_to_missing?"), 2, args); if (r != Qundef && RTEST(r)) - expr_type = "method"; + expr_type = DEFINED_METHOD; } break; } case DEFINED_YIELD: if (GET_BLOCK_PTR()) { - expr_type = "yield"; + expr_type = DEFINED_YIELD; } break; case DEFINED_ZSUPER:{ @@ -791,7 +791,7 @@ defined VALUE klass = vm_search_normal_superclass(GET_CFP()->klass); ID id = me->def ? me->def->original_id : me->called_id; if (rb_method_boundp(klass, id, 0)) { - expr_type = "super"; + expr_type = DEFINED_ZSUPER; } } break; @@ -799,7 +799,7 @@ defined case DEFINED_REF:{ val = vm_getspecial(th, GET_LEP(), Qfalse, FIX2INT(obj)); if (val != Qnil) { - expr_type = "global-variable"; + expr_type = DEFINED_GVAR; } break; } @@ -809,7 +809,7 @@ defined } if (expr_type != 0) { if (needstr != Qfalse) { - val = rb_str_new2(expr_type); + val = rb_iseq_defined_string(expr_type); } else { val = Qtrue; diff --git a/iseq.c b/iseq.c index 8d8d92174d74dd..989a8a1120f2d2 100644 --- a/iseq.c +++ b/iseq.c @@ -18,6 +18,8 @@ #include "vm_core.h" #include "iseq.h" +#define numberof(array) (int)(sizeof(array) / sizeof((array)[0])) + #include "insns.inc" #include "insns_info.inc" @@ -1747,6 +1749,45 @@ rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc) return args; } +VALUE +rb_iseq_defined_string(enum defined_type type) +{ + static const char expr_names[][18] = { + "nil", + "instance-variable", + "local-variable", + "global-variable", + "class variable", + "constant", + "method", + "yield", + "super", + "self", + "true", + "false", + "assignment", + "expression", + }; + const char *estr; + VALUE *defs, str; + + if ((unsigned)(type - 1) >= (unsigned)numberof(expr_names)) return 0; + estr = expr_names[type - 1]; + if (!estr[0]) return 0; + defs = GET_VM()->defined_strings; + if (!defs) { + defs = ruby_xcalloc(numberof(expr_names), sizeof(VALUE)); + GET_VM()->defined_strings = defs; + } + str = defs[type]; + if (!str) { + str = rb_str_new_cstr(estr);; + OBJ_FREEZE(str); + defs[type] = str; + } + return str; +} + /* ruby2cext */ VALUE diff --git a/iseq.h b/iseq.h index 39f139ff94575c..17eebfff1211d8 100644 --- a/iseq.h +++ b/iseq.h @@ -106,18 +106,27 @@ struct iseq_compile_data { /* defined? */ enum defined_type { - DEFINED_IVAR = 1, - DEFINED_IVAR2, + DEFINED_NIL = 1, + DEFINED_IVAR, + DEFINED_LVAR, DEFINED_GVAR, DEFINED_CVAR, DEFINED_CONST, DEFINED_METHOD, DEFINED_YIELD, - DEFINED_REF, DEFINED_ZSUPER, + DEFINED_SELF, + DEFINED_TRUE, + DEFINED_FALSE, + DEFINED_ASGN, + DEFINED_EXPR, + DEFINED_IVAR2, + DEFINED_REF, DEFINED_FUNC }; +VALUE rb_iseq_defined_string(enum defined_type type); + #if defined __GNUC__ && __GNUC__ >= 4 #pragma GCC visibility pop #endif diff --git a/test/ruby/test_defined.rb b/test/ruby/test_defined.rb index c02d1376b23cbb..28e3da5f31080f 100644 --- a/test/ruby/test_defined.rb +++ b/test/ruby/test_defined.rb @@ -86,6 +86,12 @@ def test_defined assert_equal nil, defined?($2) end + def test_defined_impl_specific + feature7035 = '[ruby-core:47558]' # not spec + assert_operator(defined?(Foo), :frozen?, feature7035) + assert_same(defined?(Foo), defined?(Array), feature7035) + end + class TestAutoloadedSuperclass autoload :A, "a" end diff --git a/vm.c b/vm.c index 55ccfe79391762..f842a6dfbaa007 100644 --- a/vm.c +++ b/vm.c @@ -1515,6 +1515,9 @@ rb_vm_mark(void *ptr) if (vm->trap_list[i].cmd) rb_gc_mark(vm->trap_list[i].cmd); } + if (vm->defined_strings) { + rb_gc_mark_locations(vm->defined_strings, vm->defined_strings + DEFINED_EXPR); + } } RUBY_MARK_LEAVE("vm"); @@ -1560,7 +1563,12 @@ vm_memsize(const void *ptr) { if (ptr) { const rb_vm_t *vmobj = ptr; - return sizeof(rb_vm_t) + st_memsize(vmobj->living_threads); + size_t size = sizeof(rb_vm_t); + size += st_memsize(vmobj->living_threads); + if (vmobj->defined_strings) { + size += DEFINED_EXPR * sizeof(VALUE); + } + return size; } else { return 0; diff --git a/vm_core.h b/vm_core.h index 9d776c952d917f..d890c1619b2ba6 100644 --- a/vm_core.h +++ b/vm_core.h @@ -349,6 +349,8 @@ typedef struct rb_vm_struct { * objects so do *NOT* mark this when you GC. */ struct RArray at_exit; + + VALUE *defined_strings; } rb_vm_t; typedef struct {