The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# my $x = ...
package PIL::PPad;

use warnings;
use strict;

sub fixup {
  my $self = shift;

  die unless keys %$self == 3;
  # A SOur should never reach use (SOurs are compiled into __init_ subs).
  die if     $self->{pScope} eq "SOur";
  die unless ref $self->{pSyms} eq "ARRAY";
  die unless $self->{pStmts}->isa("PIL::PStmts");

  # Skip creating a new sub-pad if
  #   a) we're in a subroutine (in this case, we use JS' lexicals) or
  #   b) we're compiling a SLet, STemp (which all use an existing variable).
  if($self->{pScope} =~ /^(SLet|STemp)$/) {
    return bless {
      pScope => $self->{pScope},
      pSyms  => [map {{ fixed => PIL::lookup_var($_->[0]), user => $_->[0] }} @{ $self->{pSyms} }],
      pStmts => $self->{pStmts}->fixup,
    } => "PIL::PPad";
  }

  my $scopeid = $PIL::CUR_LEXSCOPE_ID++;
  my $pad     = {
    map {
      push @PIL::ALL_LEXICALS, "$_->[0]_${scopeid}_$PIL::LEXSCOPE_PREFIX";
      ($_->[0] => "$_->[0]_${scopeid}_$PIL::LEXSCOPE_PREFIX");
    } @{ $self->{pSyms} }
  };

  local @PIL::CUR_LEXSCOPES = (@PIL::CUR_LEXSCOPES, $pad);

  return bless {
    pScope => $self->{pScope},
    pSyms  => [ map {{
      fixed => "$_->[0]_${scopeid}_$PIL::LEXSCOPE_PREFIX",
      user  => $_->[0],
    }} @{ $self->{pSyms} } ],
    pStmts => $self->{pStmts}->fixup,
  } => "PIL::PPad";
}

sub as_js {
  my $self = shift;

  push @PIL::VARS_TO_BACKUP, map { $_->{fixed} } @{ $self->{pSyms} }
    unless $PIL::IN_SUBLIKE;

  # Emit appropriate foo = new PIL2JS.Box(undefined) statements.
  local $_;
  my $decl = $PIL::IN_SUBLIKE ? "var " : "";
  return
    join("; ", map {
      my ($jsname, $qname, $undef) = (
        PIL::name_mangle($_->{fixed}),
        PIL::doublequote($_->{user}),
        PIL::undef_of($_->{fixed}),
      );

      if($self->{pScope} eq "SMy") {
        "$decl$jsname = $undef; pad[$qname] = $jsname";
      } elsif($self->{pScope} eq "SState") {
        push @PIL::ALL_LEXICALS, $_->{fixed};
        "pad[$qname] = $jsname";
      } elsif($self->{pScope} eq "STemp") {
        PIL::fail("Can't use temp variable declarator outside a sub-scope!")
          unless $PIL::IN_SUBLIKE;
        <<EOF;
(function () {
  var backup_container = $jsname;
  var backup_value     = $jsname.FETCH();
  block_leave_hooks.push(function () {
    $jsname = backup_container;
    $jsname.STORE(new PIL2JS.Box.Constant(backup_value));
  });
})()
EOF
      } elsif($self->{pScope} eq "SLet") {
        PIL::fail("Can't use temp variable declarator outside a sub-scope!")
          unless $PIL::IN_SUBLIKE;
        <<EOF;
(function () {
  var backup_container = $jsname;
  var backup_value     = $jsname.FETCH();
  block_leave_hooks.push(function (retval) {
    if(!PIL2JS.cps2normal(
      _26main_3a_3aprefix_3a_3f.FETCH(),
      [PIL2JS.Context.ItemAny, retval]
    ).FETCH())
      $jsname = backup_container;
      $jsname.STORE(new PIL2JS.Box.Constant(backup_value));
  });
})()
EOF
      } else {
        die;
      }
    } @{ $self->{pSyms} }) .
    ";\n" .
    $self->{pStmts}->as_js;
}

1;