/usr/local/CPAN/Java-JVM-Classfile/Java/JVM/Classfile/Perl.pm


package Java::JVM::Classfile::Perl;

use strict;
use vars qw($VERSION @ISA);
use Java::JVM::Classfile;

$VERSION = '0.16';

sub new {
  my $class = shift;
  my $filename = shift;
  my $self = {};

  my $c = Java::JVM::Classfile->new($filename);
  $self->{_class} = $c;
  bless $self, $class;
  return $self;
}

sub as_perl {
  my $self = shift;
  my $c = $self->{_class};
  my $code;
  my @cpool = @{$c->constant_pool};

  $code .= q|
package java::io::PrintStream;
sub new {
    my $class = shift;
    my $self = {};
    return bless $self, $class;
}
sub print {
    my $self = shift;
    print shift();
}
sub println {
    my $self = shift;
    my $arg = shift;
    print $arg if defined $arg;
    print "\n";
}

package java::lang::Integer;
sub parseInt {
    my($class, $s) = @_;
    return $s + 0;
}

package java::lang::System;
sub out {
    return java::io::PrintStream->new();
}

package java::lang::String;
sub new {
    my $class = shift;
    my $self = {};
    $self->{value} = "";
    return bless $self, $class;
}

sub valueOf {
    my $class = shift;
    return $_[0];
}

package java::lang::StringBuffer;
sub new {
    my $class = shift;
    my $self = {};
    $self->{value} = "";
    return bless $self, $class;
}
sub append {
    my $self = shift;
    my $text = shift;
    $self->{value} .= $text;
    return $self;
}
sub toString {
    my $self = shift;
    return $self->{value};
}
|;

  $code .= "\npackage " . $c->class . ";\n";

  $code .= "no warnings 'recursion';\n";

  die "Subclasses not supported!" if $c->superclass ne "java/lang/Object";

  foreach my $method (@{$c->methods}) {
    next if $method->name eq '<init>';
    $code .= "\nsub " . $method->name . " {\n";

    $code .= "my \@stack;\n";
    $code .= "my \$class = shift();\n";
    $code .= "my \@locals = \@_;\n";
    $code .= "my(\$o, \$p, \$return, \@in);\n";
    $code .= "my \@params;\n";
#    $code .= qq|print "locals ";\n|;
#    $code .= qq|print join("# ", \@\$locals[0]) . "\\n";\n|;
    foreach my $att (@{$method->attributes}) {
      my $name = $att->name;
      my $value = $att->value;
      next unless $name eq 'Code';
      foreach my $instruction (@{$value->code}) {
	my $label = $instruction->label;
	my $op = $instruction->op;
	my @args = @{$instruction->args};
	$code .= "$label:\n" if defined $label;
	my $javacode = "\t$op\t" . (join ", ", @{$instruction->args});
	$code .= "# $javacode\n";
#	$code .= qq|print "\@stack / code = $javacode\\n";\n|;
	if ($op eq 'getstatic') {
	  my $class = $args[0];
	  $class =~ s|/|::|g;
	  my $field = $args[1];
	  $code .= "push \@stack, $class->$field;\n";
	} elsif ($op eq 'new') {
	  my $class = $args[0];
	  $class =~ s|/|::|g;
	  $code .= "push \@stack, $class->new();\n";
	} elsif ($op eq 'invokevirtual') {
	  my $class = $args[0];
	  $class =~ s|/|::|g;
	  my $method = $args[1];
	  my $signature = $args[2];
          $code .= $self->invokevirtual_code($class, $method, $signature);
	} elsif ($op eq 'invokestatic') {
	  my $class = $args[0];
	  $class =~ s|/|::|g;
	  my $method = $args[1];
	  my $signature = $args[2];
	  my($in, $out) = $signature =~ /^\((.*?)\)(.*?)$/;
          $code .= $self->invokestatic_code($class, $method, $signature);
	} elsif ($op eq 'invokespecial') {
	  $code .= "pop \@stack;\n";
	} elsif ($op eq 'ldc') {
	  my $arg = $args[0];
	  $code .= "push \@stack, '$arg';\n";
	} elsif ($op eq 'ldc2_w') {
	  my $arg = $args[0] << 8 | $args[1]; # See JVM specs
	  $code .= "push \@stack, ".$cpool[$arg]->values->[0].";\n";
	  $code .= "push \@stack, 'FAKE VALUE FOR LONG';\n";
	} elsif ($op eq 'bipush' or $op eq 'sipush') {
	  my $arg = $args[0];
	  $code .= "push \@stack, $arg;\n";
	} elsif ($op eq 'return') {
	  $code .= "return;\n";
	} elsif ($op =~ /^[fldai]return$/) {
	  $code .= "return pop(\@stack);\n";
	} elsif ($op =~ /^[li]const_(\d)/) {
	  $code .= "push \@stack, $1;\n";
	} elsif ($op =~ /^[fai]store_(\d)/) {
	  $code .= "\$locals[$1] = pop \@stack;\n";
	} elsif ($op =~ /^[ld]store_(\d)/) {
	  $code .= "pop \@stack;\n";
	  $code .= "\$locals[$1] = pop \@stack;\n";
	} elsif ($op =~ /^[fai]store/) {
	  my $i = $args[0];
	  $code .= "\$locals[$i] = pop \@stack;\n";
	} elsif ($op =~ /^[ld]store/) {
	  my $i = $args[0];
	  $code .= "\$locals[$i] = pop \@stack;\n";
	  $code .= "pop \@stack;\n";
	} elsif ($op =~ /[fai]load_(\d)/) {
	  $code .= "push \@stack, \$locals[$1];\n";
	} elsif ($op =~ /[ld]load_(\d)/) {
	  $code .= "push \@stack, \$locals[$1];\n";
	  $code .= "push \@stack, 'FAKE VALUE FOR LONGS';\n";
	} elsif ($op =~ /^[fai]load$/) {
	  my $i = $args[0];
	  $code .= "push \@stack, \$locals[$i];\n";
	} elsif ($op =~ /^[ld]load$/) {
	  my $i = $args[0];
	  $code .= "push \@stack, \$locals[$i];\n";
	  $code .= "push \@stack, 'FAKE VALUE FOR LONGS';\n";
	} elsif ($op eq 'goto') {
	  my $label = $args[0];
	  $code .= "goto $label;\n";
	} elsif ($op eq 'dup') {
	  $code .= "push \@stack, \$stack[-1];\n";
	} elsif ($op =~ /^[fi]add$/) {
	  $code .= "push \@stack, (pop \@stack) + (pop \@stack);\n";
	} elsif ($op =~ /^[ld]add$/) {
	  $code .= qq|pop \@stack;
\$o = pop \@stack;
pop \@stack;
\$o += pop \@stack;
push \@stack, \$o;\n|;
	} elsif ($op =~ /^[fldi]sub/) {
	  $code .= "push \@stack, - (pop \@stack) + (pop \@stack);\n";
	} elsif ($op =~ /^[fldi]mul/) {
	  $code .= "push \@stack, (pop \@stack) * (pop \@stack);\n";
	} elsif ($op eq 'aaload') {
	  $code .= qq|\$o = pop \@stack;
	  	  my \$array = pop \@stack;
	  	  push \@stack, \$array->[\$o];\n|;
	} elsif ($op eq 'iinc') {
	  my $i = $args[0];
	  my $n = $args[1];
	  $code .= "\$locals[$i] += $n;\n";
	} elsif ($op eq 'if_icmplt') {
	  my $label = $args[0];
	  $code .= "goto $label if (pop \@stack) > (pop \@stack);\n";
	} elsif ($op eq 'if_icmpge') {
	  my $label = $args[0];
	  $code .= "goto $label if (pop \@stack) <= (pop \@stack);\n";
	} elsif ($op eq 'ifne') {
	  my $label = $args[0];
	  $code .= "goto $label if (pop \@stack);\n";
	} else {
	  $code .= "# ?\n";
	}
      }
    }
    $code .= "}\n\n";
  }
#  $code .= qq|print join(", ", \@ARGV) . "\\n";\n|;
  $code .= $c->class . "->main([\@ARGV]);\n";
  return $code;
}

# Invoking static methods
sub invokestatic_code {
    my $self = shift;
    my ($class, $method, $signature) = @_;

    my ($code, $incount, $doubles);
    my($in, $out) = $signature =~ /^\((.*?)\)(.*?)$/;

    $in =~ s/L[^;]*;/L/g;
    $incount = () = $in =~ /[FIL]/g;
    $doubles = () = $in =~ /[JD]/g;
    $incount += 2*$doubles;
    $out = "" if defined($out) && $out eq 'V';
    if ($in) {
	$code .= qq|\@params = splice(\@stack,-$incount);
\$return = $class->$method(\@params); # $in / $out\n|;
    } else {
	$code .= "\$return = $class->$method(); # $in / $out\n";
    }
    $code .= "push \@stack, \$return;\n" if $out;
 
    return $code;
}

# Invoking virtual methods
sub invokevirtual_code {
    my $self = shift;
    my ($class, $method, $signature) = @_;

    my ($code, $incount, $doubles);
    my($in, $out) = $signature =~ /^\((.*?)\)(.*?)$/;
    $in =~ s/L[^;]*;/L/g;
    $incount = () = $in =~ /[FIL]/g;
    $doubles = () = $in =~ /[JD]/g;
    $incount += 2*$doubles;
    $out = "" if defined($out) && $out eq 'V';

    if ($in) {
	$code .= qq|\@params = splice(\@stack,-$incount);
\$p = pop \@stack;
\$return = \$p->$method(\@params); # $in / $out\n|;
    } else {
	$code .= "\$return = (pop \@stack)->$method(); # $in / $out\n";
    }
    $code .= "push \@stack, \$return;\n" if $out;

    return $code;
}

1;