Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Raising a RuntimeError when calling Kernel#binding from a C/non-ruby frame. #3449

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

goyox86
Copy link
Contributor

@goyox86 goyox86 commented Feb 8, 2024

Ruby 3.2 compatibility.

Addresses "Kernel#binding raises RuntimeError if called from a non-Ruby frame
(such as a method defined in C)." defined in #3039

CRuby: https://bugs.ruby-lang.org/issues/18487

Used the same stack walking as seen in https://github.com/Shopify/truffleruby/blob/90bfaa00401778f11849dbca05eb1bd008271c1b/src/main/java/org/truffleruby/language/arguments/ReadCallerVariablesNode.java#L46

@oracle-contributor-agreement oracle-contributor-agreement bot added the OCA Verified All contributors have signed the Oracle Contributor Agreement. label Feb 8, 2024
@@ -334,6 +337,18 @@ RubyBinding binding(Frame callerFrame, Object self, Object[] rubyArgs, RootCallT
@Cached(
value = "getAdoptedNode(this).getEncapsulatingSourceSection()",
allowUncached = true, neverDefault = false) SourceSection sourceSection) {

final MaterializedFrame materializedCallerFrame = Truffle.getRuntime()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what the materializedCallerFrame looks like in the debugger:
image

Compared to when called from Ruby:
image

@@ -334,6 +337,18 @@ RubyBinding binding(Frame callerFrame, Object self, Object[] rubyArgs, RootCallT
@Cached(
value = "getAdoptedNode(this).getEncapsulatingSourceSection()",
allowUncached = true, neverDefault = false) SourceSection sourceSection) {

final MaterializedFrame materializedCallerFrame = Truffle.getRuntime()
.iterateFrames(f -> f.getFrame(FrameInstance.FrameAccess.MATERIALIZE).materialize(), 1);
Copy link
Member

@eregon eregon Feb 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely can't do iterateFrames in Kernel#binding.
Kernel#binding is very well optimized currently and iterateFrames would completely ruin its performance.

One way to detect C extension methods quickly is #3436 (comment).
But also I don't think this matters. We do have currently a Ruby frame for C extension methods, so it's only fair to return a Binding for that case.

The aim of this new exception is to not give an empty/unconnected binding for situations where there isn't a real binding.
For instance method(:binding).call is such a case, because Method#call is implemented in C/Java and so there is no way to get a proper Binding for Method#call, hence there it's clearer to raise an exception.
As mentioned in #3436 (comment), we should raise the new exception for method(:binding).call and add a spec for that.
And let's leave C extension methods (& rb_funcall) alone, i.e. let them return a Binding as they do, because there is a real Ruby frame in there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to try #3436 (comment) in this PR.
I tried it locally and test fast passed so maybe it's OK, it would need a full CI run to know better.
It's too risky for 24.0 but we can always merge it for 24.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
OCA Verified All contributors have signed the Oracle Contributor Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants