Skip to content

Commit cd498f7

Browse files
andrykonchineregon
authored andcommitted
[GR-19220] Add Module#const_added
PullRequest: truffleruby/3924
2 parents 5403758 + dc4e588 commit cd498f7

File tree

6 files changed

+65
-1
lines changed

6 files changed

+65
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Compatibility:
3636
* Add `timeout` argument to `Thread::SizedQueue#push` (#3039, @itarato).
3737
* Add `rb_syserr_new` function (@rwstauner).
3838
* Add `Enumerator#product` (#3039, @itarato).
39+
* Add `Module#const_added` (#3039, @itarato).
3940

4041
Performance:
4142

spec/ruby/core/module/const_added_spec.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,5 +121,40 @@ class SubClass
121121

122122
ScratchPad.recorded.should == [line + 2, line + 4, line + 7, line + 11]
123123
end
124+
125+
it "is called when the constant is already assigned a value" do
126+
ScratchPad.record []
127+
128+
mod = Module.new do
129+
def self.const_added(name)
130+
ScratchPad.record const_get(name)
131+
end
132+
end
133+
134+
mod.module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
135+
TEST = 123
136+
RUBY
137+
138+
ScratchPad.recorded.should == 123
139+
end
140+
141+
it "records re-definition of existing constants" do
142+
ScratchPad.record []
143+
144+
mod = Module.new do
145+
def self.const_added(name)
146+
ScratchPad << const_get(name)
147+
end
148+
end
149+
150+
-> {
151+
mod.module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
152+
TEST = 123
153+
TEST = 456
154+
RUBY
155+
}.should complain(/warning: already initialized constant .+::TEST/)
156+
157+
ScratchPad.recorded.should == [123, 456]
158+
end
124159
end
125160
end

spec/truffleruby.next-specs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ spec/ruby/core/sizedqueue/append_spec.rb
3333
spec/ruby/core/sizedqueue/enq_spec.rb
3434
spec/ruby/core/sizedqueue/push_spec.rb
3535

36+
spec/ruby/core/module/const_added_spec.rb
3637
spec/ruby/core/module/refinements_spec.rb
3738
spec/ruby/core/module/undefined_instance_methods_spec.rb
3839
spec/ruby/core/refinement/refined_class_spec.rb

src/main/java/org/truffleruby/RubyContext.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,11 @@ public final class RubyContext {
159159
public long nativeArgv = 0L;
160160
public long nativeArgvLength = -1L;
161161

162+
// Whether or not a custom Module.const_added callback was defined.
163+
// Optimization to avoid calling a default (empty) callback if there
164+
// are no user-defined callbacks.
165+
private volatile boolean constAddedDefined = false;
166+
162167
private final AssumedValue<Boolean> warningCategoryDeprecated;
163168
private final AssumedValue<Boolean> warningCategoryExperimental;
164169

@@ -444,7 +449,7 @@ private TruffleNFIPlatform createNativePlatform() {
444449

445450
@TruffleBoundary
446451
public static Object send(Node currentNode, Object receiver, String methodName, Object... arguments) {
447-
if (currentNode.isAdoptable()) {
452+
if (currentNode != null && currentNode.isAdoptable()) {
448453
final EncapsulatingNodeReference callNodeRef = EncapsulatingNodeReference.getCurrent();
449454
final Node prev = callNodeRef.set(currentNode);
450455
try {
@@ -779,4 +784,12 @@ public void initializeMainScriptName(String mainScriptName) {
779784
public ImmutableRubyString getMainScriptName() {
780785
return this.mainScriptName;
781786
}
787+
788+
public boolean isConstAddedEverDefined() {
789+
return constAddedDefined;
790+
}
791+
792+
public void constAddedIsDefined() {
793+
constAddedDefined = true;
794+
}
782795
}

src/main/java/org/truffleruby/core/module/ModuleFields.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ public void setAutoloadConstant(RubyContext context, Node currentNode, String na
457457
context.getFeatureLoader().addAutoload(autoloadConstant);
458458
}
459459

460+
@TruffleBoundary
460461
private RubyConstant setConstantInternal(RubyContext context, Node currentNode, String name, Object value,
461462
boolean autoload) {
462463
checkFrozen(context, currentNode);
@@ -494,6 +495,11 @@ private RubyConstant setConstantInternal(RubyContext context, Node currentNode,
494495
invalidateConstantIncludedBy(name);
495496
}
496497

498+
if (context.isConstAddedEverDefined()) {
499+
final RubySymbol nameSymbol = context.getLanguageSlow().getSymbol(name);
500+
RubyContext.send(currentNode, rubyModule, "const_added", nameSymbol);
501+
}
502+
497503
return autoload ? newConstant : previous;
498504
}
499505

@@ -571,6 +577,11 @@ public void addMethod(RubyContext context, Node currentNode, InternalMethod meth
571577
}
572578
}
573579
}
580+
581+
// track if ever a custom Module.const_add callback is defined and ignore a default one
582+
if (context.getCoreLibrary().isLoaded() && method.getName().equals("const_added")) {
583+
RubyLanguage.getCurrentContext().constAddedIsDefined();
584+
}
574585
}
575586

576587
@TruffleBoundary

src/main/ruby/truffleruby/core/module.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ def include?(mod)
6161
ancestors.any? { |m| Primitive.equal?(mod, m) }
6262
end
6363

64+
private def const_added(name)
65+
end
66+
6467
private def method_added(name)
6568
end
6669

0 commit comments

Comments
 (0)