Skip to content

Commit 7d387cd

Browse files
committed
[GR-28932] Add support for always-inlined builtins and always inline #__send__, #send and #public_send
PullRequest: truffleruby/2392
2 parents 7bdfdec + c11bee1 commit 7d387cd

29 files changed

+412
-149
lines changed

spec/ruby/core/kernel/public_send_spec.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,5 +104,13 @@ def bar
104104
}.should raise_error(NoMethodError)
105105
end
106106

107+
it "includes `public_send` in the backtrace when passed not enough arguments" do
108+
-> { public_send() }.should raise_error(ArgumentError) { |e| e.backtrace[0].should.include?("`public_send'") }
109+
end
110+
111+
it "includes `public_send` in the backtrace when passed a single incorrect argument" do
112+
-> { public_send(Object.new) }.should raise_error(TypeError) { |e| e.backtrace[0].should.include?("`public_send'") }
113+
end
114+
107115
it_behaves_like :basicobject_send, :public_send
108116
end

spec/truffle/always_inlined_spec.rb

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. This
2+
# code is released under a tri EPL/GPL/LGPL license. You can use it,
3+
# redistribute it and/or modify it under the terms of the:
4+
#
5+
# Eclipse Public License version 2.0, or
6+
# GNU General Public License version 2, or
7+
# GNU Lesser General Public License version 2.1.
8+
9+
require_relative '../ruby/spec_helper'
10+
11+
# This spec can also run on CRuby, for comparison
12+
describe "Always-inlined core methods" do
13+
describe "are part of the backtrace if the error happens inside that core method" do
14+
it "for #public_send" do
15+
-> {
16+
public_send()
17+
}.should raise_error(ArgumentError) { |e| e.backtrace_locations[0].label.should == 'public_send' }
18+
-> {
19+
public_send(Object.new)
20+
}.should raise_error(TypeError) { |e| e.backtrace_locations[0].label.should == 'public_send' }
21+
end
22+
23+
guard -> { RUBY_ENGINE != "ruby" } do
24+
it "for #send" do
25+
-> {
26+
send()
27+
}.should raise_error(ArgumentError) { |e| e.backtrace_locations[0].label.should == '__send__' }
28+
-> {
29+
send(Object.new)
30+
}.should raise_error(TypeError) { |e| e.backtrace_locations[0].label.should == '__send__' }
31+
end
32+
33+
it "for #__send__" do
34+
-> {
35+
__send__()
36+
}.should raise_error(ArgumentError) { |e| e.backtrace_locations[0].label.should == '__send__' }
37+
-> {
38+
__send__(Object.new)
39+
}.should raise_error(TypeError) { |e| e.backtrace_locations[0].label.should == '__send__' }
40+
end
41+
end
42+
end
43+
44+
describe "are not part of the backtrace if the error happens in a different method" do
45+
it "for #public_send" do
46+
-> {
47+
public_send(:yield_self) { raise "foo" }
48+
}.should raise_error(RuntimeError, "foo") { |e|
49+
e.backtrace_locations[0].label.should.start_with?('block (5 levels)')
50+
e.backtrace_locations[1].label.should == 'yield_self'
51+
if RUBY_ENGINE == 'ruby'
52+
e.backtrace_locations[2].label.should == 'public_send'
53+
else
54+
e.backtrace_locations[2].label.should.start_with?('block (4 levels)')
55+
end
56+
}
57+
end
58+
59+
it "for #send" do
60+
-> {
61+
send(:yield_self) { raise "foo" }
62+
}.should raise_error(RuntimeError, "foo") { |e|
63+
e.backtrace_locations[0].label.should.start_with?('block (5 levels)')
64+
e.backtrace_locations[1].label.should == 'yield_self'
65+
e.backtrace_locations[2].label.should.start_with?('block (4 levels)')
66+
}
67+
end
68+
69+
it "for #__send__" do
70+
-> {
71+
__send__(:yield_self) { raise "foo" }
72+
}.should raise_error(RuntimeError, "foo") { |e|
73+
e.backtrace_locations[0].label.should.start_with?('block (5 levels)')
74+
e.backtrace_locations[1].label.should == 'yield_self'
75+
e.backtrace_locations[2].label.should.start_with?('block (4 levels)')
76+
}
77+
end
78+
end
79+
end

spec/truffle/binding/of_caller_spec.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ def binding_of_caller
1414
Truffle::Binding.of_caller
1515
end
1616

17+
def binding_of_caller_through_send
18+
Truffle::Binding.send(:of_caller)
19+
end
20+
1721
#it "returns nil if there is no caller"
1822
#end
1923

@@ -34,7 +38,7 @@ def binding_of_caller
3438

3539
it "works through #send" do
3640
x = 14 # rubocop:disable Lint/UselessAssignment
37-
Truffle::Binding.send(:of_caller).local_variable_get(:x).should == 14
41+
binding_of_caller_through_send.local_variable_get(:x).should == 14
3842
end
3943

4044
end

spec/truffle/boot/source_of_caller_spec.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ def source_of_caller
1414
Truffle::Boot.source_of_caller
1515
end
1616

17+
def source_of_caller_through_send
18+
Truffle::Boot.send(:source_of_caller)
19+
end
20+
1721
#it "returns nil if there is no caller"
1822
#end
1923

@@ -26,7 +30,7 @@ def source_of_caller
2630
end
2731

2832
it "works through #send" do
29-
Truffle::Boot.send(:source_of_caller).should == __FILE__
33+
source_of_caller_through_send.should == __FILE__
3034
end
3135

3236
end

src/annotations/java/org/truffleruby/builtins/CoreMethod.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
* both a singleton method and instance method. */
3939
boolean isModuleFunction() default false;
4040

41+
boolean alwaysInlined() default false;
42+
4143
boolean needsSelf() default true;
4244

4345
int required() default 0;

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,22 @@ public Object send(Node currentNode, Object object, String methodName, Object...
460460
}
461461
}
462462

463+
@TruffleBoundary
464+
public static Object indirectCallWithCallNode(Node currentNode, RootCallTarget callTarget,
465+
Object... frameArguments) {
466+
if (currentNode.isAdoptable()) {
467+
final EncapsulatingNodeReference callNodeRef = EncapsulatingNodeReference.getCurrent();
468+
final Node prev = callNodeRef.set(currentNode);
469+
try {
470+
return IndirectCallNode.getUncached().call(callTarget, frameArguments);
471+
} finally {
472+
callNodeRef.set(prev);
473+
}
474+
} else {
475+
return IndirectCallNode.getUncached().call(callTarget, frameArguments);
476+
}
477+
}
478+
463479
public void finalizeContext() {
464480
if (!initialized) {
465481
// The RubyContext will be finalized and disposed if patching fails (potentially for

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@
146146
import org.truffleruby.interop.InteropNodesFactory;
147147
import org.truffleruby.interop.PolyglotNodesBuiltins;
148148
import org.truffleruby.interop.PolyglotNodesFactory;
149-
import org.truffleruby.language.RubyNode;
149+
import org.truffleruby.language.RubyBaseNode;
150150
import org.truffleruby.language.TruffleBootNodesBuiltins;
151151
import org.truffleruby.language.TruffleBootNodesFactory;
152152
import org.truffleruby.stdlib.CoverageNodesBuiltins;
@@ -321,7 +321,7 @@ public static void setupBuiltinsLazyPrimitives(PrimitiveManager primitiveManager
321321
}
322322

323323
// Sorted alphabetically to avoid duplicates
324-
public static List<List<? extends NodeFactory<? extends RubyNode>>> getCoreNodeFactories() {
324+
public static List<List<? extends NodeFactory<? extends RubyBaseNode>>> getCoreNodeFactories() {
325325
return Arrays.asList(
326326
ArrayIndexNodesFactory.getFactories(),
327327
ArrayNodesFactory.getFactories(),

0 commit comments

Comments
 (0)