Skip to content

Commit fa4c04a

Browse files
committed
Use CFUNC namespace only for IFUNC frames, its behavior should be unchanged
1 parent 12303e2 commit fa4c04a

File tree

3 files changed

+97
-52
lines changed

3 files changed

+97
-52
lines changed

test/ruby/test_namespace.rb

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -711,12 +711,26 @@ def ns4 = Namespace.current
711711
def self.ns5 = NS2
712712
def self.ns6 = Namespace.current
713713
def self.ns6_proc = ->(){ Namespace.current }
714+
def self.ns7
715+
res = []
716+
[1,2].chunk{ it.even? }.each do |bool, members|
717+
res << Namespace.current.object_id.to_s + ":" + bool.to_s + ":" + members.map(&:to_s).join(",")
718+
end
719+
res
720+
end
714721
715722
def self.yield_block = yield
716723
def self.call_block(&b) = b.call
717724
end
718725
FOO_NAME = Foo.name
719-
NS9 = Foo.new.ns4
726+
727+
module Kernel
728+
def foo_namespace = Namespace.current
729+
module_function :foo_namespace
730+
end
731+
732+
NS_X = Foo.new.ns4
733+
NS_Y = foo_namespace
720734
EOC
721735
ns.eval(code)
722736
outer = Namespace.current
@@ -729,9 +743,14 @@ def self.call_block(&b) = b.call
729743
assert_equal ns, ns::Foo.ns6 # singleton method -> the current
730744
assert_equal ns, ns::Foo.ns6_proc.call # method returns a proc -> the current
731745
746+
# a block after CFUNC/IFUNC in a method -> the current
747+
assert_equal ["#{ns.object_id}:false:1", "#{ns.object_id}:true:2"], ns::Foo.ns7
748+
732749
assert_equal outer, ns::Foo.yield_block{ Namespace.current } # method yields
733750
assert_equal outer, ns::Foo.call_block{ Namespace.current } # method calls a block
734-
assert_equal ns, ns::NS9 # on TOP frame, referring a class in the current
751+
752+
assert_equal ns, ns::NS_X # on TOP frame, referring a class in the current
753+
assert_equal ns, ns::NS_Y # on TOP frame, referring Kernel method defined by a CFUNC method
735754
736755
assert_equal "Foo", ns::FOO_NAME
737756
assert_equal "Foo", ns::Foo.name

vm.c

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -113,39 +113,62 @@ VM_EP_RUBY_LEP(const rb_execution_context_t *ec, const rb_control_frame_t *curre
113113
// rb_vmdebug_namespace_env_dump_raw() simulates this function
114114
const VALUE *ep = current_cfp->ep;
115115
const rb_control_frame_t * const eocfp = RUBY_VM_END_CONTROL_FRAME(ec); /* end of control frame pointer */
116-
const rb_control_frame_t *cfp = NULL, *checkpoint_cfp = current_cfp;
116+
const rb_control_frame_t *cfp = current_cfp;
117+
118+
if (VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_IFUNC)) {
119+
ep = VM_EP_LEP(current_cfp->ep);
120+
/**
121+
* Returns CFUNC frame only in this case.
122+
*
123+
* Usually CFUNC frame doesn't represent the current namespace and it should operate
124+
* the caller namespace. See the example:
125+
*
126+
* # in the main namespace
127+
* module Kernel
128+
* def foo = "foo"
129+
* module_function :foo
130+
* end
131+
*
132+
* In the case above, `module_function` is defined in the root namespace.
133+
* If `module_function` worked in the root namespace, `Kernel#foo` is invisible
134+
* from it and it causes NameError: undefined method `foo` for module `Kernel`.
135+
*
136+
* But in cases of IFUNC (blocks written in C), IFUNC doesn't have its own namespace
137+
* and its local env frame will be CFUNC frame.
138+
* For example, `Enumerator#chunk` calls IFUNC blocks, written as `chunk_i` function.
139+
*
140+
* [1].chunk{ it.even? }.each{ ... }
141+
*
142+
* Before calling the Ruby block `{ it.even? }`, `#chunk` calls `chunk_i` as IFUNC
143+
* to iterate the array's members (it's just like `#each`).
144+
* We expect that `chunk_i` works as expected by the implementation of `#chunk`
145+
* without any overwritten definitions from namespaces.
146+
* So the definitions on IFUNC frames should be equal to the caller CFUNC.
147+
*/
148+
VM_ASSERT(VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_CFUNC));
149+
return ep;
150+
}
117151

118-
while (!VM_ENV_LOCAL_P(ep) || VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_CFUNC)) {
119-
while (!VM_ENV_LOCAL_P(ep)) {
120-
ep = VM_ENV_PREV_EP(ep);
121-
}
122-
while (VM_ENV_FLAGS(ep, VM_FRAME_FLAG_CFRAME) != 0) {
123-
if (!cfp) {
124-
cfp = rb_vm_search_cf_from_ep(ec, checkpoint_cfp, ep);
125-
VM_NAMESPACE_ASSERT(cfp, "Failed to search cfp from ep");
126-
VM_NAMESPACE_ASSERT(cfp->ep == ep, "Searched cfp's ep is not equal to ep");
127-
}
128-
if (!cfp) {
129-
return NULL;
130-
}
131-
VM_NAMESPACE_ASSERT(cfp->ep, "cfp->ep == NULL");
132-
VM_NAMESPACE_ASSERT(cfp->ep == ep, "cfp->ep != ep");
152+
while (VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_CFUNC)) {
153+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
133154

134-
VM_NAMESPACE_ASSERT(!VM_FRAME_FINISHED_P(cfp), "CFUNC frame should not FINISHED");
155+
VM_NAMESPACE_ASSERT(cfp, "CFUNC should have a valid previous control frame");
156+
VM_NAMESPACE_ASSERT(cfp < eocfp, "CFUNC should have a valid caller frame");
157+
if (!cfp || cfp >= eocfp) {
158+
return NULL;
159+
}
135160

136-
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
137-
if (cfp >= eocfp) {
138-
return NULL;
139-
}
140-
VM_NAMESPACE_ASSERT(cfp, "CFUNC should have a valid previous control frame");
141-
ep = cfp->ep;
142-
if (!ep) {
143-
return NULL;
144-
}
161+
VM_NAMESPACE_ASSERT(cfp->ep, "CFUNC should have a valid caller frame with env");
162+
ep = cfp->ep;
163+
if (!ep) {
164+
return NULL;
145165
}
146-
checkpoint_cfp = cfp;
147-
cfp = NULL;
148166
}
167+
168+
while (!VM_ENV_LOCAL_P(ep)) {
169+
ep = VM_ENV_PREV_EP(ep);
170+
}
171+
149172
return ep;
150173
}
151174

@@ -3096,7 +3119,7 @@ current_namespace_on_cfp(const rb_execution_context_t *ec, const rb_control_fram
30963119
VM_NAMESPACE_ASSERT(lep, "lep should be valid");
30973120
VM_NAMESPACE_ASSERT(rb_namespace_available(), "namespace should be available here");
30983121

3099-
if (VM_ENV_FRAME_TYPE_P(lep, VM_FRAME_MAGIC_METHOD)) {
3122+
if (VM_ENV_FRAME_TYPE_P(lep, VM_FRAME_MAGIC_METHOD) || VM_ENV_FRAME_TYPE_P(lep, VM_FRAME_MAGIC_CFUNC)) {
31003123
cme = check_method_entry(lep[VM_ENV_DATA_INDEX_ME_CREF], TRUE);
31013124
VM_NAMESPACE_ASSERT(cme, "cme should be valid");
31023125
VM_NAMESPACE_ASSERT(cme->def, "cme->def shold be valid");

vm_dump.c

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -421,39 +421,42 @@ rb_vmdebug_namespace_env_dump_raw(const rb_execution_context_t *ec, const rb_con
421421
// See VM_EP_RUBY_LEP for the original logic
422422
const VALUE *ep = current_cfp->ep;
423423
const rb_control_frame_t * const eocfp = RUBY_VM_END_CONTROL_FRAME(ec); /* end of control frame pointer */
424-
const rb_control_frame_t *cfp = NULL, *checkpoint_cfp = current_cfp;
424+
const rb_control_frame_t *cfp = current_cfp, *checkpoint_cfp = current_cfp;
425425

426426
kprintf("-- Namespace detection information "
427427
"-----------------------------------------\n");
428428

429429
namespace_env_dump_unchecked(ec, ep, checkpoint_cfp, errout);
430430

431-
while (!VM_ENV_LOCAL_P(ep) || VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_CFUNC)) {
431+
if (VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_IFUNC)) {
432432
while (!VM_ENV_LOCAL_P(ep)) {
433433
ep = VM_ENV_PREV_EP(ep);
434434
namespace_env_dump_unchecked(ec, ep, checkpoint_cfp, errout);
435435
}
436-
while (VM_ENV_FLAGS(ep, VM_FRAME_FLAG_CFRAME) != 0) {
437-
if (!cfp) {
438-
cfp = vmdebug_search_cf_from_ep(ec, checkpoint_cfp, ep);
439-
}
440-
if (!cfp) {
441-
goto stop;
442-
}
443-
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
444-
if (cfp >= eocfp) {
445-
kprintf("[PREVIOUS CONTROL FRAME IS OUT OF BOUND]\n");
446-
goto stop;
447-
}
448-
ep = cfp->ep;
449-
namespace_env_dump_unchecked(ec, ep, checkpoint_cfp, errout);
450-
if (!ep) {
451-
goto stop;
452-
}
436+
goto stop;
437+
}
438+
439+
while (VM_ENV_FRAME_TYPE_P(ep, VM_FRAME_MAGIC_CFUNC)) {
440+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
441+
if (!cfp) {
442+
goto stop;
443+
}
444+
if (cfp >= eocfp) {
445+
kprintf("[PREVIOUS CONTROL FRAME IS OUT OF BOUND]\n");
446+
goto stop;
447+
}
448+
ep = cfp->ep;
449+
namespace_env_dump_unchecked(ec, ep, checkpoint_cfp, errout);
450+
if (!ep) {
451+
goto stop;
453452
}
454-
checkpoint_cfp = cfp;
455-
cfp = NULL;
456453
}
454+
455+
while (!VM_ENV_LOCAL_P(ep)) {
456+
ep = VM_ENV_PREV_EP(ep);
457+
namespace_env_dump_unchecked(ec, ep, checkpoint_cfp, errout);
458+
}
459+
457460
stop:
458461
kprintf("\n");
459462
return true;

0 commit comments

Comments
 (0)