Mailing List Archive

Calling eval_sv() at compiletime with the right context
I'm running some C code that's somewhere within a big nest of C
function calls, ultimately invoked from Perl's PL_keyword_plugin. I.e.
happening at parse-time. The code is trying to eval_sv() a string
parsed out of the source code. Here's the call currently:

https://metacpan.org/release/PEVANS/Object-Pad-FieldAttr-Checked-0.02/source/lib/Object/Pad/FieldAttr/Checked.xs#L95

I find that when that happens, the eval'ed code runs in package "main",
has no strict, no warnings, no features from the prevailing `use
VERSION`, etc... So I suspect I am doing something wrong. But I have
no idea what.

Evidentally I'm not the first to encounter this problem. mauke said it
sounded familiar, and offered this workaround:

https://github.com/mauke/Function-Parameters/blob/main/Parameters.xs#L706

I took that inspiration and adjusted it a bit; now I'm doing this:

// We have to fool eval_sv() into seeing the right package
SAVEVPTR(PL_curcop);
COP *fakecop = (COP *)newSTATEOP(0, NULL, NULL);
SAVEFREEOP(fakecop);
CopSTASH_set(fakecop, PL_curstash);
PL_curcop = fakecop;

eval_sv_rethrow(value, G_SCALAR);

This now fixes a littlebit. The invoked code now sees the correct
package, e.g. if I inject the code `print STDERR __PACKAGE__, "\n"`

Problem is everything else is still wrong. I still have no strict and
no feature flags. Though, I do get warnings. I seem to have this
bizarre mixture of some state but not the rest.

I suspect I'm somewhat on the right lines here in that I need to create
a fake COP to put into PL_curcop, but apparently it doesn't have the
right settings for hints or feature flags.

Can anyone suggest some further guidance?

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Calling eval_sv() at compiletime with the right context [ In reply to ]
On Mon, 21 Aug 2023 21:02:55 +0100
"Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:

> Problem is everything else is still wrong. I still have no strict and
> no feature flags. Though, I do get warnings. I seem to have this
> bizarre mixture of some state but not the rest.
>
> I suspect I'm somewhat on the right lines here in that I need to
> create a fake COP to put into PL_curcop, but apparently it doesn't
> have the right settings for hints or feature flags.

I've done a bit of further debugging and I'm now even more confused.

I wrote myself a little helper dump function that prints a COP to
stderr, with some decoding. Calling that in a normal BEGIN block in my
unit test gives me an expected result:

COP(0x0x55c16f28bf10)={
CopLINE = 15
CopFILE = <t/01checked.t>
.cop_seq = 12654
.cop_hints = STRICT_REFS|EXPLICIT_STRICT_REFS|EXPLICIT_STRICT_SUBS|EXPLICIT_STRICT_VARS|STRICT_SUBS|STRICT_VARS|UNI_8_BIT|LOCALIZE_HH|UTF8
.cop_features = (bundle) 2
}

I'm quite happy to believe that's correct - feature bundle 2 is also
FEATURE_BUNDLE_511 (from 5.11) and is not as far as bundle 515 for perl
5.15. This appears to match the `use v5.14` statement my code starts
with. Also those hints all sound like they match - the usual strictness
flags, plus oddly UTF8 but I think that comes from `use Test2::V0`. All
good so far.

Next up, I try calling that COP printer again to print the contents of
my fake COP that I generated using newSTATEOP(0, NULL, NULL). Here, I
get:

COP(0x0x55c16f2f05d8)={
CopLINE = 18
CopFILE = <t/01checked.t>
.cop_seq = 12657
.cop_hints = STRICT_REFS|EXPLICIT_STRICT_REFS|EXPLICIT_STRICT_SUBS|EXPLICIT_STRICT_VARS|STRICT_SUBS|STRICT_VARS|UNI_8_BIT|LOCALIZE_HH|UTF8
.cop_features = (CUSTOM) 0x00000000
}

The line and file look correct, the seq is just after the last one, I'm
happy to believe I'm looking at the right COP.

Those hints have been copied - yet, bizarrely, they don't appear to be
actually taking effect in my code. In particular, I know that
STRICT_SUBS is not apparently taking effect, because my code is just a
bareword, yet gets evaluated into its own string (much as barewords do
in the absense of 'use strict').

Also what's going on with that feature bundle? The hints .cop_hints
field implied a feature bundle of FEATURE_BUNDLE_CUSTOM, and yet the
.cop_features bitfield is empty.

I've also tried printing the (maybe not correct?) prevailing PL_curcop
just before I created the new fake one. That one decoded as:

COP(0x0x55f8e53c1a30)={
CopLINE = 18
CopFILE = <t/01checked.t>
.cop_seq = 0
.cop_hints = STRICT_REFS|EXPLICIT_STRICT_REFS|EXPLICIT_STRICT_SUBS|EXPLICIT_STRICT_VARS|STRICT_SUBS|STRICT_VARS|UNI_8_BIT|LOCALIZE_HH|UTF8
.cop_features = (CUSTOM) 0x0009E401
}

Right line/file, no seq (but maybe it doesn't at compiletime? I'm happy
to believe that), same hints (which look correct) and an interesting
value for .cop_features. That's a custom bundle. Some manual decoding
by hand reading the flags in feature.h [*] suggests:
BAREWORD_FILEHANDLES
MULTIDIMENSIONAL
SAY
SIGNATURES
STATE
SWITCH
UNICODE
(This is the 5.14 bundle, plus SIGNATURES which Object::Pad has turned
on, minus INDIRECT which Object::Pad has turned off. So that value
makes sense).

Those features would seem to make sense, but yet they haven't been
copied into the fakecop generated by newSTATEOP().

Most most puzzling. Two separate things that confuse me here.

---

[*] Oh additionally, all the features bits in feature.h are guarded by a
giant #ifdef PERL_CORE block, so I can't see them from a CPAN
module, so I have no hope of writing an automatic decoder debug
function, of the style I usually write for all the other
structures. Hence my having to decode it by hand :sadface:

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Calling eval_sv() at compiletime with the right context [ In reply to ]
On Mon, 21 Aug 2023 21:56:37 +0100
"Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:

> Most most puzzling. Two separate things that confuse me here.

Much further staring combined with some useful chat with TonyC on IRC
leads me to think this just isn't possible currently.

I have raised an Issue about it:

https://github.com/Perl/perl5/issues/21415


In the meantime, I manage to mostly get the behaviour I want by
basically calling newUNOP() to construct an OP_ENTEREVAL and then
manually executing the ops within it. This then behaves much like
Perl's eval(), but with the actual hints and features that the caller
had set.

The code, with all its commented complications, is here

https://metacpan.org/release/PEVANS/Object-Pad-FieldAttr-Checked-0.03/diff/PEVANS%2FObject-Pad-FieldAttr-Checked-0.02#lib/Object/Pad/FieldAttr/Checked.xs

Suffice to say, I think it'd be handy to have that wrapped up in a neat
new flag to eval_sv() ;)

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/