X-Git-Url: http://git.vpit.fr/?a=blobdiff_plain;f=lib%2FSub%2FOp.pm;h=d8a6b738c7461f856af60f1137ac4ac725f9b9b2;hb=efeaaece7f45dd3c188aca0094d050ea242d4bb6;hp=1c783f6fa3504399522cc4dfb27ca20476b3517e;hpb=abd480ecd0ecf811f442448f74cf0afaa7997a77;p=perl%2Fmodules%2FSub-Op.git diff --git a/lib/Sub/Op.pm b/lib/Sub/Op.pm index 1c783f6..d8a6b73 100644 --- a/lib/Sub/Op.pm +++ b/lib/Sub/Op.pm @@ -11,7 +11,7 @@ Sub::Op - Install subroutines as opcodes. =head1 VERSION -Version 0.01 +Version 0.02 =cut @@ -20,7 +20,7 @@ our ($VERSION, @ISA); sub dl_load_flags { 0x01 } BEGIN { - $VERSION = '0.01'; + $VERSION = '0.02'; require DynaLoader; push @ISA, 'DynaLoader'; __PACKAGE__->bootstrap($VERSION); @@ -50,12 +50,15 @@ In your XS file : BOOT: { sub_op_config_t c; - c.name = "reftype"; - c.namelen = sizeof("reftype")-1; - c.pp = scalar_util_reftype; - c.check = 0; - c.ud = NULL; - sub_op_register(aTHX_ &c); + sub_op_init(&c); + c.name = "reftype"; + c.namelen = sizeof("reftype")-1; + c.proto = "$"; + c.protolen = sizeof("$")-1; + c.pp = scalar_util_reftype; + c.check = 0; + c.ud = NULL; + sub_op_register(aTHX_ &c, 0); } In your Perl module file : @@ -147,14 +150,32 @@ my $sw = Variable::Magic::wizard( CORE::warn(@_) unless $_[0] =~ /^Constant subroutine.*redefined/; } if _constant_sub(do { no strict 'refs'; \&$fqn }); no strict 'refs'; - no warnings 'redefine'; + no warnings qw/prototype redefine/; *$fqn = $placeholder; } + { + my $proto = _get_prototype($name); + no strict 'refs'; + Scalar::Util::set_prototype(\&$fqn, $proto); + } + return; }, ); +sub _defined_sub { + my ($fqn) = @_; + my @parts = split /::/, $fqn; + my $name = pop @parts; + my $pkg = ''; + for (@parts) { + $pkg .= $_ . '::'; + return 0 unless do { no strict 'refs'; %$pkg }; + } + return do { no strict 'refs'; defined &{"$pkg$name"} }; +} + sub _tag { my ($pkg, $name) = @_; @@ -221,6 +242,19 @@ C's length, in bytes. =item * +C + +The prototype you want to apply to the subroutine, or C if none. +Allowed to be static. + +=item * + +C + +C's length, in bytes. + +=item * + C The pp function that will be called instead of the subroutine. @@ -246,11 +280,25 @@ An optional user data passed to the C callback. =back -=head2 C +=head2 C + +Initializes the fields of the C object. +For future compatibility, it is required to call this function with your config object before storing your actual values. +It will store safe defaults for members you won't set. + +=head2 C Registers a name and its configuration into L. The caller is responsible for allocating and freeing the C object. -No pointer to it or to its members is kept. +No pointer to it or to its members is kept, except if you pass the flag C in which case the configuration object will be stolen to be stored into L's internal datastructure. + +=head2 C + +Deeply clones the specified C object. + +=head2 C + +Free the memory associated with the specified C object. =head1 PERL API @@ -330,107 +378,6 @@ sub disable { return; } -sub _inject { - my ($pkg, $inject) = @_; - - my $stash = do { no strict 'refs'; \%{"${pkg}::"} }; - - while (my ($meth, $code) = each %$inject) { - next if exists $stash->{$meth} and (*{$stash->{$meth}}{CODE} // 0) == $code; - no strict 'refs'; - *{"${pkg}::$meth"} = $code; - } -} - -sub _defined_sub { - my ($fqn) = @_; - my @parts = split /::/, $fqn; - my $name = pop @parts; - my $pkg = ''; - for (@parts) { - $pkg .= $_ . '::'; - return 0 unless do { no strict 'refs'; %$pkg }; - } - return do { no strict 'refs'; defined &{"$pkg$name"} }; -} - -{ - my $injector; - BEGIN { - $injector = Variable::Magic::wizard( - data => sub { +{ guard => 0, pkg => $_[1], subs => $_[2] } }, - store => sub { - my ($stash, $data, $key) = @_; - - return if $data->{guard}; - local $data->{guard} = 1; - - _inject($data->{pkg}, $data->{subs}); - - return; - }, - ); - } - - sub _monkeypatch { - my %B_OP_inject; - - $B_OP_inject{first} = sub { - if (defined _custom_name($_[0])) { - $_[0] = bless $_[0], 'B::UNOP' unless $_[0]->isa('B::UNOP'); - goto $_[0]->can('first') || die 'oops'; - } - require Carp; - Carp::confess('Calling B::OP->first for something that isn\'t a custom op'); - }; - - $B_OP_inject{can} = sub { - my ($obj, $meth) = @_; - if ($meth eq 'first') { - return undef unless $obj->isa('B::UNOP') or defined _custom_name($obj); - } - $obj->SUPER::can($meth); - }; - - if (_defined_sub('B::OP::type')) { - _inject('B::OP', \%B_OP_inject); - } else { - no strict 'refs'; - Variable::Magic::cast %{'B::OP::'}, $injector, 'B::OP', \%B_OP_inject; - } - - my $B_Deparse_inject = { - pp_custom => sub { - my ($self, $op, $cx) = @_; - my $name = _custom_name($op); - die 'unhandled custom op' unless defined $name; - if ($op->flags & do { no strict 'refs'; &{'B::OPf_STACKED'}() }) { - my $kid = $op->first; - $kid = $kid->first->sibling; # skip ex-list, pushmark - my @exprs; - while (not do { no strict 'refs'; &{'B::Deparse::null'}($kid) }) { - push @exprs, $self->deparse($kid, 6); - $kid = $kid->sibling; - } - my $args = join(", ", @exprs); - return "$name($args)"; - } else { - return $name; - } - }, - }; - - if (_defined_sub('B::Deparse::pp_entersub')) { - _inject('B::Deparse', $B_Deparse_inject); - } else { - no strict 'refs'; - Variable::Magic::cast %{'B::Deparse::'}, $injector, 'B::Deparse', $B_Deparse_inject; - } - } -} - -BEGIN { _monkeypatch() } - =head1 EXAMPLES See the F directory that implements a complete example.