https://bugs.ruby-lang.org/https://bugs.ruby-lang.org/favicon.ico?17113305112022-05-16T10:40:00ZRuby Issue Tracking SystemRuby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=976052022-05-16T10:40:00ZEregon (Benoit Daloze)
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Rejected</i></li></ul><p>rb_eval_string() is used inside C code/C extensions, there is no "caller Binding" to use, C extension frames have no Binding.<br>
If you want to eval with a specific Binding, capture it (e.g., with Kernel#binding in Ruby code), then pass it to the C extension and call Binding#eval(code) (e.g. with rb_funcall).<br>
Or simply call Binding#eval from Ruby code and avoid C altogether.</p> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=976092022-05-17T04:47:29Zdaveola (David Stellar)
<ul></ul><p>That's actually not really true, there <em>is</em> a caller binding, as evidenced by the fact that you can <em>get</em> the binding inside of the eval.</p>
<p>So it sets up the binding() properly, <em>except</em> that binding().receiver is <em>incorrect</em> and it effectively gives you a state that does not actually exist (the local variables are accessible, but the instance variables are not.).</p>
<p>The entire point of this is to come up with a C api that acts like Kernel::eval but with some modifications, so the answer of "just supply binding() to the method" is not really working.</p> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=976232022-05-17T10:59:45ZEregon (Benoit Daloze)
<ul></ul><p>It's the same as TOPLEVEL_BINDING, isn't it?<br>
It doesn't use any information from the caller, which is a C extension, which has binding, hence "no caller binding".</p>
<blockquote>
<p>so the answer of "just supply binding() to the method" is not really working.</p>
</blockquote>
<p>It is, use e.g. <code>rb_funcall(my_binding, rb_intern("eval"), rb_str_new(code))</code>.<br>
And <code>my_binding</code> comes from an argument to that method defined in the C extension.</p>
<p>There seems to be no C function to create a binding from a receiver, but that's also expected, a Binding is not just a receiver, it points to a frame, the receiver but also its local variables, the default definee, the constant scope, etc.</p> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=976332022-05-17T17:02:57Zdaveola (David Stellar)
<ul></ul><p>Eregon (Benoit Daloze) wrote in <a href="#note-3">#note-3</a>:</p>
<blockquote>
<p>It's the same as TOPLEVEL_BINDING, isn't it?<br>
It doesn't use any information from the caller, which is a C extension, which has binding, hence "no caller binding".</p>
</blockquote>
<p>No, it's not. It's the same as the caller's binding, <em>except</em> that the receiver/self is set to TOPLEVEL.</p>
<p>In other words:</p>
<a name="RUBY-CODE"></a>
<h2 >RUBY CODE<a href="#RUBY-CODE" class="wiki-anchor">¶</a></h2>
<pre><code>
class Foo
def bar
@instanceVar = 42
localVar = 42
<call my C method here>
end
end
@instanceVar = "main"
localVar = "main"
Foo.new().bar() # Create a foo and call bar which calls my C api
</code></pre>
<a name="C-CODE"></a>
<h2 >C CODE<a href="#C-CODE" class="wiki-anchor">¶</a></h2>
<pre><code> // inside the C api method:
VALUE bind = evalReq("binding()");
</code></pre>
<p>This binding will be pointing to localVar = 42 but @instanceVar = "main"</p>
<p>How you can justify that would be interesting.</p> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=976352022-05-17T17:30:41ZEregon (Benoit Daloze)
<ul><li><strong>Related to</strong> <i><a class="issue tracker-1 status-5 priority-4 priority-default closed" href="/issues/18487">Bug #18487</a>: Kernel#binding behaves differently depending on implementation language of items on the stack</i> added</li></ul> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=976362022-05-17T17:33:09ZEregon (Benoit Daloze)
<ul><li><strong>Status</strong> changed from <i>Rejected</i> to <i>Open</i></li></ul><p>OK, finally the issue is clear.</p>
<p>Could you try with latest master?<br>
With <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Kernel#binding behaves differently depending on implementation language of items on the stack (Closed)" href="https://bugs.ruby-lang.org/issues/18487">#18487</a> I believe you should get a RuntimeError if you call <code>binding</code> from a C frame/method.</p>
<p>That also makes it clear one should not try to get a Binding from a C frame, it must be captured from a Ruby frame and then everything is consistent.</p> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=976372022-05-17T17:35:19ZEregon (Benoit Daloze)
<ul></ul><p><a href="https://gist.github.com/eregon/31259907c132ab61e9f136d684b1fc39" class="external">https://gist.github.com/eregon/31259907c132ab61e9f136d684b1fc39</a> is an easy way to have a single-file repro with both C code and Ruby code, it'd be nice if you can provide a complete repro as well.</p> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=976382022-05-17T17:47:17Zdaveola (David Stellar)
<ul></ul><p>Eregon (Benoit Daloze) wrote in <a href="#note-6">#note-6</a>:</p>
<blockquote>
<p>OK, finally the issue is clear.</p>
<p>Could you try with latest master?<br>
With <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Kernel#binding behaves differently depending on implementation language of items on the stack (Closed)" href="https://bugs.ruby-lang.org/issues/18487">#18487</a> I believe you should get a RuntimeError if you call <code>binding</code> from a C frame/method.</p>
<p>That also makes it clear one should not try to get a Binding from a C frame, it must be captured from a Ruby frame and then everything is consistent.</p>
</blockquote>
<p>This is 100% not what I was hoping for.</p>
<ol>
<li>That seems like it will break a number of things, if your binding is no longer correct. What if you try to eval some code that ends up needing to use the binding?</li>
<li>This is inconsistent with Kernel::eval, which, while written in C, is able to get the binding of the caller.</li>
</ol>
<p>Let's say I need to write a myEval method which is like eval, but first prints out the string that is eval'd. Or possibly checks it for certain information. You have just made this literally impossible to write in ruby.</p> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=976392022-05-17T17:50:22Zdaveola (David Stellar)
<ul></ul><blockquote>
<p>You have just made this literally impossible to write in ruby.</p>
</blockquote>
<p>And before you say "just have the caller pass in the binding" - keep in mind that Kernel::eval does not require this. So you would not, for example, be able to make code that would wrap or replace eval, but still have proper bindings and be able to access local variables, instance variables, scope info, etc..</p>
<pre><code> foobar = 42
eval("foobar += 1") # Works
myEval("foobar += 1") # Impossible
</code></pre> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=979372022-06-11T12:56:53ZEregon (Benoit Daloze)
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Rejected</i></li></ul><p>I'm reluctant to reply since you seem to ignore my comments as a Ruby implementer.</p>
<p>Anyway, you are asking for something to get the caller's binding, that is very much on purpose not provided by Ruby.<br>
Such a functionality breaks encapsulation in a very bad manner.<br>
The fact calling <code>Kernel#binding</code> from a C extension did something like that was accidental, and has been fixed (<a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Kernel#binding behaves differently depending on implementation language of items on the stack (Closed)" href="https://bugs.ruby-lang.org/issues/18487">#18487</a>).</p>
<p>There have been discussions in the past and such functionality to get the caller binding should only be made available for debugging situations, in which you can use the <code>debug_inspector</code> C API or <a href="https://github.com/banister/debug_inspector" class="external">https://github.com/banister/debug_inspector</a>.<br>
That is of course slow and should only be used for debugging situations, but maybe it makes sense to use it conditionally in your case based on some configuration/option (and otherwise just <code>alias myEval eval</code>).</p>
<p>It's a fair point this makes it impossible to wrap eval and still get the same binding (without the <code>debug_inspector</code> C API), but Ruby will not add access to the caller binding just for that.</p> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=979382022-06-11T19:12:21Zdaveola (David Stellar)
<ul></ul><p>Eregon (Benoit Daloze) wrote in <a href="#note-10">#note-10</a>:</p>
<blockquote>
<p>I'm reluctant to reply since you seem to ignore my comments as a Ruby implementer.</p>
</blockquote>
<p>I'm sorry you feel that way, and I regret posting to the truffleruby discussion by mistake, I lost track of the many threads that are talking about this.</p>
<blockquote>
<p>Anyway, you are asking for something to get the caller's binding, that is very much on purpose not provided by Ruby.</p>
</blockquote>
<p>This is being discussed I now see on:<br>
<a href="https://bugs.ruby-lang.org/issues/15778" class="external">https://bugs.ruby-lang.org/issues/15778</a></p>
<p>You, in fact, said that you thought it was a good idea (3 years ago) and I hope that is still the case, because this mechanism would solve my problem.</p>
<p>I understand that one almost never wants to break encapsulation (except in debug), but there are cases, and eval() itself <em>does</em> break encapsulation and is part of ruby.</p>
<blockquote>
<p>That is of course slow and should only be used for debugging situations, but maybe it makes sense to use it conditionally in your case</p>
</blockquote>
<p>I hear the suggestion and I will just have to switch to using a debugger for my code to work, it's not a conditional, I need the ability to write a different version of eval() for my code, and I fought hard at my company to use ruby, so it's a shame that it has to be done with a debug hack and all of it's performance penalties, but I hear that you are not willing to add this type of a pathway.</p>
<p>Hopefully 15778 will get resolved some day and then none of this will be an issue anymore for me. :)</p> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=979412022-06-12T11:26:17ZEregon (Benoit Daloze)
<ul><li><strong>Related to</strong> <i><a class="issue tracker-2 status-1 priority-4 priority-default" href="/issues/15778">Feature #15778</a>: Expose an API to pry-open the stack frames in Ruby</i> added</li></ul> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=979422022-06-12T11:35:01ZEregon (Benoit Daloze)
<ul></ul><p>daveola (David Stellar) wrote in <a href="#note-11">#note-11</a>:</p>
<blockquote>
<p>I hear the suggestion and I will just have to switch to using a debugger for my code to work, it's not a conditional, I need the ability to write a different version of eval() for my code, and I fought hard at my company to use ruby, so it's a shame that it has to be done with a debug hack and all of it's performance penalties, but I hear that you are not willing to add this type of a pathway.</p>
</blockquote>
<p>Note that no debugger is needed for the <code>debug_inspector</code> gem, it's a regular gem/dependency.<br>
That C API that the gem uses is meant to only be used for debugging situations, but there is no actual check for it, it relies on people using it only for valid situations.<br>
So you should be able to use it for your use-case without issues.</p>
<blockquote>
<p>Hopefully 15778 will get resolved some day and then none of this will be an issue anymore for me. :)</p>
</blockquote>
<p>My understanding of that issue's discussion so far is that is unlikely.<br>
I would personally be happy to have a Ruby API for this as proposed there, but I understand concerns of other CRuby committers to not make it too easy to use such a dangerous/breaking-encapsulation API, and so I guess it will remain a C API only (+ the gem) for a while.</p> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=980042022-06-14T21:12:27Zalanwu (Alan Wu)
<ul></ul><p>Thanks for the bug report. The "binding with mixed information from two<br>
contexts" situation from <a href="https://bugs.ruby-lang.org/issues/18780#note-4" class="external">https://bugs.ruby-lang.org/issues/18780#note-4</a><br>
definitely looks wrong. I wrote a reproducer and luckily, it's no<br>
longer an issue on the master branch (ab10f111c3).</p>
<blockquote>
<p>Let's say I need to write a myEval method which is like eval, but first<br>
prints out the string that is eval'd. ... You have just made this literally<br>
impossible to write in ruby.</p>
</blockquote>
<p>This is in fact possible with the <code>rb_binding_new()</code> API on both 2.7.6 and on<br>
the master branch. Here is a demo script:</p>
<pre><code class="ruby syntaxhl" data-language="ruby"><span class="nb">puts</span> <span class="no">RUBY_DESCRIPTION</span>
<span class="nb">require</span> <span class="s1">'tmpdir'</span>
<span class="nb">require</span> <span class="s1">'rbconfig'</span>
<span class="c1"># From https://gist.github.com/eregon/31259907c132ab61e9f136d684b1fc39</span>
<span class="c1"># courtesy of Benoit.</span>
<span class="k">def</span> <span class="nf">inline_c_extension</span><span class="p">(</span><span class="n">c_code</span><span class="p">)</span>
<span class="no">Dir</span><span class="p">.</span><span class="nf">mktmpdir</span><span class="p">(</span><span class="s1">'inline_c_extension'</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">dir</span><span class="o">|</span>
<span class="no">File</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">dir</span><span class="si">}</span><span class="s2">/cext.c"</span><span class="p">,</span> <span class="n">c_code</span><span class="p">)</span>
<span class="no">File</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="s2">"</span><span class="si">#{</span><span class="n">dir</span><span class="si">}</span><span class="s2">/extconf.rb"</span><span class="p">,</span> <span class="o"><<~</span><span class="no">RUBY</span><span class="p">)</span><span class="sh">
require 'mkmf'
create_makefile('cext')
</span><span class="no"> RUBY</span>
<span class="n">out</span> <span class="o">=</span> <span class="no">IO</span><span class="p">.</span><span class="nf">popen</span><span class="p">([</span><span class="no">RbConfig</span><span class="p">.</span><span class="nf">ruby</span><span class="p">,</span> <span class="s1">'extconf.rb'</span><span class="p">],</span> <span class="ss">chdir: </span><span class="n">dir</span><span class="p">,</span> <span class="o">&</span><span class="ss">:read</span><span class="p">)</span>
<span class="k">raise</span> <span class="s2">"ruby extconf.rb failed: </span><span class="si">#{</span><span class="vg">$?</span><span class="p">.</span><span class="nf">inspect</span><span class="si">}</span><span class="se">\n</span><span class="si">#{</span><span class="n">out</span><span class="si">}</span><span class="s2">"</span> <span class="k">unless</span> <span class="vg">$?</span><span class="p">.</span><span class="nf">success?</span>
<span class="n">out</span> <span class="o">=</span> <span class="no">IO</span><span class="p">.</span><span class="nf">popen</span><span class="p">([</span><span class="s1">'make'</span><span class="p">],</span> <span class="ss">chdir: </span><span class="n">dir</span><span class="p">,</span> <span class="o">&</span><span class="ss">:read</span><span class="p">)</span>
<span class="k">raise</span> <span class="s2">"make failed: </span><span class="si">#{</span><span class="vg">$?</span><span class="p">.</span><span class="nf">inspect</span><span class="si">}</span><span class="se">\n</span><span class="si">#{</span><span class="n">out</span><span class="si">}</span><span class="s2">"</span> <span class="k">unless</span> <span class="vg">$?</span><span class="p">.</span><span class="nf">success?</span>
<span class="nb">require</span> <span class="s2">"</span><span class="si">#{</span><span class="n">dir</span><span class="si">}</span><span class="s2">/cext.</span><span class="si">#{</span><span class="no">RbConfig</span><span class="o">::</span><span class="no">CONFIG</span><span class="p">[</span><span class="s1">'DLEXT'</span><span class="p">]</span><span class="si">}</span><span class="s2">"</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">inline_c_extension</span> <span class="o"><<~</span><span class="no">C</span><span class="sh">
#include "ruby.h"
// A wrapped version of Kernel#eval that prints the program before evaluating.
// Uses the caller's Ruby context just like Kernel#eval.
static VALUE
my_eval(VALUE self, VALUE program)
{
ID p_id = rb_intern("p");
ID eval_id = rb_intern("eval");
VALUE binding;
if (1 /* do what I understand OP wants */) {
binding = rb_binding_new();
}
else {
// Flip the if to check the "impossible binding" issue from <a href="/issues/18780">[ruby-core:108597]</a>
binding = rb_eval_string("p [:rbeval_string, @foo, foo]; binding");
}
rb_funcall(self, p_id, 1, program);
return rb_funcall(binding, eval_id, 1, program);
}
void
Init_cext(void)
{
rb_define_global_function("my_eval", my_eval, 1);
}
</span><span class="no">C</span>
<span class="vi">@foo</span> <span class="o">=</span> <span class="ss">:main</span>
<span class="n">foo</span> <span class="o">=</span> <span class="ss">:mainl</span>
<span class="n">my_eval</span><span class="p">(</span><span class="s2">"p [:in_main, self, @foo, foo]"</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">Foo</span>
<span class="k">def</span> <span class="nf">foo</span>
<span class="vi">@foo</span> <span class="o">=</span> <span class="ss">:inclass</span>
<span class="n">foo</span> <span class="o">=</span> <span class="ss">:inclassl</span>
<span class="n">my_eval</span><span class="p">(</span><span class="s2">"p [:inside_foo, self, @foo, foo]"</span><span class="p">)</span>
<span class="k">end</span>
<span class="n">new</span><span class="p">.</span><span class="nf">foo</span>
<span class="k">end</span>
</code></pre>
<pre><code class="shell syntaxhl" data-language="shell"><span class="nv">$ </span>ruby demo.rb
ruby 3.2.0dev <span class="o">(</span>2022-06-14T16:06:06Z master ab10f111c3<span class="o">)</span> <span class="o">[</span>x86_64-darwin21]
<span class="s2">"p [:in_main, self, @foo, foo]"</span>
<span class="o">[</span>:in_main, main, :main, :mainl]
<span class="s2">"p [:inside_foo, self, @foo, foo]"</span>
<span class="o">[</span>:inside_foo, <span class="c">#<Foo:0x000000010fcc1360 @foo=:inclass>, :inclass, :inclassl]</span>
</code></pre>
<p>In terms of [<a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Kernel#binding behaves differently depending on implementation language of items on the stack (Closed)" href="https://bugs.ruby-lang.org/issues/18487">#18487</a>], <code>my_eval</code> in the demo is not any more powerful than<br>
<code>Kernel#binding</code> and <code>Kernel#eval</code> since it's only getting the binding of<br>
the Ruby frame immediately below it.</p>
<p>By the way, thanks to some awesome work by <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/10">@shyouhei (Shyouhei Urabe)</a>, newer versions of public C headers<br>
have API documentations. I would recommend reading those docs instead of the implementation.<br>
For example for <code>rb_eval_string()</code>:</p>
<pre><code>/**
* Evaluates the given string in an isolated binding.
*
* Here "isolated" means that the binding does not inherit any other
* bindings. This behaves same as the binding for required libraries.
* ...
</code></pre>
<p>I wonder if we host the HTML version of the C API docs anywhere...</p> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=980052022-06-14T21:19:28Zalanwu (Alan Wu)
<ul><li><strong>Status</strong> changed from <i>Rejected</i> to <i>Open</i></li></ul><blockquote>
<p>I wrote a reproducer and luckily, it's no<br>
longer an issue on the master branch (ab10f111c3).</p>
</blockquote>
<p>I spoke too soon! Flipping the <code>if</code> in <code>demo.rb</code>:</p>
<pre><code class="text syntaxhl" data-language="text">ruby 3.2.0dev (2022-06-14T16:06:06Z master ab10f111c3) [x86_64-darwin21]
[:rbeval_string, :main, :mainl]
"p [:in_main, self, @foo, foo]"
[:in_main, main, :main, :mainl]
[:rbeval_string, :main, :inclassl] <<<<<< mixed context!
"p [:inside_foo, self, @foo, foo]"
[:inside_foo, main, :main, :inclassl]
</code></pre>
<p>I'll reopen this and look into it later.</p> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=980062022-06-14T22:52:34Zdaveola (David Stellar)
<ul></ul><p>alanwu (Alan Wu) wrote in <a href="#note-15">#note-15</a>:</p>
<blockquote>
<p>I spoke too soon! Flipping the <code>if</code> in <code>demo.rb</code>:</p>
<p>I'll reopen this and look into it later.</p>
</blockquote>
<p>Doesn't the change in <a href="https://bugs.ruby-lang.org/issues/18487" class="external">https://bugs.ruby-lang.org/issues/18487</a> (raising a kernel error) imply that this doesn't need to be fixed?</p>
<p>Also, thanks for the pointer to rb_binding_new(), I'll take a look at that, but issue 18487 implies that it will likely be viewed as incorrect to return the caller's binding.</p> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=980072022-06-15T00:14:42Zalanwu (Alan Wu)
<ul></ul><p>The issue with <a class="issue tracker-1 status-5 priority-4 priority-default closed" title="Bug: Kernel#binding behaves differently depending on implementation language of items on the stack (Closed)" href="https://bugs.ruby-lang.org/issues/18487">#18487</a> was that <code>Kernel#binding</code> used to find the<br>
first Ruby frame on the stack and that this iterating behavior<br>
is brittle as it basically acts as an assertion that the frames<br>
that it skips over remain as non-Ruby frames in future versions. We<br>
changed it so that it doesn't iterate anymore and only checks<br>
the direct caller. It's easier to reason about since it's a<br>
weaker assertion and there is only one frame involved.</p>
<p>So now binding related APIs should only consider the direct<br>
caller. The weird <code>rb_eval_string()</code> scenario somewhat betrays<br>
this new semantics because when the frame for <code>Kernel#binding</code><br>
is active the stack looks like this:</p>
<pre><code> Type Name
C Kernel#binding
Ruby <eval code frame>
C my_eval
Ruby Foo#foo
</code></pre>
<p>It returns the local from <code>Foo#foo</code>, so not only does it skip<br>
over a C frame, it also skips over a Ruby frame! Now, maybe<br>
we could understand <code>Kernel#binding</code>'s behavior here as not<br>
skipping frames but rather returning a binding previously<br>
created at the time when we call <code>rb_eval_string()</code>, when the<br>
stack looked like:</p>
<pre><code> C my_eval
Ruby Foo#foo
</code></pre>
<p>But the way it idiosyncratically creates a binding by mixing<br>
contexts is still surprising. The behavior doesn't match<br>
what the docs for <code>rb_eval_string()</code> would suggest either<br>
since it's not an isolated binding.</p>
<p>Calling <code>rb_binding_new()</code> from a C method doesn't create<br>
a new frame, and it's distinct from calling <code>Kernel#binding</code><br>
from a C method because of that. Using <code>rb_binding_new()</code> is<br>
somewhat like assuming the role of <code>Kernel#binding</code>. One can only call<br>
<code>rb_binding_new()</code> from a C method so making it raise in that<br>
context would render it completely useless -- not desirable!</p> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=984102022-07-21T12:09:33Zmame (Yusuke Endoh)mame@ruby-lang.org
<ul></ul><p>We discussed this issue at the dev meeting.</p>
<p><a class="user active user-mention" href="https://bugs.ruby-lang.org/users/13">@matz (Yukihiro Matsumoto)</a> said <code>self</code> should be changed. <code>rb_eval_string</code> should evaluate the argument under the current context. The <code>self</code> should be picked from the context instead of <code>main</code>. If there is no caller binding (which is a main use case for <code>rb_eval_string</code>, as <a class="user active user-mention" href="https://bugs.ruby-lang.org/users/772">@Eregon (Benoit Daloze)</a> said), <code>self</code> should be <code>main</code>.</p>
<p>We will revisit the decision if compatibility issues are discovered.</p> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=984312022-07-22T00:24:56Zshyouhei (Shyouhei Urabe)shyouhei@ruby-lang.org
<ul></ul><p>Updating the header document.</p>
<pre><code class="patch syntaxhl" data-language="patch"><span class="p">From b968f277386649b7531a8999be54eacf3c599cdb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?=
</span> <shyouhei@ruby-lang.org>
<span class="p">Date: Thu, 21 Jul 2022 18:00:04 +0900
Subject: [PATCH] [DOC] update rb_eval_string() document.
</span><span class="err">
</span><span class="p">---
</span> include/ruby/internal/eval.h | 33 ++++++++++++++++++++++++++++++---
template/Doxyfile.tmpl | 1 +
2 files changed, 31 insertions(+), 3 deletions(-)
<span class="err">
</span><span class="gh">diff --git a/include/ruby/internal/eval.h b/include/ruby/internal/eval.h
index 34a53849da..b7ab66515f 100644
</span><span class="gd">--- a/include/ruby/internal/eval.h
</span><span class="gi">+++ b/include/ruby/internal/eval.h
</span><span class="p">@@ -28,10 +28,12 @@</span> RBIMPL_SYMBOL_EXPORT_BEGIN()
RBIMPL_ATTR_NONNULL(())
/**
<span class="gd">- * Evaluates the given string in an isolated binding.
</span><span class="gi">+ * Evaluates the given string.
</span> *
<span class="gd">- * Here "isolated" means that the binding does not inherit any other
- * bindings. This behaves same as the binding for required libraries.
</span><span class="gi">+ * In case it is called from within a C-backended method, the evaluation is
+ * done under the current binding. However there can be no method. On such
+ * situation this function evaluates in an isolated binding, like `require`
+ * runs in a separate one.
</span> *
* `__FILE__` will be `"(eval)"`, and `__LINE__` starts from 1 in the
* evaluation.
<span class="p">@@ -39,6 +41,31 @@</span> RBIMPL_ATTR_NONNULL(())
* @param[in] str Ruby code to evaluate.
* @exception rb_eException Raises an exception on error.
* @return The evaluated result.
<span class="gi">+ *
+ * @internal
+ *
+ * @shyouhei's old tale about the birth and growth of this function:
+ *
+ * At the beginning, there was no rb_eval_string(). @shyouhei heard that
+ * @shugo, author of Apache httpd's mod_ruby module, requested @matz for this
+ * API. He wanted a way so that mod_ruby can evaluate ruby scripts one by one,
+ * separately, in each different contexts. So this function was made. It was
+ * designed to be a global interpreter entry point like ruby_run_node().
+ *
+ * The way it is implemented however allows extension libraries (not just
+ * programs like Apache httpd) to call this function. Because its name says
+ * nothing about the initial design, people started to think of it as an
+ * orthodox way to call ruby level `eval` method from their extension
+ * libraries. Even our `extension.rdoc` has had a description of this function
+ * basically according to this understanding.
+ *
+ * The old (mod_ruby like) usage still works. But over time, usages of this
+ * function from extension libraries got popular, while mod_ruby faded out; is
+ * no longer maintained now. Devs decided to actively support both. This
+ * function now auto-detects how it is called, and switches how it works
+ * depending on it.
+ *
+ * @see https://bugs.ruby-lang.org/issues/18780
</span> */
VALUE rb_eval_string(const char *str);
<span class="gh">diff --git a/template/Doxyfile.tmpl b/template/Doxyfile.tmpl
index 36c0b1c8d6..502e171384 100644
</span><span class="gd">--- a/template/Doxyfile.tmpl
</span><span class="gi">+++ b/template/Doxyfile.tmpl
</span><span class="p">@@ -274,6 +274,7 @@</span> ALIASES += "old{1}=Old name of @ref \1.^^@deprecated Use @ref \1 i
ALIASES += "shyouhei=\@shyouhei"
ALIASES += "ko1=\@ko1"
ALIASES += "matz=\@matz"
<span class="gi">+ALIASES += "shugo=\@shugo"
</span>
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
<span class="gd">--
</span><span class="p">2.17.1
</span><span class="err">
</span></code></pre> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=984382022-07-22T10:00:34ZEregon (Benoit Daloze)
<ul></ul><p>Nice story :)</p>
<p>Probably a typo: <code>s/old table/old tale/</code></p> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=984392022-07-22T12:10:58Zshyouhei (Shyouhei Urabe)shyouhei@ruby-lang.org
<ul></ul><p>Yes, it is a typo. Thank you.</p> Ruby master - Bug #18780: Incorrect binding receiver for C API rb_eval_string()https://bugs.ruby-lang.org/issues/18780?journal_id=988832022-08-24T16:43:56Zjeremyevans0 (Jeremy Evans)merch-redmine@jeremyevans.net
<ul><li><strong>Status</strong> changed from <i>Open</i> to <i>Closed</i></li></ul><p>Fixed by <a class="changeset" title="respect current frame of `rb_eval_string` `self` is nearest Ruby method's `self`. If there is no..." href="https://bugs.ruby-lang.org/projects/ruby-master/repository/git/revisions/5bbba76489628f4509495ebf4ba0a7aad4c0b560">5bbba76489628f4509495ebf4ba0a7aad4c0b560</a></p>