Skip to content

Commit ec4bbc5

Browse files
committed
[GR-19220] Add Refinement#refined_class (#3094)
PullRequest: truffleruby/3865
2 parents 3b72b32 + 40d8fb6 commit ec4bbc5

File tree

8 files changed

+133
-68
lines changed

8 files changed

+133
-68
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Compatibility:
2727
* Add optional `timeout` argument to `Thread::SizedsQueue#pop` (#3039, @itarato).
2828
* Handle `long long` and aliases in `Fiddle` (#3128, @eregon).
2929
* Add `Module#refinements` (#3039, @itarato).
30+
* Add `Refinement#refined_class` (#3039, @itarato).
3031

3132
Performance:
3233

spec/truffleruby.next-specs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,4 @@ spec/ruby/core/sizedqueue/pop_spec.rb
3131
spec/ruby/core/sizedqueue/shift_spec.rb
3232

3333
spec/ruby/core/module/refinements_spec.rb
34+
spec/ruby/core/refinement/refined_class_spec.rb

src/main/java/org/truffleruby/builtins/BuiltinsClasses.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@
7676
import org.truffleruby.core.method.UnboundMethodNodesFactory;
7777
import org.truffleruby.core.module.ModuleNodesBuiltins;
7878
import org.truffleruby.core.module.ModuleNodesFactory;
79+
import org.truffleruby.core.refinement.RefinementNodesBuiltins;
80+
import org.truffleruby.core.refinement.RefinementNodesFactory;
7981
import org.truffleruby.core.monitor.TruffleMonitorNodesBuiltins;
8082
import org.truffleruby.core.monitor.TruffleMonitorNodesFactory;
8183
import org.truffleruby.core.mutex.ConditionVariableNodesBuiltins;
@@ -223,6 +225,7 @@ public static void setupBuiltinsLazy(CoreMethodNodeManager coreManager) {
223225
RangeNodesBuiltins.setup(coreManager);
224226
ReadlineNodesBuiltins.setup(coreManager);
225227
ReadlineHistoryNodesBuiltins.setup(coreManager);
228+
RefinementNodesBuiltins.setup(coreManager);
226229
RegexpNodesBuiltins.setup(coreManager);
227230
SecureRandomizerNodesBuiltins.setup(coreManager);
228231
SizedQueueNodesBuiltins.setup(coreManager);
@@ -304,6 +307,7 @@ public static void setupBuiltinsLazyPrimitives(PrimitiveManager primitiveManager
304307
RangeNodesBuiltins.setupPrimitives(primitiveManager);
305308
ReadlineNodesBuiltins.setupPrimitives(primitiveManager);
306309
ReadlineHistoryNodesBuiltins.setupPrimitives(primitiveManager);
310+
RefinementNodesBuiltins.setupPrimitives(primitiveManager);
307311
RegexpNodesBuiltins.setupPrimitives(primitiveManager);
308312
SecureRandomizerNodesBuiltins.setupPrimitives(primitiveManager);
309313
SizedQueueNodesBuiltins.setupPrimitives(primitiveManager);
@@ -386,6 +390,7 @@ public static List<List<? extends NodeFactory<? extends RubyBaseNode>>> getCoreN
386390
RangeNodesFactory.getFactories(),
387391
ReadlineNodesFactory.getFactories(),
388392
ReadlineHistoryNodesFactory.getFactories(),
393+
RefinementNodesFactory.getFactories(),
389394
RegexpNodesFactory.getFactories(),
390395
SecureRandomizerNodesFactory.getFactories(),
391396
SizedQueueNodesFactory.getFactories(),

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

Lines changed: 0 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import com.oracle.truffle.api.strings.TruffleString;
3434
import com.oracle.truffle.api.strings.TruffleString.ByteIndexOfStringNode;
3535
import org.truffleruby.RubyContext;
36-
import org.truffleruby.RubyLanguage;
3736
import org.truffleruby.annotations.CoreMethod;
3837
import org.truffleruby.builtins.CoreMethodArrayArgumentsNode;
3938
import org.truffleruby.builtins.CoreMethodNode;
@@ -2466,73 +2465,6 @@ private RubyModule newRefinementModule(RubyModule namespace, RubyModule moduleTo
24662465

24672466
}
24682467

2469-
@Primitive(name = "refinement_import_methods")
2470-
public abstract static class ImportMethodsNode extends PrimitiveArrayArgumentsNode {
2471-
2472-
@TruffleBoundary
2473-
@Specialization
2474-
protected RubyModule importMethods(RubyModule refinement, RubyModule moduleToImportFrom) {
2475-
var firstNonRubyMethod = getFirstNonRubyMethodOrNull(moduleToImportFrom, getLanguage());
2476-
if (firstNonRubyMethod != null) {
2477-
throw new RaiseException(getContext(),
2478-
coreExceptions().argumentError(createErrorMessage(firstNonRubyMethod, moduleToImportFrom),
2479-
this));
2480-
}
2481-
2482-
importMethodsFromModuleToRefinement(moduleToImportFrom, refinement);
2483-
2484-
return refinement;
2485-
}
2486-
2487-
private String createErrorMessage(InternalMethod method, RubyModule module) {
2488-
return StringUtils.format("Can't import method which is not defined with Ruby code: %s#%s",
2489-
module.getName(), method.getName());
2490-
}
2491-
2492-
private void importMethodsFromModuleToRefinement(RubyModule module, RubyModule refinement) {
2493-
var declarationContext = createDeclarationContextWithRefinement(refinement);
2494-
for (InternalMethod methodToCopy : module.fields.getMethods()) {
2495-
var clonedMethod = cloneMethod(methodToCopy, declarationContext, refinement);
2496-
refinement.fields.addMethod(getContext(), this, clonedMethod);
2497-
}
2498-
}
2499-
2500-
private InternalMethod getFirstNonRubyMethodOrNull(RubyModule module, RubyLanguage language) {
2501-
for (InternalMethod method : module.fields.getMethods()) {
2502-
if (!method.isDefinedInRuby(language)) {
2503-
return method;
2504-
}
2505-
}
2506-
2507-
return null;
2508-
}
2509-
2510-
// Creates a declaration context which contains the refined methods from the given refinement
2511-
private DeclarationContext createDeclarationContextWithRefinement(RubyModule refinement) {
2512-
final Map<RubyModule, RubyModule[]> refinements = new HashMap<>();
2513-
refinements.put(refinement.fields.getRefinedModule(), new RubyModule[]{ refinement });
2514-
return new DeclarationContext(
2515-
Visibility.PUBLIC,
2516-
new FixedDefaultDefinee(refinement),
2517-
refinements);
2518-
}
2519-
2520-
private InternalMethod cloneMethod(InternalMethod method, DeclarationContext declarationContext,
2521-
RubyModule refinement) {
2522-
var clonedCallTarget = cloneCallTarget(method);
2523-
return method.withCallTargetAndDeclarationContextAndDeclarationModule(clonedCallTarget, declarationContext,
2524-
refinement);
2525-
}
2526-
2527-
private RootCallTarget cloneCallTarget(InternalMethod method) {
2528-
var rubyRootNode = (RubyRootNode) method.getCallTarget().getRootNode();
2529-
var clonedRootNode = rubyRootNode.cloneUninitialized();
2530-
2531-
return clonedRootNode.getCallTarget();
2532-
}
2533-
}
2534-
2535-
25362468
@GenerateUncached
25372469
@CoreMethod(names = "using", required = 1, visibility = Visibility.PRIVATE, alwaysInlined = true)
25382470
public abstract static class ModuleUsingNode extends UsingNode {
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. This
3+
* code is released under a tri EPL/GPL/LGPL license. You can use it,
4+
* redistribute it and/or modify it under the terms of the:
5+
*
6+
* Eclipse Public License version 2.0, or
7+
* GNU General Public License version 2, or
8+
* GNU Lesser General Public License version 2.1.
9+
*/
10+
package org.truffleruby.core.refinement;
11+
12+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
13+
import com.oracle.truffle.api.RootCallTarget;
14+
import com.oracle.truffle.api.dsl.Specialization;
15+
import org.truffleruby.RubyLanguage;
16+
import org.truffleruby.annotations.CoreMethod;
17+
import org.truffleruby.annotations.CoreModule;
18+
import org.truffleruby.annotations.Primitive;
19+
import org.truffleruby.annotations.Visibility;
20+
import org.truffleruby.builtins.CoreMethodArrayArgumentsNode;
21+
import org.truffleruby.builtins.PrimitiveArrayArgumentsNode;
22+
import org.truffleruby.core.module.RubyModule;
23+
import org.truffleruby.core.string.StringUtils;
24+
import org.truffleruby.language.RubyRootNode;
25+
import org.truffleruby.language.control.RaiseException;
26+
import org.truffleruby.language.methods.DeclarationContext;
27+
import org.truffleruby.language.methods.DeclarationContext.FixedDefaultDefinee;
28+
import org.truffleruby.language.methods.InternalMethod;
29+
30+
import java.util.HashMap;
31+
import java.util.Map;
32+
33+
@CoreModule(value = "Refinement", isClass = true)
34+
public abstract class RefinementNodes {
35+
36+
@Primitive(name = "refinement_import_methods")
37+
public abstract static class ImportMethodsNode extends PrimitiveArrayArgumentsNode {
38+
39+
@TruffleBoundary
40+
@Specialization
41+
protected RubyModule importMethods(RubyModule refinement, RubyModule moduleToImportFrom) {
42+
var firstNonRubyMethod = getFirstNonRubyMethodOrNull(moduleToImportFrom, getLanguage());
43+
if (firstNonRubyMethod != null) {
44+
throw new RaiseException(getContext(),
45+
coreExceptions().argumentError(createErrorMessage(firstNonRubyMethod, moduleToImportFrom),
46+
this));
47+
}
48+
49+
importMethodsFromModuleToRefinement(moduleToImportFrom, refinement);
50+
51+
return refinement;
52+
}
53+
54+
private String createErrorMessage(InternalMethod method, RubyModule module) {
55+
return StringUtils.format("Can't import method which is not defined with Ruby code: %s#%s",
56+
module.getName(), method.getName());
57+
}
58+
59+
private void importMethodsFromModuleToRefinement(RubyModule module, RubyModule refinement) {
60+
var declarationContext = createDeclarationContextWithRefinement(refinement);
61+
for (InternalMethod methodToCopy : module.fields.getMethods()) {
62+
var clonedMethod = cloneMethod(methodToCopy, declarationContext, refinement);
63+
refinement.fields.addMethod(getContext(), this, clonedMethod);
64+
}
65+
}
66+
67+
private InternalMethod getFirstNonRubyMethodOrNull(RubyModule module, RubyLanguage language) {
68+
for (InternalMethod method : module.fields.getMethods()) {
69+
if (!method.isDefinedInRuby(language)) {
70+
return method;
71+
}
72+
}
73+
74+
return null;
75+
}
76+
77+
// Creates a declaration context which contains the refined methods from the given refinement
78+
private DeclarationContext createDeclarationContextWithRefinement(RubyModule refinement) {
79+
final Map<RubyModule, RubyModule[]> refinements = new HashMap<>();
80+
refinements.put(refinement.fields.getRefinedModule(), new RubyModule[]{ refinement });
81+
return new DeclarationContext(
82+
Visibility.PUBLIC,
83+
new FixedDefaultDefinee(refinement),
84+
refinements);
85+
}
86+
87+
private InternalMethod cloneMethod(InternalMethod method, DeclarationContext declarationContext,
88+
RubyModule refinement) {
89+
var clonedCallTarget = cloneCallTarget(method);
90+
return method.withCallTargetAndDeclarationContextAndDeclarationModule(clonedCallTarget, declarationContext,
91+
refinement);
92+
}
93+
94+
private RootCallTarget cloneCallTarget(InternalMethod method) {
95+
var rubyRootNode = (RubyRootNode) method.getCallTarget().getRootNode();
96+
var clonedRootNode = rubyRootNode.cloneUninitialized();
97+
98+
return clonedRootNode.getCallTarget();
99+
}
100+
}
101+
102+
@CoreMethod(names = "refined_class")
103+
public abstract static class RefinedClassNode extends CoreMethodArrayArgumentsNode {
104+
105+
@Specialization
106+
protected RubyModule refinedClass(RubyModule refinement) {
107+
return refinement.fields.getRefinedModule();
108+
}
109+
}
110+
}

test/mri/excludes/TestRefinement.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@
1111
exclude :test_ancestors, "[ruby-core:86949] [Bug #14744]."
1212
exclude :test_import_methods, "NoMethodError: undefined method `bar' for #<TestRefinement::TestImport::A:0x17bee78>"
1313
exclude :test_refinements, "TruffleRuby does not guarantee refinement list ordering"
14+
exclude :test_refined_class, "TruffleRuby does not guarantee refinement list ordering"

test/mri/excludes/TestRipper/Sexp.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
exclude :test_pattern_matching_427, "needs investigation"
33
exclude :test_pattern_matching_439, "needs investigation"
44
exclude :test_params_mlhs, "transient"
5+
exclude :test_pattern_matching_216, " case 0; in *a,b,c; end ."

test/mri/tests/ruby/test_refinement.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1850,6 +1850,20 @@ def test_refinements
18501850
assert_equal([int_refinement, str_refinement], m.refinements)
18511851
end
18521852

1853+
def test_refined_class
1854+
refinements = Module.new {
1855+
refine Integer do
1856+
int_refinement = self
1857+
end
1858+
1859+
refine String do
1860+
str_refinement = self
1861+
end
1862+
}.refinements
1863+
assert_equal(Integer, refinements[0].refined_class)
1864+
assert_equal(String, refinements[1].refined_class)
1865+
end
1866+
18531867
def test_warn_setconst_in_refinmenet
18541868
bug10103 = '[ruby-core:64143] [Bug #10103]'
18551869
warnings = [

0 commit comments

Comments
 (0)