Skip to content

Commit 9c0f7ce

Browse files
committed
[GR-26849] Raise a RuntimeError when calling a method needing caller data directly from a foreign language
* Instead of an internal error later on.
1 parent a50c8e5 commit 9c0f7ce

File tree

4 files changed

+86
-4
lines changed

4 files changed

+86
-4
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) 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+
#include "ruby.h"
11+
#include "ruby/thread.h"
12+
#include "rubyspec.h"
13+
14+
#ifdef __cplusplus
15+
extern "C" {
16+
#endif
17+
18+
static VALUE call_binding(VALUE self) {
19+
return rb_tr_wrap(polyglot_invoke(rb_tr_unwrap(self), "binding"));
20+
}
21+
22+
static VALUE call_binding_rb_funcall(VALUE self) {
23+
return rb_funcall(self, rb_intern("binding"), 0);
24+
}
25+
26+
void Init_truffleruby_foreign_caller_spec(void) {
27+
VALUE cls = rb_define_class("CApiTruffleRubyForeignCallerSpecs", rb_cObject);
28+
rb_define_method(cls, "call_binding", call_binding, 0);
29+
rb_define_method(cls, "call_binding_rb_funcall", call_binding_rb_funcall, 0);
30+
}
31+
32+
#ifdef __cplusplus
33+
}
34+
#endif
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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/optional/capi/spec_helper'
10+
11+
load_extension("truffleruby_foreign_caller")
12+
13+
describe "Calling a method needing the caller frame" do
14+
before :each do
15+
@s = CApiTruffleRubyForeignCallerSpecs.new
16+
end
17+
18+
it "directly from C code raises a RuntimeError" do
19+
-> {
20+
@s.call_binding
21+
}.should raise_error(RuntimeError, 'Cannot call Ruby method which needs caller data directly in a foreign language')
22+
end
23+
24+
it "using rb_funcall() yields the Binding of rb_funcall()" do
25+
caller_variable = nil
26+
binding = @s.call_binding_rb_funcall
27+
binding.should be_kind_of(Binding)
28+
29+
# On CRuby it would instead return the Binding of the caller Ruby frame
30+
binding.local_variables.should.include?(:args)
31+
binding.local_variables.should_not.include?(:caller_variable)
32+
33+
caller_variable.should == nil
34+
end
35+
end

src/main/java/org/truffleruby/language/CallStackManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ public R visitFrame(FrameInstance frameInstance) {
192192
}
193193
}
194194

195-
private boolean isRubyFrame(Frame frame) {
195+
public static boolean isRubyFrame(Frame frame) {
196196
return tryGetMethod(frame) != null;
197197
}
198198

src/main/java/org/truffleruby/language/arguments/ReadCallerDataNode.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import com.oracle.truffle.api.CompilerDirectives;
1313
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
14+
import com.oracle.truffle.api.Truffle;
1415
import com.oracle.truffle.api.frame.Frame;
1516
import com.oracle.truffle.api.frame.MaterializedFrame;
1617
import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
@@ -19,9 +20,11 @@
1920
import com.oracle.truffle.api.nodes.Node;
2021
import com.oracle.truffle.api.profiles.ConditionProfile;
2122

23+
import org.truffleruby.language.CallStackManager;
2224
import org.truffleruby.language.FrameAndVariablesSendingNode;
2325
import org.truffleruby.language.NotOptimizedWarningNode;
2426
import org.truffleruby.language.RubyContextNode;
27+
import org.truffleruby.language.control.RaiseException;
2528

2629
public abstract class ReadCallerDataNode extends RubyContextNode {
2730

@@ -47,10 +50,20 @@ private Object getCallerData() {
4750
// we don't want to deoptimize this CallTarget on every call.
4851
getNotOptimizedNode().warn("Unoptimized reading of caller data.");
4952
}
50-
MaterializedFrame callerFrame = getContext()
51-
.getCallStack()
52-
.getCallerFrame(FrameAccess.MATERIALIZE)
53+
54+
final MaterializedFrame callerFrame = Truffle
55+
.getRuntime()
56+
.getCallerFrame()
57+
.getFrame(FrameAccess.MATERIALIZE)
5358
.materialize();
59+
if (!CallStackManager.isRubyFrame(callerFrame)) {
60+
throw new RaiseException(
61+
getContext(),
62+
coreExceptions().runtimeError(
63+
"Cannot call Ruby method which needs caller data directly in a foreign language",
64+
this));
65+
}
66+
5467
return getDataFromFrame(callerFrame);
5568
}
5669

0 commit comments

Comments
 (0)