[前][次][番号順一覧][スレッド一覧]

ruby-changes:56197

From: Nobuyoshi <ko1@a...>
Date: Sun, 23 Jun 2019 01:47:43 +0900 (JST)
Subject: [ruby-changes:56197] Nobuyoshi Nakada: 9384383019 (trunk): Module#constant_source_location [Feature #10771]

https://git.ruby-lang.org/ruby.git/commit/?id=9384383019

From 93843830198ba436e2ea21a60a11758d47cf521b Mon Sep 17 00:00:00 2001
From: Nobuyoshi Nakada <nobu@r...>
Date: Thu, 13 Dec 2018 21:49:05 +0900
Subject: Module#constant_source_location [Feature #10771]


diff --git a/constant.h b/constant.h
index 6c8cda0..f1a13fe 100644
--- a/constant.h
+++ b/constant.h
@@ -47,5 +47,8 @@ int rb_public_const_defined_at(VALUE klass, ID id); https://github.com/ruby/ruby/blob/trunk/constant.h#L47
 int rb_public_const_defined_from(VALUE klass, ID id);
 rb_const_entry_t *rb_const_lookup(VALUE klass, ID id);
 int rb_autoloading_value(VALUE mod, ID id, VALUE *value, rb_const_flag_t *flag);
+VALUE rb_const_source_location(VALUE, ID);
+VALUE rb_const_source_location_from(VALUE, ID);
+VALUE rb_const_source_location_at(VALUE, ID);
 
 #endif /* CONSTANT_H */
diff --git a/object.c b/object.c
index 17964bb..9536cd3 100644
--- a/object.c
+++ b/object.c
@@ -2720,6 +2720,105 @@ rb_mod_const_defined(int argc, VALUE *argv, VALUE mod) https://github.com/ruby/ruby/blob/trunk/object.c#L2720
     return Qtrue;
 }
 
+static VALUE
+rb_mod_const_source_location(int argc, VALUE *argv, VALUE mod)
+{
+    VALUE name, recur, loc = Qnil;
+    rb_encoding *enc;
+    const char *pbeg, *p, *path, *pend;
+    ID id;
+
+    rb_check_arity(argc, 1, 2);
+    name = argv[0];
+    recur = (argc == 1) ? Qtrue : argv[1];
+
+    if (SYMBOL_P(name)) {
+	if (!rb_is_const_sym(name)) goto wrong_name;
+	id = rb_check_id(&name);
+	if (!id) return Qnil;
+	return RTEST(recur) ? rb_const_source_location(mod, id) : rb_const_source_location_at(mod, id);
+    }
+
+    path = StringValuePtr(name);
+    enc = rb_enc_get(name);
+
+    if (!rb_enc_asciicompat(enc)) {
+	rb_raise(rb_eArgError, "invalid class path encoding (non ASCII)");
+    }
+
+    pbeg = p = path;
+    pend = path + RSTRING_LEN(name);
+
+    if (p >= pend || !*p) {
+      wrong_name:
+	rb_name_err_raise(wrong_constant_name, mod, name);
+    }
+
+    if (p + 2 < pend && p[0] == ':' && p[1] == ':') {
+	mod = rb_cObject;
+	p += 2;
+	pbeg = p;
+    }
+
+    while (p < pend) {
+	VALUE part;
+	long len, beglen;
+
+	while (p < pend && *p != ':') p++;
+
+	if (pbeg == p) goto wrong_name;
+
+	id = rb_check_id_cstr(pbeg, len = p-pbeg, enc);
+	beglen = pbeg-path;
+
+	if (p < pend && p[0] == ':') {
+	    if (p + 2 >= pend || p[1] != ':') goto wrong_name;
+	    p += 2;
+	    pbeg = p;
+	}
+
+	if (!id) {
+	    part = rb_str_subseq(name, beglen, len);
+	    OBJ_FREEZE(part);
+	    if (!rb_is_const_name(part)) {
+		name = part;
+		goto wrong_name;
+	    }
+	    else {
+		return Qnil;
+	    }
+	}
+	if (!rb_is_const_id(id)) {
+	    name = ID2SYM(id);
+	    goto wrong_name;
+	}
+	if (p < pend) {
+	    if (RTEST(recur)) {
+		mod = rb_const_get(mod, id);
+	    }
+	    else {
+		mod = rb_const_get_at(mod, id);
+	    }
+	    if (!RB_TYPE_P(mod, T_MODULE) && !RB_TYPE_P(mod, T_CLASS)) {
+		rb_raise(rb_eTypeError, "%"PRIsVALUE" does not refer to class/module",
+			 QUOTE(name));
+	    }
+	}
+	else {
+	    if (RTEST(recur)) {
+		loc = rb_const_source_location(mod, id);
+	    }
+	    else {
+		loc = rb_const_source_location_at(mod, id);
+	    }
+	    break;
+	}
+	recur = Qfalse;
+    }
+
+    return loc;
+}
+
 /*
  *  call-seq:
  *     obj.instance_variable_get(symbol)    -> obj
@@ -4249,6 +4348,7 @@ InitVM_Object(void) https://github.com/ruby/ruby/blob/trunk/object.c#L4348
     rb_define_method(rb_cModule, "const_get", rb_mod_const_get, -1);
     rb_define_method(rb_cModule, "const_set", rb_mod_const_set, 2);
     rb_define_method(rb_cModule, "const_defined?", rb_mod_const_defined, -1);
+    rb_define_method(rb_cModule, "const_source_location", rb_mod_const_source_location, -1);
     rb_define_private_method(rb_cModule, "remove_const",
 			     rb_mod_remove_const, 1); /* in variable.c */
     rb_define_method(rb_cModule, "const_missing",
diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb
index 3786eed..604edf5 100644
--- a/test/ruby/test_module.rb
+++ b/test/ruby/test_module.rb
@@ -2375,6 +2375,23 @@ class TestModule < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_module.rb#L2375
     }
   end
 
+  ConstLocation = [__FILE__, __LINE__]
+
+  def test_const_source_location
+    assert_equal(ConstLocation, self.class.const_source_location(:ConstLocation))
+    assert_equal(ConstLocation, self.class.const_source_location("ConstLocation"))
+    assert_equal(ConstLocation, Object.const_source_location("#{self.class.name}::ConstLocation"))
+    assert_raise(TypeError) {
+      self.class.const_source_location(nil)
+    }
+    assert_raise_with_message(NameError, /wrong constant name/) {
+      self.class.const_source_location("xxx")
+    }
+    assert_raise_with_message(TypeError, %r'does not refer to class/module') {
+      self.class.const_source_location("ConstLocation::FILE")
+    }
+  end
+
   private
 
   def assert_top_method_is_private(method)
diff --git a/variable.c b/variable.c
index 296e58a..9dc1a30 100644
--- a/variable.c
+++ b/variable.c
@@ -2483,6 +2483,62 @@ undefined_constant(VALUE mod, VALUE name) https://github.com/ruby/ruby/blob/trunk/variable.c#L2483
                       mod, name);
 }
 
+static VALUE
+rb_const_location_from(VALUE klass, ID id, int exclude, int recurse, int visibility)
+{
+    while (RTEST(klass)) {
+	rb_const_entry_t *ce;
+
+	while ((ce = rb_const_lookup(klass, id))) {
+	    if (visibility && RB_CONST_PRIVATE_P(ce)) {
+		return Qnil;
+	    }
+	    if (exclude && klass == rb_cObject) {
+		goto not_found;
+	    }
+	    if (NIL_P(ce->file)) return rb_ary_new();
+	    return rb_assoc_new(ce->file, INT2NUM(ce->line));
+	}
+	if (!recurse) break;
+	klass = RCLASS_SUPER(klass);
+    }
+
+  not_found:
+    return Qnil;
+}
+
+static VALUE
+rb_const_location(VALUE klass, ID id, int exclude, int recurse, int visibility)
+{
+    VALUE loc;
+
+    if (klass == rb_cObject) exclude = FALSE;
+    loc = rb_const_location_from(klass, id, exclude, recurse, visibility);
+    if (!NIL_P(loc)) return loc;
+    if (exclude) return loc;
+    if (BUILTIN_TYPE(klass) != T_MODULE) return loc;
+    /* search global const too, if klass is a module */
+    return rb_const_location_from(rb_cObject, id, FALSE, recurse, visibility);
+}
+
+VALUE
+rb_const_source_location_from(VALUE klass, ID id)
+{
+    return rb_const_location(klass, id, TRUE, TRUE, FALSE);
+}
+
+VALUE
+rb_const_source_location(VALUE klass, ID id)
+{
+    return rb_const_location(klass, id, FALSE, TRUE, FALSE);
+}
+
+VALUE
+rb_const_source_location_at(VALUE klass, ID id)
+{
+    return rb_const_location(klass, id, TRUE, FALSE, FALSE);
+}
+
 /*
  *  call-seq:
  *     remove_const(sym)   -> obj
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]