From 7e1d634adc3f3697b031b568c2920e7558d2a8c6 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 18 Jan 2023 18:45:18 +0100 Subject: [PATCH] Record the SourceSection in the constant for the module/class keywords * Fixes https://github.com/oracle/truffleruby/issues/2833 --- CHANGELOG.md | 1 + .../core/module/const_source_location_spec.rb | 7 ++++++- spec/ruby/fixtures/constants.rb | 3 ++- .../core/module/const_source_location_tags.txt | 1 - .../java/org/truffleruby/core/CoreLibrary.java | 4 ++-- .../org/truffleruby/core/klass/ClassNodes.java | 17 +++++++++++------ .../language/objects/DefineClassNode.java | 3 ++- .../org/truffleruby/parser/BodyTranslator.java | 3 +++ 8 files changed, 27 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b2c1a182d66..34e5be035b19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ Compatibility: * Fix `String#casecmp?` for empty strings of different encodings (#2826, @eregon). * Implement `Enumerable#compact` and `Enumerator::Lazy#compact` (#2733, @andrykonchin). * Implement `Array#intersect?` (#2831, @nirvdrum). +* Record the source location in the constant for the `module`/`class` keywords (#2833, @eregon). Performance: diff --git a/spec/ruby/core/module/const_source_location_spec.rb b/spec/ruby/core/module/const_source_location_spec.rb index 11a2e74756e6..145b069e2e47 100644 --- a/spec/ruby/core/module/const_source_location_spec.rb +++ b/spec/ruby/core/module/const_source_location_spec.rb @@ -67,6 +67,11 @@ end describe "with statically assigned constants" do + it "works for the module and class keywords" do + ConstantSpecs.const_source_location(:ModuleB).should == [@constants_fixture_path, ConstantSpecs::ModuleB::LINE] + ConstantSpecs.const_source_location(:ClassA).should == [@constants_fixture_path, ConstantSpecs::ClassA::LINE] + end + it "searches location path the immediate class or module first" do ConstantSpecs::ClassA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ClassA::CS_CONST10_LINE] ConstantSpecs::ModuleA.const_source_location(:CS_CONST10).should == [@constants_fixture_path, ConstantSpecs::ModuleA::CS_CONST10_LINE] @@ -133,7 +138,7 @@ it "calls #to_str to convert the given name to a String" do name = mock("ClassA") name.should_receive(:to_str).and_return("ClassA") - ConstantSpecs.const_source_location(name).should == [@constants_fixture_path, ConstantSpecs::ClassA::CS_CLASS_A_LINE] + ConstantSpecs.const_source_location(name).should == [@constants_fixture_path, ConstantSpecs::ClassA::LINE] end it "raises a TypeError if conversion to a String by calling #to_str fails" do diff --git a/spec/ruby/fixtures/constants.rb b/spec/ruby/fixtures/constants.rb index 47a8e87e5686..ffe45fb1f670 100644 --- a/spec/ruby/fixtures/constants.rb +++ b/spec/ruby/fixtures/constants.rb @@ -44,6 +44,7 @@ module ModuleA # Included in ParentA module ModuleB + LINE = __LINE__ - 1 CS_CONST10 = :const10_9 CS_CONST11 = :const11_2 CS_CONST12 = :const12_1 @@ -87,7 +88,7 @@ module ModuleIncludePrepended # are run. class ClassA - CS_CLASS_A_LINE = __LINE__ - 1 + LINE = __LINE__ - 1 CS_CONST10 = :const10_10 CS_CONST10_LINE = __LINE__ - 1 CS_CONST16 = :const16 diff --git a/spec/tags/core/module/const_source_location_tags.txt b/spec/tags/core/module/const_source_location_tags.txt index 0bbb9651a526..ce85a6cc2c33 100644 --- a/spec/tags/core/module/const_source_location_tags.txt +++ b/spec/tags/core/module/const_source_location_tags.txt @@ -1,3 +1,2 @@ -fails:Module#const_source_location calls #to_str to convert the given name to a String fails:Module#const_source_location autoload returns the autoload location while not resolved fails:Module#const_source_location returns updated location from const_set diff --git a/src/main/java/org/truffleruby/core/CoreLibrary.java b/src/main/java/org/truffleruby/core/CoreLibrary.java index 4f089ae54b43..94d4c0e8eed3 100644 --- a/src/main/java/org/truffleruby/core/CoreLibrary.java +++ b/src/main/java/org/truffleruby/core/CoreLibrary.java @@ -732,11 +732,11 @@ private RubyClass defineClass(String name) { } private RubyClass defineClass(RubyClass superclass, String name) { - return ClassNodes.createInitializedRubyClass(context, null, objectClass, superclass, name); + return ClassNodes.createInitializedRubyClass(context, null, objectClass, superclass, name, node); } private RubyClass defineClass(RubyModule lexicalParent, RubyClass superclass, String name) { - return ClassNodes.createInitializedRubyClass(context, null, lexicalParent, superclass, name); + return ClassNodes.createInitializedRubyClass(context, null, lexicalParent, superclass, name, node); } private RubyModule defineModule(String name) { diff --git a/src/main/java/org/truffleruby/core/klass/ClassNodes.java b/src/main/java/org/truffleruby/core/klass/ClassNodes.java index e41b351d4251..8ec2c15924f7 100644 --- a/src/main/java/org/truffleruby/core/klass/ClassNodes.java +++ b/src/main/java/org/truffleruby/core/klass/ClassNodes.java @@ -12,6 +12,7 @@ import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.nodes.Node; import org.truffleruby.RubyContext; import org.truffleruby.RubyLanguage; import org.truffleruby.annotations.CoreMethod; @@ -78,13 +79,14 @@ public static RubyClass createSingletonClassOfObject(RubyContext context, Source superclass, null, true, - attached); + attached, + null); return ensureItHasSingletonClassCreated(context, rubyClass); } @TruffleBoundary public static RubyClass createInitializedRubyClass(RubyContext context, SourceSection sourceSection, - RubyModule lexicalParent, RubyClass superclass, String name) { + RubyModule lexicalParent, RubyClass superclass, String name, Node currentNode) { assert superclass != null; final RubyClass rubyClass = createRubyClass( context, @@ -94,7 +96,8 @@ public static RubyClass createInitializedRubyClass(RubyContext context, SourceSe superclass, name, false, - null); + null, + currentNode); return ensureItHasSingletonClassCreated(context, rubyClass); } @@ -106,7 +109,8 @@ private static RubyClass createRubyClass(RubyContext context, RubyClass superclass, String name, boolean isSingleton, - RubyDynamicObject attached) { + RubyDynamicObject attached, + Node currentNode) { assert superclass != null; final RubyClass rubyClass = new RubyClass( classClass, @@ -119,7 +123,7 @@ private static RubyClass createRubyClass(RubyContext context, superclass); if (lexicalParent != null) { - rubyClass.fields.getAdoptedByLexicalParent(context, lexicalParent, name, null); + rubyClass.fields.getAdoptedByLexicalParent(context, lexicalParent, name, currentNode); } return rubyClass; @@ -182,7 +186,8 @@ private static RubyClass createSingletonClass(RubyContext context, RubyClass rub singletonSuperclass, null, true, - rubyClass); + rubyClass, + null); SharedObjects.propagate(context.getLanguageSlow(), rubyClass, metaClass); rubyClass.setMetaClass(metaClass); diff --git a/src/main/java/org/truffleruby/language/objects/DefineClassNode.java b/src/main/java/org/truffleruby/language/objects/DefineClassNode.java index 5c0aded51141..de61693bee7f 100644 --- a/src/main/java/org/truffleruby/language/objects/DefineClassNode.java +++ b/src/main/java/org/truffleruby/language/objects/DefineClassNode.java @@ -73,7 +73,8 @@ public Object execute(VirtualFrame frame) { getEncapsulatingSourceSection(), lexicalParentModule, superClass, - name); + name, + this); callInherited(frame, superClass, definedClass); } else { if (!(existing instanceof RubyClass)) { diff --git a/src/main/java/org/truffleruby/parser/BodyTranslator.java b/src/main/java/org/truffleruby/parser/BodyTranslator.java index 59dacac18667..c11141c530f2 100644 --- a/src/main/java/org/truffleruby/parser/BodyTranslator.java +++ b/src/main/java/org/truffleruby/parser/BodyTranslator.java @@ -1095,6 +1095,7 @@ public RubyNode visitClassNode(ClassParseNode node) { final RubyNode superClass = node.getSuperNode() != null ? node.getSuperNode().accept(this) : null; final DefineClassNode defineOrGetClass = new DefineClassNode(name, lexicalParent, superClass); + defineOrGetClass.unsafeSetSourceSection(sourceSection); final RubyNode ret = openModule( sourceSection, @@ -2199,6 +2200,7 @@ public RubyNode visitModuleNode(ModuleParseNode node) { RubyNode lexicalParent = translateCPath(sourceSection, node.getCPath()); final DefineModuleNode defineModuleNode = DefineModuleNodeGen.create(name, lexicalParent); + defineModuleNode.unsafeSetSourceSection(sourceSection); final RubyNode ret = openModule( sourceSection, @@ -2868,6 +2870,7 @@ public RubyNode visitSClassNode(SClassParseNode node) { final RubyNode receiverNode = node.getReceiverNode().accept(this); final SingletonClassNode singletonClassNode = SingletonClassNodeGen.create(receiverNode); + singletonClassNode.unsafeSetSourceSection(sourceSection); boolean dynamicConstantLookup = environment.isDynamicConstantLookup();