I have the following class structure:
class A { mixed foo() { }
class AA { mixed foo() { return A::foo(); // Implemented with apply_external(1, f_A_foo_fun_num, args); } } }
class B { inherit A; class BB { inherit A::AA; } }
class C { inherit B; }
class D { inherit C; class BB { inherit C::BB; } }
But with A, B and C implemented in a cmod. A::AA and B::BB are marked as PROGRAM_USES_PARENT.
My problem is that when foo() is called in D()->BB(), find_external_context() returns a loc.inherit with an inherit_level of zero, which causes apply_external() to call the wrong identifier. As far as I can see, the problem might lie in the default branch of find_external_context() after the recursive call:
apply_external(1, 1, 4)... -find_external_context(1, inherit=2) - i->parent_offset=1 i->parent_identifier=0 - o->parent_identifier=0 inherit->identifier_level=2 - inherit-- (2 >= 2) -find_external_context(1, inherit=1) - i->parent_offset=1 i->parent_identifier=80 - o->parent_identifier=0 inherit->identifier_level=0 - inherit-- (1 >= 1) -find_external_context(1, inherit=0) - i->parent_offset=-18 i->parent_identifier=-1 - o->parent_identifier=0 inherit->identifier_level=0 - Following o->parent - Parent identifier = 0 (BB), inherit # = 0 --find_external_context: parent_id=0 (BB) * inh->parent_identifier: 80 * loc->parent_identifier: 0 * loc->inherit->parent_offset: -18 * loc->inherit->identifier_level: 0 * parent_identifier: 80 - Parent identifier = 80 (BB), inherit # = 2 --find_external_context: parent_id=80 (BB) * inh->parent_identifier: 0 * loc->parent_identifier: 80 * loc->inherit->parent_offset: 0 * loc->inherit->identifier_level: 0 * parent_identifier: 0 - Parent identifier = 0 (BB), inherit # = 0 --find_external_context: parent_id=0 (BB) apply_external(1, 1, 4) ==> apply_low(0x83c5750, 1, 4) Too many arguments to create(). Expected at most 1 (got 4).
Does anybody know in what step it has gone wrong?
Interresting; the bug doesn't show up when I call foo() directly from hilfe, just when it's called via apply_current():
DD()->BB("")->foo(2,([]),"","");
apply_external(1, 1, 4)... -find_external_context(1, inherit=2) - i->parent_offset=1 i->parent_identifier=1 - o->parent_identifier=0 inherit->identifier_level=2 - inherit-- (2 >= 2) -find_external_context(1, inherit=1) - i->parent_offset=1 i->parent_identifier=80 - o->parent_identifier=0 inherit->identifier_level=0 - inherit-- (1 >= 1) -find_external_context(1, inherit=0) - i->parent_offset=-18 i->parent_identifier=-1 - o->parent_identifier=0 inherit->identifier_level=0 - Following o->parent - Parent identifier = 0 (BB), inherit # = 0 --find_external_context: parent_id=0 (BB) * inh->parent_identifier: 80 * loc->parent_identifier: 0 * loc->inherit->parent_offset: -18 * loc->inherit->identifier_level: 0 * parent_identifier: 80 - Parent identifier = 80 (BB), inherit # = 2 --find_external_context: parent_id=80 (BB) * inh->parent_identifier: 1 * loc->parent_identifier: 80 * loc->inherit->parent_offset: 0 * loc->inherit->identifier_level: 0 * parent_identifier: 1 - Parent identifier = 1 (AA), inherit # = 3 --find_external_context: parent_id=1 (AA) apply_external(1, 1, 4) ==> apply_low(0x83c4ee0, 2, 4)
The main difference seems to be that i->parent_identifier is 1 when called directly from hilfe:
apply_external(1, 1, 4)... apply_external(1, 1, 4) ==> -find_external_context(1, inherit=2) -- i->parent_offset=1 i->parent_identifier=0 +- i->parent_offset=1 i->parent_identifier=1 - o->parent_identifier=0 inherit->identifier_level=2 - inherit-- (2 >= 2) -find_external_context(1, inherit=1)
* parent_identifier: 80 - Parent identifier = 80 (BB), inherit # = 2 --find_external_context: parent_id=80 (BB) -* inh->parent_identifier: 0 +* inh->parent_identifier: 1 * loc->parent_identifier: 80 * loc->inherit->parent_offset: 0 * loc->inherit->identifier_level: 0 -- parent_identifier: 0 -- Parent identifier = 0 (BB), inherit # = 0 ---find_external_context: parent_id=0 (BB) +* parent_identifier: 1 +- Parent identifier = 1 (AA), inherit # = 3 +--find_external_context: parent_id=1 (AA) apply_external(1, 1, 4) ==> - apply_low(0x83c5750, 1, 4) + apply_low(0x83c4ee0, 2, 4)
So the question becomes: Why is i->parent_identifier zero in the first case?
Uhhgn, complicated..
D::BB also has PROGRAM_USES_PARENT, right? Is it possible to minimize the test case any further?
Uhhgn, complicated..
You don't say?
D::BB also has PROGRAM_USES_PARENT, right? Is it possible to minimize
I assume so, D (and D::BB) is written in pike.
the test case any further?
I don't have a stand-alone test, but I assume it's possible.
To complicate things further: A::AA, B::BB and D::BB are iterators, and A::AA::foo() is called via A::AA::next() via B::BB::next() in an instance of D::BB via foreach_iterate() called by a function implemented in the class C. Simple, isn't it?
Anyway, the main problem seems to be that Pike_fp->fun when A::AA::foo() calls A::foo() has a bad value (cf interpret.c:apply_external() and 12677018).
I think I've found the cause; precompile.pike puts INHERIT sections after the section with subclasses, which caused the code:
PIKECLASS A { PIKECLASS AA program_flags PROGRAM_USES_PARENT; { ... } }
PIKECLASS B { INHERIT A;
PIKECLASS BB program_flags PROGRAM_USES_PARENT; { EXTRA { low_inherit(A_AA_program, 0, A_AA_program_fun_num + Pike_compiler->previous->new_program->inherits[1].identifier_level, 1 + 42, 0, NULL);
} } }
to fail, since Pike_compiler->previous->new_program->inherits[1] hasn't been initialized yet when the low_inherit() is executed.
pike-devel@lists.lysator.liu.se