Skip to content

Commit 31df24e

Browse files
committed
[GR-28711] Add method assumptions map to modules
PullRequest: truffleruby/2442
2 parents df459e3 + 9c72d34 commit 31df24e

File tree

9 files changed

+408
-47
lines changed

9 files changed

+408
-47
lines changed

spec/ruby/core/module/include_spec.rb

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,124 @@ class C
234234
remove_const :C
235235
end
236236
end
237+
238+
it "updates the method when an included module is updated" do
239+
a_class = Class.new do
240+
def foo
241+
'a'
242+
end
243+
end
244+
245+
m_module = Module.new
246+
247+
b_class = Class.new(a_class) do
248+
include m_module
249+
end
250+
251+
b = b_class.new
252+
253+
foo = -> { b.foo }
254+
255+
foo.call.should == 'a'
256+
257+
m_module.module_eval do
258+
def foo
259+
'm'
260+
end
261+
end
262+
263+
foo.call.should == 'm'
264+
end
265+
266+
it "updates the method when a nested included module is updated" do
267+
a_class = Class.new do
268+
def foo
269+
'a'
270+
end
271+
end
272+
273+
n_module = Module.new
274+
275+
m_module = Module.new do
276+
include n_module
277+
end
278+
279+
b_class = Class.new(a_class) do
280+
include m_module
281+
end
282+
283+
b = b_class.new
284+
285+
foo = -> { b.foo }
286+
287+
foo.call.should == 'a'
288+
289+
n_module.module_eval do
290+
def foo
291+
'n'
292+
end
293+
end
294+
295+
foo.call.should == 'n'
296+
end
297+
298+
it "updates the method when a new module is included" do
299+
a_class = Class.new do
300+
def foo
301+
'a'
302+
end
303+
end
304+
305+
m_module = Module.new do
306+
def foo
307+
'm'
308+
end
309+
end
310+
311+
b_class = Class.new(a_class)
312+
b = b_class.new
313+
314+
foo = -> { b.foo }
315+
316+
foo.call.should == 'a'
317+
318+
b_class.class_eval do
319+
include m_module
320+
end
321+
322+
foo.call.should == 'm'
323+
end
324+
325+
it "updates the method when a new module with nested module is included" do
326+
a_class = Class.new do
327+
def foo
328+
'a'
329+
end
330+
end
331+
332+
n_module = Module.new do
333+
def foo
334+
'n'
335+
end
336+
end
337+
338+
m_module = Module.new do
339+
include n_module
340+
end
341+
342+
b_class = Class.new(a_class)
343+
b = b_class.new
344+
345+
foo = -> { b.foo }
346+
347+
foo.call.should == 'a'
348+
349+
b_class.class_eval do
350+
include m_module
351+
end
352+
353+
foo.call.should == 'n'
354+
end
237355
end
238356

239357
describe "Module#include?" do

spec/ruby/core/module/prepend_spec.rb

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,77 @@ def self.prepend_features(mod)
3636
ScratchPad.recorded.should == [ [ m3, c], [ m2, c ], [ m, c ] ]
3737
end
3838

39+
it "updates the method when a module is prepended" do
40+
m_module = Module.new do
41+
def foo
42+
"m"
43+
end
44+
end
45+
a_class = Class.new do
46+
def foo
47+
'a'
48+
end
49+
end
50+
b_class = Class.new(a_class)
51+
b = b_class.new
52+
foo = -> { b.foo }
53+
foo.call.should == 'a'
54+
a_class.class_eval do
55+
prepend m_module
56+
end
57+
foo.call.should == 'm'
58+
end
59+
60+
it "updates the method when a prepended module is updated" do
61+
m_module = Module.new
62+
a_class = Class.new do
63+
prepend m_module
64+
def foo
65+
'a'
66+
end
67+
end
68+
b_class = Class.new(a_class)
69+
b = b_class.new
70+
foo = -> { b.foo }
71+
foo.call.should == 'a'
72+
m_module.module_eval do
73+
def foo
74+
"m"
75+
end
76+
end
77+
foo.call.should == 'm'
78+
end
79+
80+
it "updates the method when a new module with an included module is prepended" do
81+
a_class = Class.new do
82+
def foo
83+
'a'
84+
end
85+
end
86+
87+
n_module = Module.new do
88+
def foo
89+
'n'
90+
end
91+
end
92+
93+
m_module = Module.new do
94+
include n_module
95+
end
96+
97+
b_class = Class.new(a_class)
98+
b = b_class.new
99+
foo = -> { b.foo }
100+
101+
foo.call.should == 'a'
102+
103+
a_class.class_eval do
104+
prepend m_module
105+
end
106+
107+
foo.call.should == 'n'
108+
end
109+
39110
it "raises a TypeError when the argument is not a Module" do
40111
-> { ModuleSpecs::Basic.prepend(Class.new) }.should raise_error(TypeError)
41112
end

spec/ruby/core/module/remove_method_spec.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,28 @@ def method_to_remove; 2; end
4343
x.method_to_remove.should == 1
4444
end
4545

46+
it "updates the method implementation" do
47+
m_module = Module.new do
48+
def foo
49+
'm'
50+
end
51+
end
52+
53+
a_class = Class.new do
54+
include m_module
55+
56+
def foo
57+
'a'
58+
end
59+
end
60+
61+
a = a_class.new
62+
foo = -> { a.foo }
63+
foo.call.should == 'a'
64+
a_class.remove_method(:foo)
65+
foo.call.should == 'm'
66+
end
67+
4668
it "removes multiple methods with 1 call" do
4769
klass = Class.new do
4870
def method_to_remove_1; 1; end
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright (c) 2021, 2021 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.method;
11+
12+
import org.truffleruby.language.methods.InternalMethod;
13+
14+
import com.oracle.truffle.api.Assumption;
15+
import com.oracle.truffle.api.Truffle;
16+
17+
public final class MethodEntry {
18+
19+
private final Assumption assumption;
20+
private final InternalMethod method;
21+
22+
public MethodEntry(InternalMethod method) {
23+
assert method != null;
24+
this.assumption = Truffle.getRuntime().createAssumption();
25+
this.method = method;
26+
}
27+
28+
public MethodEntry() {
29+
this.assumption = Truffle.getRuntime().createAssumption();
30+
this.method = null;
31+
}
32+
33+
public MethodEntry withNewAssumption() {
34+
if (method != null) {
35+
return new MethodEntry(method);
36+
} else {
37+
return new MethodEntry();
38+
}
39+
}
40+
41+
public Assumption getAssumption() {
42+
return assumption;
43+
}
44+
45+
public InternalMethod getMethod() {
46+
return method;
47+
}
48+
49+
public void invalidate(String message) {
50+
assumption.invalidate(message);
51+
}
52+
53+
}

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@
99
*/
1010
package org.truffleruby.core.module;
1111

12-
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
13-
1412
/** Either an IncludedModule, a RubyClass or a RubyModule. Private interface, do not use outside RubyModule. */
1513
public abstract class ModuleChain {
1614

17-
@CompilationFinal protected ModuleChain parentModule;
15+
protected ModuleChain parentModule;
1816

1917
public ModuleChain(ModuleChain parentModule) {
2018
this.parentModule = parentModule;

0 commit comments

Comments
 (0)