home *** CD-ROM | disk | FTP | other *** search
/ PC Professionell 2004 December / PCpro_2004_12.ISO / files / webserver / tsw / TSW_3.4.0.exe / Apache2 / perl / ToObject.pm < prev    next >
Encoding:
Perl POD Document  |  2003-11-12  |  16.2 KB  |  658 lines

  1. # Copyright (c) 1998-2003 by Jonathan Swartz. All rights reserved.
  2. # This program is free software; you can redistribute it and/or modify
  3. # it under the same terms as Perl itself.
  4.  
  5. package HTML::Mason::Compiler::ToObject;
  6.  
  7. use strict;
  8.  
  9. use Params::Validate qw(SCALAR validate);
  10. use HTML::Mason::Tools qw(make_fh taint_is_on);
  11.  
  12. use HTML::Mason::Compiler;
  13. use base qw( HTML::Mason::Compiler );
  14.  
  15. use HTML::Mason::Exceptions( abbr => [qw(wrong_compiler_error system_error)] );
  16.  
  17. use File::Path qw(mkpath rmtree);
  18. use File::Basename qw(dirname);
  19.  
  20. BEGIN
  21. {
  22.     __PACKAGE__->valid_params
  23.     (
  24.      comp_class =>
  25.          { parse => 'string', type => SCALAR, default => 'HTML::Mason::Component',
  26.            descr => "The class into which component objects will be blessed" },
  27.  
  28.      subcomp_class =>
  29.          { parse => 'string', type => SCALAR, default => 'HTML::Mason::Component::Subcomponent',
  30.            descr => "The class into which subcomponent objects will be blessed" },
  31.  
  32.      in_package =>
  33.          { parse => 'string', type => SCALAR, default => 'HTML::Mason::Commands',
  34.            descr => "The package in which component execution will take place" },
  35.  
  36.      preamble =>
  37.          { parse => 'string', type => SCALAR, default => '',
  38.            descr => "A chunk of Perl code to add to the beginning of each compiled component" },
  39.  
  40.      postamble =>
  41.          { parse => 'string', type => SCALAR, default => '',
  42.            descr => "A chunk of Perl code to add to the end of each compiled component" },
  43.  
  44.      use_strict =>
  45.          { parse => 'boolean', type => SCALAR, default => 1,
  46.            descr => "Whether to turn on Perl's 'strict' pragma in components" },
  47.  
  48.          define_args_hash =>
  49.          { parse => 'string', type => SCALAR, default => 'auto',
  50.            regex => qr/^(?:always|auto|never)$/,
  51.            descr => "Whether or not to create the %ARGS hash" },
  52.     );
  53. }
  54.  
  55. use HTML::Mason::MethodMaker
  56.     ( read_write => [ map { [ $_ => __PACKAGE__->validation_spec->{$_} ] }
  57.               qw( comp_class
  58.                           define_args_hash
  59.                           in_package
  60.               postamble
  61.               preamble
  62.                           subcomp_class
  63.               use_strict
  64.                         )
  65.             ],
  66.     );
  67.  
  68.  
  69. sub compile
  70. {
  71.     my $self = shift;
  72.     my %p = @_;
  73.  
  74.     local $self->{comp_class} = delete $p{comp_class} if exists $p{comp_class};
  75.     return $self->SUPER::compile( %p );
  76. }
  77.  
  78. #
  79. # compile_to_file( source => ..., file => ... )
  80. # Save object text in an object file.
  81. #
  82. # We attempt to handle several cases in which a file already exists
  83. # and we wish to create a directory, or vice versa.  However, not
  84. # every case is handled; to be complete, mkpath would have to unlink
  85. # any existing file in its way.
  86. #
  87. sub compile_to_file
  88. {
  89.     my $self = shift;
  90.  
  91.     my %p = validate( @_, {   file => { type => SCALAR },
  92.                 source => { isa => 'HTML::Mason::ComponentSource' } },
  93.             );
  94.  
  95.     my ($file, $source) = @p{qw(file source)};
  96.     my @newfiles = ($file);
  97.  
  98.     if (defined $file && !-f $file) {
  99.     my ($dirname) = dirname($file);
  100.     if (!-d $dirname) {
  101.         unlink($dirname) if (-e _);
  102.         push @newfiles, mkpath($dirname, 0, 0775);
  103.         system_error "Couldn't create directory $dirname: $!"
  104.         unless -d $dirname;
  105.     }
  106.     rmtree($file) if (-d $file);
  107.     }
  108.  
  109.     ($file) = $file =~ /^(.*)/s if taint_is_on;  # Untaint blindly
  110.  
  111.     my $fh = make_fh();
  112.     open $fh, "> $file"
  113.     or system_error "Couldn't create object file $file: $!";
  114.  
  115.     $self->compile( comp_source => $source->comp_source_ref,
  116.             name => $source->friendly_name,
  117.             comp_class => $source->comp_class,
  118.             fh => $fh );
  119.  
  120.     close $fh 
  121.     or system_error "Couldn't close object file $file: $!";
  122.     
  123.     return \@newfiles;
  124. }
  125.  
  126. sub object_id
  127. {
  128.     my $self = shift;
  129.  
  130.     local $self->{comp_class} = '';
  131.  
  132.     return $self->SUPER::object_id;
  133. }
  134.  
  135. sub _output_chunk
  136. {
  137.     my ($self, $fh, $string) = (shift, shift, shift);
  138.     if ($fh)
  139.     {
  140.     print $fh (ref $_ ? $$_ : $_) foreach grep defined, @_;
  141.     }
  142.     else
  143.     {
  144.     $$string .= (ref $_ ? $$_ : $_) foreach @_;
  145.     }
  146. }
  147.  
  148. # There are some really spooky relationships between the variables &
  149. # data members in the compiled_component() routine.
  150.  
  151. sub compiled_component
  152. {
  153.     my ($self, %p) = @_;
  154.     my $c = $self->{current_compile};
  155.     my $obj_text = '';
  156.  
  157.     local $c->{compiled_def} = $self->_compile_subcomponents if %{ $c->{def} };
  158.     local $c->{compiled_method} = $self->_compile_methods if %{ $c->{method} };
  159.  
  160.     # Create the file header to assert creatorship
  161.     my $id = $self->object_id;
  162.     $id =~ s,([\\']),\\$1,g;
  163.     $self->_output_chunk($p{fh}, \$obj_text, "# MASON COMPILER ID: $id\n");
  164.  
  165.     # Some preamble stuff, including 'use strict', 'use vars', and <%once> block
  166.     my $header = $self->_make_main_header;
  167.     $self->_output_chunk($p{fh}, \$obj_text, $header);
  168.  
  169.  
  170.     my $params = $self->_component_params;
  171.  
  172.     $params->{compiler_id} = "'$id'";
  173.     $params->{load_time} = time;
  174.  
  175.     $params->{subcomps} = '\%_def' if %{ $c->{def} };
  176.     $params->{methods} = '\%_method' if %{ $c->{method} };
  177.  
  178.     if ( $self->_blocks('shared') )
  179.     {
  180.     my %subs;
  181.     while ( my ($name, $pref) = each %{ $c->{compiled_def} } )
  182.     {
  183.         my $key = "subcomponent_$name";
  184.         $subs{$key} = $pref->{code};
  185.         $pref->{code} = "sub {\n\$m->call_dynamic('$key',\@_)\n}";
  186.     }
  187.     while (my ($name, $pref) = each %{ $c->{compiled_method} } )
  188.     {
  189.         my $key = "method_$name";
  190.         $subs{$key} = $pref->{code};
  191.         $pref->{code} = "sub {\n\$m->call_dynamic( '$key', \@_ )\n}";
  192.     }
  193.     $subs{main} = $params->{code};
  194.     $params->{code} = "sub {\n\$m->call_dynamic( 'main', \@_ )\n}";
  195.  
  196.     $params->{dynamic_subs_init} =
  197.         join '', ( "sub {\n",
  198.                $self->_blocks('shared'),
  199.                "return {\n",
  200.                map( "'$_' => $subs{$_},\n", sort keys %subs ),
  201.                "\n}\n}"
  202.              );
  203.     }
  204.  
  205.     $self->_output_chunk($p{fh}, \$obj_text, $self->_subcomponents_footer);
  206.     $self->_output_chunk($p{fh}, \$obj_text, $self->_methods_footer);
  207.  
  208.  
  209.  
  210.     $params->{object_size} = 0;
  211.     $params->{object_size} += length for ($header, %$params);
  212.  
  213.     $self->_output_chunk($p{fh}, \$obj_text,
  214.              $self->_constructor( $self->comp_class,
  215.                           $params ),
  216.              ';',
  217.             );
  218.  
  219.     return \$obj_text;
  220. }
  221.  
  222. sub assert_creatorship
  223. {
  224.     my ($self, $p) = @_;
  225.     my $id;
  226.     if ($p->{object_code}) {
  227.     # Read the object code as a string
  228.  
  229.     ($id) = ${$p->{object_code}} =~ /\A# MASON COMPILER ID: (\S+)$/m
  230.         or wrong_compiler_error "Couldn't find a Compiler ID in compiled code.";
  231.     } else {
  232.     # Open the object file and read its first line
  233.  
  234.     my $fh = make_fh();
  235.     open $fh, $p->{object_file} or die "Can't read $p->{object_file}: $!";
  236.     ($id) = <$fh> =~ /\A# MASON COMPILER ID: (\S+)$/m
  237.         or wrong_compiler_error "Couldn't find a Compiler ID in $p->{object_file}.";
  238.     close $fh;
  239.     }
  240.     
  241.     wrong_compiler_error 'This object file was created by an incompatible Compiler or Lexer.  Please remove the component files in your object directory.'
  242.     unless $id eq $self->object_id;
  243. }
  244.  
  245. sub _compile_subcomponents
  246. {
  247.     my $self = shift;
  248.  
  249.     return $self->_compile_subcomponents_or_methods('def');
  250. }
  251.  
  252. sub _compile_methods
  253. {
  254.     my $self = shift;
  255.  
  256.     return $self->_compile_subcomponents_or_methods('method');
  257. }
  258.  
  259. sub _compile_subcomponents_or_methods
  260. {
  261.     my $self = shift;
  262.     my $type = shift;
  263.  
  264.     my %compiled;
  265.     foreach ( keys %{ $self->{current_compile}{$type} } )
  266.     {
  267.     local $self->{current_compile} = $self->{current_compile}{$type}{$_};
  268.     local $self->{current_compile}->{in_named_block} = {type => $type, name => $_};
  269.     $compiled{$_} = $self->_component_params;
  270.     }
  271.  
  272.     return \%compiled;
  273. }
  274.  
  275. sub _make_main_header
  276. {
  277.     my $self = shift;
  278.  
  279.     my $pkg = $self->in_package;
  280.  
  281.     return join '', ( "package $pkg;\n",
  282.               $self->use_strict ? "use strict;\n" : "no strict;\n",
  283.               sprintf( "use vars qw(\%s);\n",
  284.                    join ' ', '$m', $self->allow_globals ),
  285.               $self->_blocks('once'),
  286.             );
  287. }
  288.  
  289. sub _subcomponents_footer
  290. {
  291.     my $self = shift;
  292.  
  293.     return $self->_subcomponent_or_method_footer('def');
  294. }
  295.  
  296. sub _methods_footer
  297. {
  298.     my $self = shift;
  299.  
  300.     return $self->_subcomponent_or_method_footer('method');
  301. }
  302.  
  303. sub _subcomponent_or_method_footer
  304. {
  305.     my $self = shift;
  306.     my $c = $self->{current_compile};
  307.     my $type = shift;
  308.  
  309.     return '' unless %{ $c->{$type} };
  310.  
  311.     return join('',
  312.         "my %_$type =\n(\n",
  313.         map( {("'$_' => " ,
  314.                $self->_constructor( $self->{subcomp_class},
  315.                         $c->{"compiled_$type"}{$_} ) ,
  316.                ",\n")} keys %{ $c->{"compiled_$type"} } ) ,
  317.         "\n);\n"
  318.            );
  319. }
  320.  
  321. sub _constructor
  322. {
  323.     my ($self, $class, $params) = @_;
  324.  
  325.     return ("${class}->new(\n",
  326.         map( {("'$_' => ", $params->{$_}, ",\n")} sort keys %$params ),
  327.         "\n)\n",
  328.        );
  329. }
  330.  
  331. sub _component_params
  332. {
  333.     my $self = shift;
  334.  
  335.     my %params = ( code => join ( '', "sub {\n", $self->_body, "}" ),
  336.          );
  337.  
  338.     $params{flags} = join '', "{\n", $self->_flags, "\n}"
  339.         if keys %{ $self->{current_compile}{flags} };
  340.  
  341.     $params{attr}  = join '', "{\n", $self->_attr, "\n}"
  342.         if keys %{ $self->{current_compile}{attr} };
  343.  
  344.     $params{declared_args} = join '', "{\n", $self->_declared_args, "\n}"
  345.     if @{ $self->{current_compile}{args} };
  346.  
  347.     $params{has_filter} = 1 if $self->_blocks('filter');
  348.  
  349.     return \%params;
  350. }
  351.  
  352. sub _body
  353. {
  354.     my $self = shift;
  355.  
  356.     return join '', ( $self->preamble,
  357.                       $self->_set_request,
  358.               $self->_arg_declarations,
  359.                       $self->_filter,
  360.               "\$m->debug_hook( \$m->current_comp->path ) if ( \%DB:: );\n\n",
  361.               $self->_blocks('init'),
  362.               $self->{current_compile}{body},
  363.               $self->_blocks('cleanup'),
  364.               $self->postamble,
  365.               "return undef;\n",
  366.             );
  367. }
  368.  
  369. sub _set_request
  370. {
  371.     my $self = shift;
  372.  
  373.     return if $self->in_package eq 'HTML::Mason::Commands';
  374.  
  375.     return 'local $' . $self->in_package . '::m = $HTML::Mason::Commands::m;' . "\n";
  376. }
  377.  
  378. my %coercion_funcs = ( '@' => 'HTML::Mason::Tools::coerce_to_array',
  379.                '%' => 'HTML::Mason::Tools::coerce_to_hash',
  380.              );
  381. sub _arg_declarations
  382. {
  383.     my $self = shift;
  384.  
  385.     my $init;
  386.     my @args_hash;
  387.     my $pos;
  388.     my @req_check;
  389.     my @decl;
  390.     my @assign;
  391.  
  392.     my $define_args_hash = $self->_define_args_hash;
  393.  
  394.     unless ( @{ $self->{current_compile}{args} } )
  395.     {
  396.         return unless $define_args_hash;
  397.  
  398.         return ( "my \%ARGS;\n",
  399.                  "{ local \$^W; \%ARGS = \@_ unless (\@_ % 2); }\n"
  400.                );
  401.     }
  402.  
  403.     $init = <<'EOF';
  404. HTML::Mason::Exception::Params->throw
  405.     ( error =>
  406.       "Odd number of parameters passed to component expecting name/value pairs"
  407.     ) if @_ % 2;
  408. EOF
  409.  
  410.     if ( $define_args_hash )
  411.     {
  412.         @args_hash = "my \%ARGS = \@_;\n";
  413.     }
  414.  
  415.     # opening brace will be closed later.  we want this in a separate
  416.     # block so that the rest of the component can't see %pos
  417.     $pos = <<'EOF';
  418. {
  419.     my %pos;
  420.     for ( my $x = 0; $x < @_; $x += 2 )
  421.     {
  422.         $pos{ $_[$x] } = $x + 1;
  423.     }
  424. EOF
  425.  
  426.     my @required =
  427.         ( map { $_->{name} }
  428.           grep { ! defined $_->{default} }
  429.           @{ $self->{current_compile}{args} }
  430.         );
  431.  
  432.     if (@required)
  433.     {
  434.         # just to be sure
  435.         local $" = ' ';
  436.         @req_check = <<"EOF";
  437.  
  438.     foreach my \$arg ( qw( @required ) )
  439.     {
  440.         HTML::Mason::Exception::Params->throw
  441.             ( error => "no value sent for required parameter '\$arg'" )
  442.                 unless exists \$pos{\$arg};
  443.     }
  444. EOF
  445.     }
  446.  
  447.     foreach ( @{ $self->{current_compile}{args} } )
  448.     {
  449.     my $var_name = "$_->{type}$_->{name}";
  450.     push @decl, $var_name;
  451.  
  452.         my $arg_in_array = "\$_[ \$pos{'$_->{name}'} ]";
  453.  
  454.     my $coerce;
  455.     if ( $coercion_funcs{ $_->{type} } )
  456.     {
  457.         $coerce = $coercion_funcs{ $_->{type} } . "( $arg_in_array, '$var_name')";
  458.     }
  459.     else
  460.     {
  461.         $coerce = $arg_in_array;
  462.     }
  463.  
  464.     push @assign, "#line $_->{line} $_->{file}\n"
  465.         if defined $_->{line} && defined $_->{file} && $self->use_source_line_numbers;
  466.  
  467.     if ( defined $_->{default} )
  468.     {
  469.         my $default_val = $_->{default};
  470.         # allow for comments after default declaration
  471.         $default_val .= "\n" if defined $_->{default} && $_->{default} =~ /\#/;
  472.  
  473.         push @assign, <<"EOF";
  474.      $var_name = exists \$pos{'$_->{name}'} ? $coerce : $default_val;
  475. EOF
  476.     }
  477.     else
  478.     {
  479.         push @assign,
  480.         "    $var_name = $coerce;\n";
  481.     }
  482.     }
  483.  
  484.     my $decl = 'my ( ';
  485.     $decl .= join ', ', @decl;
  486.     $decl .= " );\n";
  487.  
  488.     # closing brace closes opening of @pos
  489.     return $init, @args_hash, $decl, $pos, @req_check, @assign, "}\n";
  490. }
  491.  
  492. sub _define_args_hash
  493. {
  494.     my $self = shift;
  495.  
  496.     return 1 if $self->define_args_hash eq 'always';
  497.     return 0 if $self->define_args_hash eq 'never';
  498.  
  499.     foreach ( $self->preamble,
  500.               $self->_blocks('filter'),
  501.               $self->_blocks('init'),
  502.               $self->{current_compile}{body},
  503.               $self->_blocks('cleanup'),
  504.               $self->postamble,
  505.               grep { defined } map { $_->{default} } @{ $self->{current_compile}{args} }
  506.             )
  507.     {
  508.         return 1 if /ARGS/;
  509.     }
  510. }
  511.  
  512. sub _filter
  513. {
  514.     my $self = shift;
  515.  
  516.     my @filter;
  517.     @filter = $self->_blocks('filter')
  518.         or return;
  519.  
  520.     return ( join '',
  521.              "\$m->current_comp->filter( sub { local \$_ = shift;\n",
  522.              ( join ";\n", @filter ),
  523.              ";\n",
  524.              "return \$_;\n",
  525.              "} );\n",
  526.            );
  527.  
  528. }
  529.  
  530. sub _flags
  531. {
  532.     my $self = shift;
  533.  
  534.     return $self->_flags_or_attr('flags');
  535. }
  536.  
  537. sub _attr
  538. {
  539.     my $self = shift;
  540.  
  541.     return $self->_flags_or_attr('attr');
  542. }
  543.  
  544. sub _flags_or_attr
  545. {
  546.     my $self = shift;
  547.     my $type = shift;
  548.  
  549.     return join "\n,", ( map { "$_ => $self->{current_compile}{$type}{$_}" }
  550.              keys %{ $self->{current_compile}{$type} } );
  551. }
  552.  
  553. sub _declared_args
  554. {
  555.     my $self = shift;
  556.  
  557.     my @args;
  558.  
  559.     foreach my $arg ( sort {"$a->{type}$a->{name}" cmp "$b->{type}$b->{name}" }
  560.               @{ $self->{current_compile}{args} } )
  561.     {
  562.     my $def = defined $arg->{default} ? "$arg->{default}" : 'undef';
  563.     $def =~ s,([\\']),\\$1,g;
  564.     $def = "'$def'" unless $def eq 'undef';
  565.  
  566.     push @args, "  '$arg->{type}$arg->{name}' => { default => $def }";
  567.     }
  568.  
  569.     return join ",\n", @args;
  570. }
  571.  
  572. 1;
  573.  
  574. __END__
  575.  
  576. =head1 NAME
  577.  
  578. HTML::Mason::Compiler::ToObject - A Compiler subclass that generates Mason object code
  579.  
  580. =head1 SYNOPSIS
  581.  
  582.   my $compiler = HTML::Mason::Compiler::ToObject->new;
  583.  
  584.   my $object_code = $compiler->compile( comp_source => $source, name => $comp_name );
  585.  
  586. =head1 DESCRIPTION
  587.  
  588. This Compiler subclass generates Mason object code (Perl code).  It is
  589. the default Compiler class used by Mason.
  590.  
  591. =head1 PARAMETERS TO THE new() CONSTRUCTOR
  592.  
  593. All of these parameters are optional.
  594.  
  595. =over
  596.  
  597. =item comp_class
  598.  
  599. The class into which component objects are blessed.  This defaults to
  600. L<HTML::Mason::Component|HTML::Mason::Component>.
  601.  
  602. =item subcomp_class
  603.  
  604. The class into which subcomponent objects are blessed.  This defaults
  605. to L<HTML::Mason::Component::Subcomponent|HTML::Mason::Component::Subcomponent>.
  606.  
  607. =item in_package
  608.  
  609. This is the package in which a component's code is executed.  For
  610. historical reasons, this defaults to C<HTML::Mason::Commands>.
  611.  
  612. =item preamble
  613.  
  614. Text given for this parameter is placed at the beginning of each component. See also L<postamble|HTML::Mason::Params/postamble>.
  615.  
  616. =item postamble
  617.  
  618. Text given for this parameter is placed at the end of each component. See also L<preamble|HTML::Mason::Params/preamble>.
  619.  
  620. =item use_strict
  621.  
  622. True or false, default is true. Indicates whether or not a given
  623. component should C<use strict>.
  624.  
  625. =item define_args_hash
  626.  
  627. One of "always", "auto", or "never".  This determines whether or not
  628. an C<%ARGS> hash is created in components.  If it is set to "always",
  629. one is always defined.  If set to "never", it is never defined.
  630.  
  631. The default, "auto", will cause the hash to be defined only if some
  632. part of the component contains the string "ARGS".  This is somewhat
  633. crude, and may result in some false positives, but this is preferable
  634. to false negatives.
  635.  
  636. Not defining the args hash means that we can avoid copying component
  637. arguments, which can save memory and slightly improve execution speed.
  638.  
  639. =back
  640.  
  641. =head1 METHODS
  642.  
  643. This class is primarily meant to be used by the Interpreter object,
  644. and as such has a very limited public API.
  645.  
  646. =over
  647.  
  648. =item compile (comp_source => $source, name => $name, comp_class = $comp_class)
  649.  
  650. This method will take component source and return the compiled object
  651. code for that source.  The C<comp_source> and C<name> parameters are
  652. optional.  The C<comp_class> can be used to change the component class
  653. for this one comonent.
  654.  
  655. =back
  656.  
  657. =cut
  658.