=back
+=head2 $B::overlay
+
+Although the optree is read-only, there is an overlay facility that allows
+you to override what values the various B::*OP methods return for a
+particular op. C<$B::overlay> should be set to reference a two-deep hash:
+indexed by OP address, then method name. Whenever a an op method is
+called, the value in the hash is returned if it exists. This facility is
+used by B::Deparse to "undo" some optimisations. For example:
+
+
+ local $B::overlay = {};
+ ...
+ if ($op->name eq "foo") {
+ $B::overlay->{$$op} = {
+ name => 'bar',
+ next => $op->next->next,
+ };
+ }
+ ...
+ $op->name # returns "bar"
+ $op->next # returns the next op but one
+
+
=head1 AUTHOR
Malcolm Beattie, C<mbeattie@sable.ox.ac.uk>
return opsv;
}
+
+static SV *
+get_overlay_object(pTHX_ const OP *o, const char * const name, U32 namelen)
+{
+ HE *he;
+ SV **svp;
+ SV *key;
+ SV *sv =get_sv("B::overlay", 0);
+ if (!sv || !SvROK(sv))
+ return NULL;
+ sv = SvRV(sv);
+ if (SvTYPE(sv) != SVt_PVHV)
+ return NULL;
+ key = newSViv(PTR2IV(o));
+ he = hv_fetch_ent((HV*)sv, key, 0, 0);
+ SvREFCNT_dec(key);
+ if (!he)
+ return NULL;
+ sv = HeVAL(he);
+ if (!sv || !SvROK(sv))
+ return NULL;
+ sv = SvRV(sv);
+ if (SvTYPE(sv) != SVt_PVHV)
+ return NULL;
+ svp = hv_fetch((HV*)sv, name, namelen, 0);
+ if (!svp)
+ return NULL;
+ sv = *svp;
+ return sv;
+}
+
+
static SV *
make_sv_object(pTHX_ SV *sv)
{
PPCODE:
if (ix < 0 || ix > 46)
croak("Illegal alias %d for B::*OP::next", (int)ix);
- offset = op_methods[ix].offset;
+ ret = get_overlay_object(aTHX_ o,
+ op_methods[ix].name, op_methods[ix].namelen);
+ if (ret) {
+ ST(0) = ret;
+ XSRETURN(1);
+ }
/* handle non-direct field access */
+ offset = op_methods[ix].offset;
if (offset < 0) {
switch (ix) {
#ifdef USE_ITHREADS
'different COP->stashoff for different stashes';
}
+
+# Test $B::overlay
+{
+ my $methods = {
+ BINOP => [ qw(last) ],
+ COP => [ qw(arybase cop_seq file filegv hints hints_hash io
+ label line stash stashpv
+ stashoff warnings) ],
+ LISTOP => [ qw(children) ],
+ LOGOP => [ qw(other) ],
+ LOOP => [ qw(lastop nextop redoop) ],
+ OP => [ qw(desc flags name next opt ppaddr private sibling
+ size spare targ type) ],
+ PADOP => [ qw(gv padix sv) ],
+ PMOP => [ qw(code_list pmflags pmoffset pmreplroot pmreplstart pmstash pmstashpv precomp reflags) ],
+ PVOP => [ qw(pv) ],
+ SVOP => [ qw(gv sv) ],
+ UNOP => [ qw(first) ],
+ };
+
+ my $overlay = {};
+ my $op = B::svref_2object(sub { my $x = 1 })->ROOT;
+
+ for my $class (sort keys %$methods) {
+ for my $meth (@{$methods->{$class}}) {
+ my $full = "B::${class}::$meth";
+ die "Duplicate method '$full'\n"
+ if grep $_ eq $full, @{$overlay->{$meth}};
+ push @{$overlay->{$meth}}, "B::${class}::$meth";
+ }
+ }
+
+ {
+ local $B::overlay; # suppress 'used once' warning
+ local $B::overlay = { $$op => $overlay };
+
+ for my $class (sort keys %$methods) {
+ bless $op, "B::$class"; # naughty
+ for my $meth (@{$methods->{$class}}) {
+ if ($op->can($meth)) {
+ my $list = $op->$meth;
+ ok(defined $list
+ && ref($list) eq "ARRAY"
+ && grep($_ eq "B::${class}::$meth", @$list),
+ "overlay: B::$class $meth");
+ }
+ else {
+ pass("overlay: B::$class $meth (skipped; no method)");
+ }
+ }
+ }
+ }
+ # B::overlay should be disabled again here
+ is($op->name, "leavesub", "overlay: orig name");
+}
+
done_testing();