From f7bb117119750b4104e9dcf3350bdd50a6127a04 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 25 Mar 2021 16:24:27 -0700 Subject: [PATCH 1/2] Add constant location information to classes This patch teaches classes and modules about their own source location. So for example, with this patch you can do the following: ```ruby module A class B end end p A::B.source_location ``` This is equivalent to `A.const_source_location(:B)` except that you don't need a reference to `A` and you don't need to know the name of `B`. --- constant.h | 1 + internal/class.h | 4 ++++ object.c | 16 +++++++++++++++- test/ruby/test_module.rb | 7 +++++++ variable.c | 25 ++++++++++--------------- 5 files changed, 37 insertions(+), 16 deletions(-) diff --git a/constant.h b/constant.h index e0d36909e1..8bc68640c2 100644 --- a/constant.h +++ b/constant.h @@ -42,6 +42,7 @@ VALUE rb_mod_public_constant(int argc, const VALUE *argv, VALUE obj); VALUE rb_mod_deprecate_constant(int argc, const VALUE *argv, VALUE obj); void rb_free_const_table(struct rb_id_table *tbl); VALUE rb_const_source_location(VALUE, ID); +VALUE rb_const_source_location_of(VALUE); MJIT_SYMBOL_EXPORT_BEGIN int rb_autoloading_value(VALUE mod, ID id, VALUE *value, rb_const_flag_t *flag); diff --git a/internal/class.h b/internal/class.h index 6c03a31a4e..fd1102201f 100644 --- a/internal/class.h +++ b/internal/class.h @@ -14,6 +14,7 @@ #include "internal/serial.h" /* for rb_serial_t */ #include "ruby/internal/stdbool.h" /* for bool */ #include "ruby/intern.h" /* for rb_alloc_func_t */ +#include "constant.h" /* for rb_const_entry_t */ #include "ruby/ruby.h" /* for struct RBasic */ #ifdef RCLASS_SUPER @@ -42,6 +43,9 @@ struct rb_classext_struct { struct rb_id_table *cc_tbl; /* ID -> [[ci, cc1], cc2, ...] */ struct rb_subclass_entry *subclasses; struct rb_subclass_entry **parent_subclasses; + + rb_const_entry_t *location; + /** * In the case that this is an `ICLASS`, `module_subclasses` points to the link * in the module's `subclasses` list that indicates that the klass has been diff --git a/object.c b/object.c index 8e4c89b974..8d8bdced20 100644 --- a/object.c +++ b/object.c @@ -2788,7 +2788,8 @@ rb_mod_const_source_location(int argc, VALUE *argv, VALUE mod) const char *pbeg, *p, *path, *pend; ID id; - rb_check_arity(argc, 1, 2); + rb_check_arity(argc, 0, 2); + name = argv[0]; recur = (argc == 1) ? Qtrue : argv[1]; @@ -2882,6 +2883,18 @@ rb_mod_const_source_location(int argc, VALUE *argv, VALUE mod) UNREACHABLE_RETURN(Qundef); } +static VALUE +rb_mod_source_location(VALUE mod) +{ + rb_const_entry_t *ce = RCLASS_EXT(mod)->location; + if (ce) { + return rb_assoc_new(ce->file, INT2NUM(ce->line)); + } + else { + return Qnil; + } +} + /* * call-seq: * obj.instance_variable_get(symbol) -> obj @@ -4673,6 +4686,7 @@ InitVM_Object(void) 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_method(rb_cModule, "source_location", rb_mod_source_location, 0); 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 3cd2f04eff..5afff0c5ab 100644 --- a/test/ruby/test_module.rb +++ b/test/ruby/test_module.rb @@ -2869,6 +2869,8 @@ def foo; bar; end end ConstLocation = [__FILE__, __LINE__] + class ConstLocationClass; end + module ConstLocationModule; end def test_const_source_location assert_equal(ConstLocation, self.class.const_source_location(:ConstLocation)) @@ -2885,6 +2887,11 @@ def test_const_source_location } end + def test_source_location + assert_equal(self.class.const_source_location(:ConstLocationClass), ConstLocationClass.source_location) + assert_equal(self.class.const_source_location(:ConstLocationModule), ConstLocationModule.source_location) + end + module CloneTestM_simple C = 1 def self.m; C; end diff --git a/variable.c b/variable.c index 85ff35ba8c..2f31f1cd1c 100644 --- a/variable.c +++ b/variable.c @@ -2998,8 +2998,6 @@ set_namespace_path(VALUE named_namespace, VALUE namespace_path) void rb_const_set(VALUE klass, ID id, VALUE val) { - rb_const_entry_t *ce; - if (NIL_P(klass)) { rb_raise(rb_eTypeError, "no class/module to define constant %"PRIsVALUE"", QUOTE_ID(id)); @@ -3015,20 +3013,14 @@ rb_const_set(VALUE klass, ID id, VALUE val) { struct rb_id_table *tbl = RCLASS_CONST_TBL(klass); if (!tbl) { - RCLASS_CONST_TBL(klass) = tbl = rb_id_table_create(0); - rb_clear_constant_cache(); - ce = ZALLOC(rb_const_entry_t); - rb_id_table_insert(tbl, id, (VALUE)ce); - setup_const_entry(ce, klass, val, CONST_PUBLIC); - } - else { - struct autoload_const ac = { - .mod = klass, .id = id, - .value = val, .flag = CONST_PUBLIC, - /* fill the rest with 0 */ - }; - const_tbl_update(&ac); + RCLASS_CONST_TBL(klass) = rb_id_table_create(0); } + struct autoload_const ac = { + .mod = klass, .id = id, + .value = val, .flag = CONST_PUBLIC, + /* fill the rest with 0 */ + }; + const_tbl_update(&ac); } RB_VM_LOCK_LEAVE(); @@ -3129,6 +3121,9 @@ const_tbl_update(struct autoload_const *ac) ce = ZALLOC(rb_const_entry_t); rb_id_table_insert(tbl, id, (VALUE)ce); + if (RB_TYPE_P(val, T_MODULE) || RB_TYPE_P(val, T_CLASS)) { + RCLASS_EXT(val)->location = ce; + } setup_const_entry(ce, klass, val, visibility); } } -- 2.30.0