1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Avoid top-level search for nested constant reference from nil in defined?

Fixes [Bug #16332]

Constant access was changed to no longer allow top-level constant access
through `nil`, but `defined?` wasn't changed at the same time to stay
consistent.

Use a separate defined type to distinguish between a constant
referenced from the current lexical scope and one referenced from
another namespace.
This commit is contained in:
Dylan Thacker-Smith 2019-11-06 01:47:32 -05:00 committed by Nobuyoshi Nakada
parent a5b6d7bca8
commit ac112f2b5d
Notes: git 2019-11-13 15:37:26 +09:00
6 changed files with 28 additions and 11 deletions

View file

@ -4728,13 +4728,13 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
ADD_INSN3(ret, line, defined,
(rb_is_const_id(node->nd_mid) ?
INT2FIX(DEFINED_CONST) : INT2FIX(DEFINED_METHOD)),
INT2FIX(DEFINED_CONST_FROM) : INT2FIX(DEFINED_METHOD)),
ID2SYM(node->nd_mid), needstr);
return;
case NODE_COLON3:
ADD_INSN1(ret, line, putobject, rb_cObject);
ADD_INSN3(ret, line, defined,
INT2FIX(DEFINED_CONST), ID2SYM(node->nd_mid), needstr);
INT2FIX(DEFINED_CONST_FROM), ID2SYM(node->nd_mid), needstr);
return;
/* method dispatch */
@ -7493,7 +7493,7 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, in
if (node->nd_aid == idOROP) {
lassign = NEW_LABEL(line);
ADD_INSN(ret, line, dup); /* cref cref */
ADD_INSN3(ret, line, defined, INT2FIX(DEFINED_CONST),
ADD_INSN3(ret, line, defined, INT2FIX(DEFINED_CONST_FROM),
ID2SYM(mid), Qfalse); /* cref bool */
ADD_INSNL(ret, line, branchunless, lassign); /* cref */
}

19
iseq.c
View file

@ -1830,13 +1830,20 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
case TS_NUM: /* ULONG */
if (insn == BIN(defined) && op_no == 0) {
enum defined_type deftype = (enum defined_type)op;
if (deftype == DEFINED_FUNC) {
ret = rb_fstring_lit("func"); break;
switch (deftype) {
case DEFINED_FUNC:
ret = rb_fstring_lit("func");
break;
case DEFINED_REF:
ret = rb_fstring_lit("ref");
break;
case DEFINED_CONST_FROM:
ret = rb_fstring_lit("constant-from");
break;
default:
ret = rb_iseq_defined_string(deftype);
break;
}
if (deftype == DEFINED_REF) {
ret = rb_fstring_lit("ref"); break;
}
ret = rb_iseq_defined_string(deftype);
if (ret) break;
}
else if (insn == BIN(checktype) && op_no == 0) {

3
iseq.h
View file

@ -300,7 +300,8 @@ enum defined_type {
DEFINED_EXPR,
DEFINED_IVAR2,
DEFINED_REF,
DEFINED_FUNC
DEFINED_FUNC,
DEFINED_CONST_FROM
};
VALUE rb_iseq_defined_string(enum defined_type type);

View file

@ -50,8 +50,13 @@ class TestConst < Test::Unit::TestCase
def test_const_access_from_nil
assert_raise(TypeError) { eval("nil::Object") }
assert_nil eval("defined?(nil::Object)")
assert_raise(TypeError) { eval("c = nil; c::Object") }
assert_nil eval("c = nil; defined?(c::Object)")
assert_raise(TypeError) { eval("sc = Class.new; sc::C = nil; sc::C::Object") }
assert_nil eval("sc = Class.new; sc::C = nil; defined?(sc::C::Object)")
end
def test_redefinition

View file

@ -81,6 +81,7 @@ leafness_of_defined(rb_num_t op_type)
case DEFINED_ZSUPER:
return false;
case DEFINED_CONST:
case DEFINED_CONST_FROM:
/* has rb_autoload_load(); */
return false;
case DEFINED_FUNC:

View file

@ -3477,11 +3477,14 @@ vm_defined(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, rb_num_t op_
break;
}
case DEFINED_CONST:
case DEFINED_CONST_FROM: {
bool allow_nil = type == DEFINED_CONST;
klass = v;
if (vm_get_ev_const(ec, klass, SYM2ID(obj), 1, 1)) {
if (vm_get_ev_const(ec, klass, SYM2ID(obj), allow_nil, true)) {
expr_type = DEFINED_CONST;
}
break;
}
case DEFINED_FUNC:
klass = CLASS_OF(v);
if (rb_method_boundp(klass, SYM2ID(obj), 0)) {