X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDevel%2FDeclare.pm;h=db54af1111ecfe01628430602999d25b460b5dcd;hb=d62484bb99e1e15a0eebf5350bf329063f33fbf5;hp=01bb7d7df10bd45f03958cf2c06ae981fda50d0f;hpb=94caac6e9e3af7e002f7eef2bed2e2bf2bb6d2a8;p=p5sagit%2FDevel-Declare.git diff --git a/lib/Devel/Declare.pm b/lib/Devel/Declare.pm index 01bb7d7..db54af1 100644 --- a/lib/Devel/Declare.pm +++ b/lib/Devel/Declare.pm @@ -6,15 +6,30 @@ use 5.008001; our $VERSION = 0.001000; -use vars qw(%declarators); +# mirrored in Declare.xs as DD_HANDLE_* + +use constant DECLARE_NAME => 1; +use constant DECLARE_PROTO => 2; +use constant DECLARE_NONE => 4; +use constant DECLARE_PACKAGE => 8+1; # name implicit + +use vars qw(%declarators %declarator_handlers); use base qw(DynaLoader); +use Scalar::Util 'set_prototype'; bootstrap Devel::Declare; sub import { - my ($class, @args) = @_; + my ($class, %args) = @_; my $target = caller; - $class->setup_for($target => \@args); + if (@_ == 1) { # "use Devel::Declare;" + no strict 'refs'; + foreach my $name (qw(NAME PROTO NONE PACKAGE)) { + *{"${target}::DECLARE_${name}"} = *{"DECLARE_${name}"}; + } + } else { + $class->setup_for($target => \%args); + } } sub unimport { @@ -26,28 +41,104 @@ sub unimport { sub setup_for { my ($class, $target, $args) = @_; setup(); - $declarators{$target}{$_} = 1 for @$args; + foreach my $key (keys %$args) { + my $info = $args->{$key}; + my ($flags, $sub); + if (ref($info) eq 'ARRAY') { + ($flags, $sub) = @$info; + } elsif (ref($info) eq 'CODE') { + $flags = DECLARE_NAME; + $sub = $info; + } else { + die "Info for sub ${key} must be [ \$flags, \$sub ] or \$sub"; + } + $declarators{$target}{$key} = $flags; + $declarator_handlers{$target}{$key} = $sub; + } } sub teardown_for { my ($class, $target) = @_; delete $declarators{$target}; + delete $declarator_handlers{$target}; teardown(); } -my $temp_pack; my $temp_name; +my $temp_save; sub init_declare { - my ($pack, $use, $name) = @_; - no strict 'refs'; - *{"${pack}::${name}"} = sub (&) { ($pack, $name, $_[0]); }; - ($temp_pack, $temp_name) = ($pack, $name); + my ($usepack, $use, $inpack, $name, $proto) = @_; + my ($name_h, $XX_h, $extra_code) + = $declarator_handlers{$usepack}{$use}->( + $usepack, $use, $inpack, $name, $proto, defined(wantarray) + ); + ($temp_name, $temp_save) = ([], []); + if ($name) { + $name = "${inpack}::${name}" unless $name =~ /::/; + push(@$temp_name, $name); + no strict 'refs'; + push(@$temp_save, \&{$name}); + no warnings 'redefine'; + no warnings 'prototype'; + *{$name} = $name_h; + } + if ($XX_h) { + push(@$temp_name, "${inpack}::X"); + no strict 'refs'; + push(@$temp_save, \&{"${inpack}::X"}); + no warnings 'redefine'; + no warnings 'prototype'; + *{"${inpack}::X"} = $XX_h; + } + if (defined wantarray) { + return $extra_code || '0;'; + } else { + return; + } } sub done_declare { no strict 'refs'; - delete ${"${temp_pack}::"}{$temp_name}; + my $name = pop(@{$temp_name||[]}); + die "done_declare called with no temp_name stack" unless defined($name); + my $saved = pop(@$temp_save); + $name =~ s/(.*):://; + my $temp_pack = $1; + delete ${"${temp_pack}::"}{$name}; + if ($saved) { + no warnings 'prototype'; + *{"${temp_pack}::${name}"} = $saved; + } +} + +sub build_sub_installer { + my ($class, $pack, $name, $proto) = @_; + return eval " + package ${pack}; + my \$body; + sub ${name} (${proto}) :lvalue {\n" + .'$body->(@_); + }; + sub { ($body) = @_; };'; +} + +sub setup_declarators { + my ($class, $pack, $to_setup) = @_; + die "${class}->setup_declarator(\$pack, \\\%to_setup)" + unless defined($pack) && ref($to_setup eq 'HASH'); + foreach my $name (keys %$to_setup) { + my $info = $to_setup->{$name}; + my $flags = $info->{flags} || DECLARE_NAME; + my $run = $info->{run}; + my $compile = $info->{compile}; + my $proto = $info->{proto} || '&'; + my $sub_proto = $proto; + # make all args optional to enable lvalue for DECLARE_NONE + $sub_proto =~ s/;//; $sub_proto = ';'.$sub_proto; + my $installer = $class->build_sub_installer($pack, $name, $proto); + # XXX UNCLEAN + } } =head1 NAME