Sophie

Sophie

distrib > Mandriva > 2007.0 > i586 > media > contrib-release > by-pkgid > 4c9f17ec5da473f7fb52041bb9197c5a > files > 118

kaffe-devel-1.1.8-0.20060723.1mdv2007.0.i586.rpm

#
# Functions for reading in and writing out a Java .class file.
# Also does a bit of consistency checking of the file.
#
# The only really nasty thing I've done (because of poor perl skils more
# than anything else) is to make the %class a local() in a number of
# places so that the check routines can see it.
#
# Class structure:  Generally references to hashes. Tables are implemented as arrays.
#
# TODO:
#	make a &checkClass() function.
#	change a lot of 'local's to 'my's. (not local(%class), though)
#	Make CLASSIN and CLASSOUT parameters to read/write functions.
#	POD documentation
#	Cannot handle modifying float values.  I can read and decode, but don't
#	have the math to convert back to a binary format (both floats and doubles).

#
# Copyright (c) 1999 University of Utah CSL.
#
# This file is distributed under the terms of the GNU Public License.
#


package JavaClass;

###
### Define constants for Java Classes
###

*classMagic = \0xcafebabe;	# The magic header every .class file starts with

## The magic identifiers for entries in the .class Constant Table.
*CONSTANT_Class = \7;
*CONSTANT_FieldRef = \9;
*CONSTANT_MethodRef = \10;
*CONSTANT_InterfaceMethodRef = \11;
*CONSTANT_String = \8;
*CONSTANT_Integer = \3;
*CONSTANT_Float = \4;
*CONSTANT_Long = \5;
*CONSTANT_Double = \6;
*CONSTANT_NameAndType = \12;
*CONSTANT_Utf8 = \1;

## String names associated with each type of Constant Table entry.
%CONSTANTNames = (
    $CONSTANT_Class => "Class",
    $CONSTANT_FieldRef => "Field",
    $CONSTANT_MethodRef => "Method",
    $CONSTANT_InterfaceMethodRef => "Inteface Method",
    $CONSTANT_String => "String",
    $CONSTANT_Float => "Float",
    $CONSTANT_Integer => "Integer",
    $CONSTANT_Double => "Double",
    $CONSTANT_Long => "Long",
    $CONSTANT_NameAndType => "Name&Type",
    $CONSTANT_Utf8 => "Utf8"
    );

## String names for the shorthand used in signatures
$sig{'V'} = 'void';
$sig{'I'} = 'int';
$sig{'J'} = 'long';
$sig{'Z'} = 'boolean';
$sig{'F'} = 'float';
$sig{'D'} = 'double';
$sig{'B'} = 'byte';
$sig{'S'} = 'short';
$sig{'C'} = 'char';

## Access control flags for classes, methods and fields.
*ACC_PUBLIC    = \0x0001;
*ACC_PRIVATE   = \0x0002;
*ACC_PROTECTED = \0x0004;
*ACC_STATIC    = \0x0008;
*ACC_FINAL     = \0x0010;
*ACC_SUPER     = \0x0020;		# class only
*ACC_SYNCHRONIZED = \0x0020;		# field/method
*ACC_VOLATILE  = \0x0040;
*ACC_TRANSIENT = \0x0080;
*ACC_INTERFACE = \0x0200;
*ACC_ABSTRACT  = \0x0400;
*ACC_NATIVE    = \0x0100;
*ACC_STRICT    = \0x0800;

*ACC_UNKNOWN   = \0xF000;

###
###  Global variables
###

# Control the verbosity of &printClass()
$detailedFields = 0;
$detailedMethods = 0;

###
### Conversion functions
###

## parseJavaSig() takes a single argument, a single Java-internal
## method signature and returns a list ($package, $return, $class,
## $method, @args) where the items have been converted to a more
## source-like format (e.g., english).
sub parseJavaSig() {
  ## Parameters
  my $jsig = shift;

  ## Local variables
  my $class = '';
  my $package = '';
  my $method = '';
  my @args = ();
  my $ret = '';

  ## Temporaries
  my $depth = 0;
  my $repct = 0;
  my $arg = '';

  ### First is the class (all chars until a ".")
  $jsig =~ s/^([^.]*).//;
  $class = $1;
  $class =~ s,/,.,g; # / -> .

  # Peel the package name out of the class name (everything before last ".")
  if ($class =~ m/(.*)\.[^\.]*$/) {
    $package = $1;
  }

  ### Second comes the method name (all chars until a left paren)
  $jsig =~ s/^([^\(]*)\(//;
  $method = $1;

  ### Now the arguments
 SIGPARSE:
  while(1) {
    $repct = $jsig =~ s/^(I|J|Z|F|D|B|S|C|L|\[|\))//;  ## No V types
    die "badly formed signature at $jsig" if ($repct == 0);
    $arg = $1;

    ## Stop if we hit the end paren
    last SIGPARSE if $arg eq "\)";

    if ($arg eq '[') {
      $depth++;
      # continue parsing array type...
      next SIGPARSE;
    } elsif ($arg eq 'L') {
      $jsig =~ s/^([^;]*);//;
      $arg = $1;
      $arg =~ s,/,.,g;
    } else {
      ## convert single-char identifier to english
      $arg = $sig{$arg};
    }

    ## If we hit an array, tack the array depth on the end
    if ($depth > 0) {
      $arg = $arg . "[]" x $depth;
      $depth = 0;
    }

    # Put the arg at the end of the list of args
    push (@args, $arg)
  }

  ### Last is the return type
  $depth = 0;
  $repct = $jsig =~ s/^(I|J|Z|F|D|B|S|C|L|V|\[)//; ## Adds V over argument types
  die "badly formed return type: \'$jsig\'" if ($repct == 0);
  $ret = $1;

  # If its an array, eat the [ and re-set $ret
  if ($ret eq '[') {
    $depth = 1;
    while ($jsig =~ s/\[//) {
      $depth++;
    }
    $jsig =~ s/^(I|J|Z|F|D|B|S|C|L)//; ## No [ or V
    die "badly formed return type: \'$jsig\'" if ($repct == 0);
    $ret = $1;
  }

  if ($ret eq 'L') {
    $jsig =~ s/^([^;]*);//;
    $ret = $1;
    $ret =~ s,/,.,g;
  } else {
    ## Convert single char identifier to english
    $ret = $sig{"$ret"};
  }

  # Tack the array brackets on
  if ($depth > 0) {
    $ret = $ret . "[]" x $depth;
  }

  ### Return the info in an easy-to-use list
  return ($package, $ret, $class, $method, @args);
}

###
### Print functions
###

sub printClass {
    my $r_cl = shift;
    my %class = %{$r_cl};

    my $flStr = &ACCFlagsToString($class{accessFlags}, 1);
    print "$flStr\n";

    &printConstantPool($r_cl);

    ## Print 'this_class'
    my $thisClName = %{$class{constantPool}[$class{thisClass}]}->{nameIndex};
    $thisClName = %{$class{constantPool}[$thisClName]}->{val};
    print "this_class @ $class{thisClass} ($thisClName)\n";

    ## Print 'super_class'
    if ($class{superClass} != 0) {
      my $superClName = %{$class{constantPool}[$class{superClass}]}->{nameIndex};
      $superClName = %{$class{constantPool}[$superClName]}->{val};
      print "super_class @ $class{superClass} ($superClName)\n";
    } else {
      print "No super class\n";
    }

    ## Print direct super interfaces
    &printInterfaces($r_cl);

    ## Print fields
    &printFields($r_cl);

    ## Print methods
    &printMethods($r_cl);

    ## Print attributes
    &printAttributes("", $r_cl, $class{attributes});
}

sub printMethods {
    my $r_cl = shift;
    local(%class) = %{$r_cl};

    if ($class{methodCt} == 0) {
	print "No methods.\n";
    } else {
	$i = 0;
	print "Methods:\n";
	while ($i < $class{methodCt}) {
	    my %method = %{$class{methods}[$i]};

	    my $accflags = ACCFlagsToString($method{accessFlags}, 0);
	    my $name = $class{constantPool}[$method{nameIndex}]->{val};
	    my $desc = $class{constantPool}[$method{descriptorIndex}]->{val};

	    if ($detailedMethods) {
		print ("\t$i: ");
		print (".accessFlags=$accflags; ");
		print (".name @ $method{nameIndex} ($name); ");
		print (".descriptor @ $method{descriptorIndex} ($desc); ");
		print (".attrCt = $method{attributesCt};\n");
		&printAttributes("\t\t", \%class, $method{attributes});
	    } else {
		print ("\t$accflags $name $desc\n");
	    }
	} continue {
	    $i++;
	}
    }
}

sub printFields {
    my $r_cl = shift;
    local(%class) = %{$r_cl};

    if ($class{fieldCt} == 0) {
	print "No fields.\n";
    } else {
	$i = 0;
	print "Fields:\n";
	while ($i < $class{fieldCt}) {
	    my %field = %{$class{fields}[$i]};

	    my $accflags = ACCFlagsToString($field{accessFlags}, 0);
	    my $name = $class{constantPool}[$field{nameIndex}]->{val};
	    my $desc = $class{constantPool}[$field{descriptorIndex}]->{val};

	    if ($detailedFields) {
		print ("\t$i: ");
		print (".accessFlags=$accflags; ");
		print (".name @ $field{nameIndex} ($name); ");
		print (".descriptor @ $field{descriptorIndex} ($desc); ");
		print (".attrCt = $field{attributesCt};\n");
		&printAttributes("\t\t", \%class, $field{attributes});
	    } else {
		print ("\t$accflags $desc $name\n");
	    }
	} continue {
	    $i++;
	}
    }
}

sub printInterfaces {
    my $r_cl = shift;
    local(%class) = %{$r_cl};

    if ($class{interfaceCt} == 0) {
	print "No interfaces.\n";
    } else {
	my $i = 0;
	print "Interfaces:\n";
	while ($i < $class{interfaceCt}) {
	    my $iClass = $class{interfaces}[$i];
	    my %iClassConst = %{$class{constantPool}[$iClass]};
	    my $iClassName = $iClassConst{nameIndex};
	    my $interfaceName = %{$class{constantPool}[$iClassName]}->{val};
	    print "\t$i] @ $iClass ($interfaceName)\n";
	} continue {
	    $i++;
	}
    }
}

sub ACCFlagsToString {
    my $flags = shift;
    my $isClass = shift;
    my @flags = ();

    push(@flags, "public") if $flags & $ACC_PUBLIC;
    push(@flags, "private") if $flags & $ACC_PRIVATE;
    push(@flags, "protected") if $flags & $ACC_PROTECTED;
    push(@flags, "abstract") if $flags & $ACC_ABSTRACT;
    push(@flags, "static") if $flags & $ACC_STATIC;
    push(@flags, "final") if $flags & $ACC_FINAL;
    if ($isClass) {
	push(@flags, "super") if $flags & $ACC_SUPER;
    }
    else {
	push(@flags, "synchronized") if $flags & $ACC_SYNCHRONIZED;
    }
    push(@flags, "native") if $flags & $ACC_NATIVE;
    push(@flags, "volatile") if $flags & $ACC_VOLATILE;
    push(@flags, "transient") if $flags & $ACC_TRANSIENT;
    push(@flags, "strictfp") if $flags & $ACC_STRICT;
    push(@flags, "interface") if $flags & $ACC_INTERFACE;
    push(@flags, "UNKNOWN") if $flags & $ACC_UNKNOWN;

    return join(',', @flags);
}

sub printConstantPool {
    my $r_cl = shift;
    local(%class) = %{$r_cl};		# cvt the class reference to the class hash

    print("Constant Pool Entries: $class{constantPoolCt}\n");

    $i = 1;
    while ($i < $class{constantPoolCt}) {
	my %cpEntry = %{$class{constantPool}[$i]};

	print "$i] $CONSTANTNames{$cpEntry{tag}}: ";

    	if ($cpEntry{tag} eq $CONSTANT_Class) {
	    my $ni = $cpEntry{nameIndex};

    	    &checkIndex($ni, "Name", $CONSTANT_Utf8);

	    my $nm = $class{constantPool}[$ni]->{val};

	    print (".name @ $ni ($nm);");

    	} elsif (($cpEntry{tag} eq $CONSTANT_FieldRef)
		 || ($cpEntry{tag} eq $CONSTANT_MethodRef)
		 || ($cpEntry{tag} eq $CONSTANT_InterfaceMethodRef)) {
    	    &checkIndex($cpEntry{classIndex}, "Class", $CONSTANT_Class);
    	    &checkIndex($cpEntry{nameTypeIndex}, "Name & Type", $CONSTANT_NameAndType);

	    print (".class @ $cpEntry{classIndex}; .name&type @ $cpEntry{nameTypeIndex};");
    	} elsif ($cpEntry{tag} eq $CONSTANT_String) {
	    my $si = $cpEntry{stringIndex};
    	    &checkIndex($si, "String", $CONSTANT_Utf8);

	    my $str = $class{constantPool}[$si]->{val};

	    print (".string @ $cpEntry{stringIndex} ($str);");
    	} elsif ($cpEntry{tag} eq $CONSTANT_NameAndType) {
	    my $ni = $cpEntry{nameIndex};
	    my $di = $cpEntry{descriptorIndex};

    	    &checkIndex($ni, "Name", $CONSTANT_Utf8);
    	    &checkIndex($di, "Descriptor", $CONSTANT_Utf8);

	    my $nstr = $class{constantPool}[$ni]->{val};
	    my $dstr = $class{constantPool}[$di]->{val};

	    print (".name @ $ni ($nstr); .descriptor @ $di ($dstr); ");
    	} elsif ($cpEntry{tag} eq $CONSTANT_Integer) {
	    print (".value = $cpEntry{val}");
    	} elsif ($cpEntry{tag} eq $CONSTANT_Utf8) {
	    print (".length=$cpEntry{len}; ");
	    print (".val=$cpEntry{val}; ");
    	} elsif ($cpEntry{tag} eq $CONSTANT_Float) {
	    print (".val=$cpEntry{strVal}; ");
    	} elsif ($cpEntry{tag} eq $CONSTANT_Double) {
	    print (".val=$cpEntry{strVal}; ");
	    $i++; ## Ick.  8-byte entries take two constant pool entries
    	} elsif ($cpEntry{tag} eq $CONSTANT_Long) {
	    print (".val=$cpEntry{strVal}; ");
	    $i++; ## Ick.  8-byte entries take two constant pool entries
    	} else {
    	    &fatal("Unknown Constant type $cpEntry{tag}!\n");
    	}

	print ("\n");

	$i++;
    }
}

sub printAttributes {
    my ($prefix, $r_class, $r_attrs) = @_;

    return if (!defined($r_attrs));

    my $i = 0;
    my %class = %{$r_class};

    print ("${prefix}Attributes:\n");
    foreach $r_attr (@{$r_attrs}) {

	my $name = $class{constantPool}[$r_attr->{nameIndex}]->{val};
	print ("${prefix}\t.name=$name; ");
	print (".length=" . $r_attr->{len} . ";");

	if ($name eq 'SourceFile') {
	  if ($r_attr->{len} != 2) {
	    print ("!Badly formed SourceFile Attribute, must be 2!");
	  } else {
	    my ($high, $low) = unpack("CC", $r_attr->{attr});
	    my $idx = ($high * 256) + $low;
	    my $name = $class{constantPool}[$idx]->{val};
	    print (" @ " . $idx . " (\"" . $name . "\")");
	  }
	}
	elsif ($name eq 'InnerClasses') {
	    my ($high, $low) = unpack("CC", $r_attr->{attr});
	    my $nr = ($high * 256) + $low;

	    print (" @ $nr entries");

	    my $array = substr ($r_attr->{attr}, 2);
	    for (my $i = 0; $i < $nr; $i++, $array = substr ($array, 8)) {
		my ($inner, $outer, $name, $acces) = unpack ("nnnn", $array);

		print("\n");
		# print("${prefix}\t\t$i] $inner, $outer, $name, $acces");

		print("${prefix}\t\t$i]");
		print(" .inner = " . $inner);
		print(" (" . $class{constantPool}[$class{constantPool}[$inner]->{nameIndex}]->{val} . ")") if $inner;

		print(" .outer = $outer");
		print(" (" . $class{constantPool}[$class{constantPool}[$outer]->{nameIndex}]->{val} . ")") if $outer;

		print(" .name = $name");
		print(" ($class{constantPool}[$name]->{val})") if ($name);

		print(" .acces = $acces (" . &ACCFlagsToString($acces, 1) .")");
	    }
	}
	print ("\n");
    }
}

###
### Read Class function
###

sub readClass {
    my $classFile = shift;
    local(%class) = (());

    open(CLASSIN, $classFile)
	|| open(CLASSIN, "${classFile}.class")
	    || die ("Cannot open $classFile for reading");

    ###
    ### Header Magic
    ###

    $class{magic} = read_u4();
    if ($class{magic} != $classMagic) {
    	fatal("Bad class magic '$class{magic}' --expected '$classMagic'.  $classFile is probably not a Java class file.");
    }

    ## Read in the major and minor version numbers
    $class{minorVersion} = &read_u2();
    $class{majorVersion} = &read_u2();

    print("Version: $class{majorVersion}.$class{minorVersion}  (expected 45.3)\n")
    	if ($class{minorVersion} ne 3) || ($class{majorVersion} ne 45);

    ###
    ### Constant Pool
    ###
    $class{constantPoolCt} = &read_u2();
    $class{constantPool} = [];

    $i = 1; # constant pool actually starts with entry 1...
    while ($i < $class{constantPoolCt}) {
    	my %cpEntry;
    	$cpEntry{tag} = &read_u1();

    	if ($cpEntry{tag} eq $CONSTANT_Class) {
    	    $cpEntry{nameIndex} = &read_u2();

    	    &checkIndex($cpEntry{nameIndex}, "Name");
    	} elsif ($cpEntry{tag} eq $CONSTANT_FieldRef) {
    	    $cpEntry{classIndex} = &read_u2();
    	    $cpEntry{nameTypeIndex} = &read_u2();

    	    &checkIndex($cpEntry{classIndex}, "Class");
    	    &checkIndex($cpEntry{nameTypeIndex}, "Name & Type");
    	} elsif ($cpEntry{tag} eq $CONSTANT_MethodRef) {
    	    $cpEntry{classIndex} = &read_u2();
    	    $cpEntry{nameTypeIndex} = &read_u2();

    	    &checkIndex($cpEntry{classIndex}, "Class");
    	    &checkIndex($cpEntry{nameTypeIndex}, "Name & Type");
    	} elsif ($cpEntry{tag} eq $CONSTANT_InterfaceMethodRef) {
    	    $cpEntry{classIndex} = &read_u2();
    	    $cpEntry{nameTypeIndex} = &read_u2();

    	    &checkIndex($cpEntry{classIndex}, "Class");
    	    &checkIndex($cpEntry{nameTypeIndex}, "Name & Type");
    	} elsif ($cpEntry{tag} eq $CONSTANT_String) {
    	    $cpEntry{stringIndex} = &read_u2();

    	    &checkIndex($cpEntry{stringIndex}, "String");
    	} elsif ($cpEntry{tag} eq $CONSTANT_NameAndType) {
    	    $cpEntry{nameIndex} = &read_u2();
    	    $cpEntry{descriptorIndex} = &read_u2();

    	    &checkIndex($cpEntry{nameIndex}, "Name");
    	    &checkIndex($cpEntry{descriptorIndex}, "Descriptor");
    	} elsif ($cpEntry{tag} eq $CONSTANT_Integer) {
    	    $cpEntry{val} = &read_u4();
    	} elsif ($cpEntry{tag} eq $CONSTANT_Utf8) {
    	    $cpEntry{len} = &read_u2();
    	    $cpEntry{val} = &read_utf8($cpEntry{len});
    	} elsif ($cpEntry{tag} eq $CONSTANT_Float) {
	    $cpEntry{val} = &read_u4();
	    $cpEntry{strVal} = &read_float($cpEntry{val});
    	} elsif ($cpEntry{tag} eq $CONSTANT_Double) {
	    $cpEntry{val} = &read_u8();
	    $cpEntry{strVal} = &read_double($cpEntry{val});
    	} elsif ($cpEntry{tag} eq $CONSTANT_Long) {
	    $cpEntry{val} = &read_u8();
	    $cpEntry{strVal} = "<Unknown>";
    	} else {
    	    &fatal("Unknown Constant type $cpEntry{tag}!\n");
    	}

    	$class{constantPool}[$i] = \%cpEntry;

	## Ick.  8-byte entries take two constant pool entries
	$i++ if (($cpEntry{tag} == $CONSTANT_Long)
		 || ($cpEntry{tag} == $CONSTANT_Double));
    } continue {
    	$i++;
    }

    ###
    ### Misc. Class Info
    ###

    $class{accessFlags} = &read_u2();

    $class{thisClass} = &read_u2();
    &checkIndex($class{thisClass}, "this_class", $CONSTANT_Class);

    $class{superClass} = &read_u2();
    if ($class{superClass} != 0) {
      &checkIndex($class{superClass}, "super_class", $CONSTANT_Class);
    }
    # so what if it's java.lang.Object
    # else {
    #  print ("Warning: class has no super class.  Must be java.lang.Object\n");
    #}

    ###
    ### Direct super-interfaces
    ###
    $class{interfaceCt} = &read_u2();
    $class{interfaces} = [];

    $i = 0;
    while ($i < $class{interfaceCt}) {
    	$class{interfaces}[$i] = &read_u2();

    	&checkIndex($class{interfaces}[$i], "Interface \#$i", $CONSTANT_Class);
    } continue {
    	$i++;
    }

    ###
    ### Fields
    ###
    $class{fieldCt} = &read_u2();
    $class{fields} = [];

    $i = 0;
    while ($i < $class{fieldCt}) {
    	my %field;
    	$field{accessFlags} = &read_u2();
    	$field{nameIndex} = &read_u2();
    	$field{descriptorIndex} = &read_u2();
    	$field{attributesCt} = &read_u2();
    	$field{attributes} = &readAttributes($field{attributesCt});

    	&checkIndex($field{nameIndex}, "Field Name", $CONSTANT_Utf8);
    	&checkIndex($field{descriptorIndex}, "Field Descriptor", $CONSTANT_Utf8);

    	$class{fields}[$i] = \%field;
    } continue {
    	$i++;
    }

    ###
    ### Methods
    ###
    $class{methodCt} = &read_u2();
    $class{methods} = [];

    $i = 0;
    while ($i < $class{methodCt}) {
    	my %method;

    	$method{accessFlags} = &read_u2();
    	$method{nameIndex} = &read_u2();
    	$method{descriptorIndex} = &read_u2();
    	$method{attributesCt} = &read_u2();
    	$method{attributes} = &readAttributes($method{attributesCt});

    	&checkIndex($method{nameIndex}, "Method Name", $CONSTANT_Utf8);
    	&checkIndex($method{descriptorIndex}, "Method Descriptor", $CONSTANT_Utf8);

    	$class{methods}[$i] = \%method;
    } continue {
    	$i++;
    }

    ###
    ### Class attributes
    ###
    $class{attributesCt} = &read_u2();
    $class{attributes} = &readAttributes($class{attributesCt});

    ###
    ### End of .class file
    ###

    return \%class;
}

###
### Write Class function
###

sub writeClass {
    my $r_class = shift;
    my $classFile = shift;
    local(%class) = %{$r_class};

    if ($classFile =~ /\.class$/) {
	open(CLASSOUT, ">$classFile")
	    || die ("Cannot open $classFile for writing");
    } else {
	open(CLASSOUT, ">$classFile.class")
	    || die ("Cannot open $classFile.class for writing");
    }

    ###
    ### Header Magic
    ###

    if ($class{magic} != $classMagic) {
    	fatal("Bad class magic '$class{magic}' --expected '$classMagic'.  Not writing class file.");
    }

    &write_u4($class{magic});

    ## Write major/minor version numbers
    &write_u2($class{minorVersion});
    &write_u2($class{majorVersion});

    ###
    ### Constant Pool
    ###
    &write_u2($class{constantPoolCt});

    $i = 1; # constant pool actually starts with entry 1...
    while ($i < $class{constantPoolCt}) {
    	my %cpEntry = %{$class{constantPool}[$i]};
    	&write_u1($cpEntry{tag});

    	if ($cpEntry{tag} eq $CONSTANT_Class) {
    	    &checkIndex($cpEntry{nameIndex}, "Name", $CONSTANT_Utf8);

    	    &write_u2($cpEntry{nameIndex});
    	} elsif (($cpEntry{tag} eq $CONSTANT_FieldRef)
		 || ($cpEntry{tag} eq $CONSTANT_MethodRef)
		 || ($cpEntry{tag} eq $CONSTANT_InterfaceMethodRef)) {
    	    &checkIndex($cpEntry{classIndex}, "Class", $CONSTANT_Class);
    	    &checkIndex($cpEntry{nameTypeIndex}, "Name & Type", $CONSTANT_NameAndType);

    	    &write_u2($cpEntry{classIndex});
    	    &write_u2($cpEntry{nameTypeIndex});
    	} elsif ($cpEntry{tag} eq $CONSTANT_String) {
    	    &checkIndex($cpEntry{stringIndex}, "String", $CONSTANT_Utf8);

    	    &write_u2($cpEntry{stringIndex});
    	} elsif ($cpEntry{tag} eq $CONSTANT_NameAndType) {
    	    &checkIndex($cpEntry{nameIndex}, "Name", $CONSTANT_Utf8);
    	    &checkIndex($cpEntry{descriptorIndex}, "Descriptor", $CONSTANT_Utf8);

    	    &write_u2($cpEntry{nameIndex});
    	    &write_u2($cpEntry{descriptorIndex});
    	} elsif ($cpEntry{tag} eq $CONSTANT_Integer) {
    	    &write_u4($cpEntry{val});
    	} elsif ($cpEntry{tag} eq $CONSTANT_Utf8) {
    	    &write_u2($cpEntry{len});
    	    &write_utf8($cpEntry{val});
    	} elsif ($cpEntry{tag} eq $CONSTANT_Float) {
	    &write_u4($cpEntry{val});
    	} elsif ($cpEntry{tag} eq $CONSTANT_Double) {
	    &write_u8($cpEntry{val});
	    $i++; ## Ick.  8-byte entries take two constant pool entries
    	} elsif ($cpEntry{tag} eq $CONSTANT_Long) {
	    &write_u8($cpEntry{val});
	    $i++; ## Ick.  8-byte entries take two constant pool entries
    	} else {
    	    &fatal("Unknown Constant type $cpEntry{tag}!\n");
    	}
    } continue {
    	$i++;
    }

    ###
    ### Misc. Class Info
    ###

    &write_u2($class{accessFlags});
    &write_u2($class{thisClass});
    &write_u2($class{superClass});

    ###
    ### Direct super-interfaces
    ###
    &write_u2($class{interfaceCt});

    $i = 0;
    while ($i < $class{interfaceCt}) {
    	&write_u2($class{interfaces}[$i]);
    } continue {
    	$i++;
    }

    ###
    ### Fields
    ###
    &write_u2($class{fieldCt});

    $i = 0;
    while ($i < $class{fieldCt}) {
    	my %field = %{$class{fields}[$i]};
    	&write_u2($field{accessFlags});
    	&write_u2($field{nameIndex});
    	&write_u2($field{descriptorIndex});
    	&write_u2($field{attributesCt});
    	&writeAttributes($field{attributes});
    } continue {
    	$i++;
    }

    ###
    ### Methods
    ###
    &write_u2($class{methodCt});

    $i = 0;
    while ($i < $class{methodCt}) {
    	my %method = %{$class{methods}[$i]};

    	&write_u2($method{accessFlags});
    	&write_u2($method{nameIndex});
	&write_u2($method{descriptorIndex});
    	&write_u2($method{attributesCt});
    	&writeAttributes($method{attributes});
    } continue {
    	$i++;
    }

    ###
    ### Class attributes
    ###
    &write_u2($class{attributesCt});
    &writeAttributes($class{attributes});

    ###
    ### End of .class file
    ###

    return \%class;
}

###
### Integrity check functions
###

sub checkIndex {
    my ($val, $name, $type) = @_;

    # $class is a global

    if ($val == 0) {
      &fatal("ERROR: Found constant pool index 0 for $name.  (Expecting a CONSTANT_$CONSTANTNames{$type} entry.)");
    }

    if ($val >= $class{constantPoolCt}) {
	&fatal("ERROR: $name index for current constant is $val, must be less than $class{constantPoolCt}\n");
    }

    if (defined($type)) {
	my $actualTag = $class{constantPool}[$val]{tag};
	if ($actualTag != $type) {
	    &fatal("ERROR: $name expects a CONSTANT_$CONSTANTNames{$type} entry at $val, but found a CONSTANT_$CONSTANTNames{$actualTag} entry\n");
	}
    }
}

###
### Read primitives
###

sub read_u8 {
    my $long = 0;
    (read(CLASSIN, $long, 8) == 8) || die ("premature eof in read_u8()\n");
    my ($b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8) = unpack("CCCCCCCC", $long);
    return (($b1 << 56) + ($b2 << 48) + ($b3 << 40) + ($b4 << 32)
	    + ($b5 << 24) + ($b6 << 16) + ($b7 << 8) + $b8);
}

sub read_u4 {
    my $long = 0;
    (read(CLASSIN, $long, 4) == 4) || die ("premature eof in read_u4()\n");
    my ($top, $highmid, $lowmid, $low) = unpack("CCCC", $long);
    return ($top * (256*256*256)) + ($highmid * (256*256)) + ($lowmid * 256) + $low;
}

sub read_u2 {
    my $short = 0;
    (read(CLASSIN, $short, 2) == 2) || die ("premature eof in read_u2()\n");
    my ($high, $low) = unpack("CC", $short);
    #print("read_u2: $high, $low\n");
    return ($high * 256) + $low;
}

sub read_u1 {
    my $byte = 0;
    (read(CLASSIN, $byte, 1) == 1) || die ("premature eof in read_u1()\n");
    my $val = unpack("C", $byte);
    return $val;
}

sub read_n {
    my $byteCt = shift;
    my $foo = '';
    (read(CLASSIN, $foo, $byteCt) == $byteCt) || die ("premature eof in read_n($byteCt)\n");

    return $foo;
}

sub read_float {
    my $intVal = shift;

    return "+INF" if ($intVal == 0x7f800000);
    return "-INF" if ($intVal == 0xff800000);
    if ((($intVal >= 0x7f800001) && ($intVal <= 0x7fffffff))
	|| (($intVal >= 0xff800001) && ($intVal <= 0xffffffff))) {
	return "NaN";
    }

    ## Otherwise, convert to a floating point number
    $sign     = (($intVal >> 31) == 0) ?  1 : -1;
    $exponent = (($intVal >> 23) & 0xFF);
    $mantissa = ($exponent == 0) ? ($intVal & 0x7fffff) << 1 : ($intVal & 0x7fffff) | 0x800000;

    return $sign * $mantissa * 2 ** ($exponent - 150);
}

sub read_double {
    my $intVal = shift;

    return "+INF" if ($intVal == 0x7f800000);
    return "-INF" if ($intVal == 0xff800000);
    if ((($intVal >= 0x7f800001) && ($intVal <= 0x7fffffff))
	|| (($intVal >= 0xff800001) && ($intVal <= 0xffffffff))) {
	return "NaN";
    }

    ## Otherwise, convert to a floating point number
    $sign     = (($intVal >> 31) == 0) ?  1 : -1;
    $exponent = (($intVal >> 23) & 0xFF);
    $mantissa = ($exponent == 0) ? ($intVal & 0x7fffff) << 1 : ($intVal & 0x7fffff) | 0x800000;

    return $sign * $mantissa * 2 ** ($exponent - 150);
}

sub read_utf8 {
    my $byteCt = shift;
    my $utf = '';
    (read(CLASSIN, $utf, $byteCt) == $byteCt) || die ("premature eof in read_utf8($byteCt)\n");

    my $str = unpack("A$byteCt", $utf);
    return $str;
}

sub readAttributes {
    my ($ct) = @_;

    my @attrs = [];

    return undef if ($ct < 1);

    my $i = 0;
    while ($i < $ct) {
	my %attribute;
        $attribute{nameIndex} = &read_u2();
	$attribute{len} = &read_u4();
	$attribute{attr} = &read_n($attribute{len});

	&checkIndex($attribute{nameIndex}, "Attribute Name", $CONSTANT_Utf8);

	$attrs[$i] = \%attribute;
    } continue {
	$i++;
    }

    return \@attrs;
}

###
### Write primitives
###

sub write_u8 {
    my $val = shift;
    $b8 = $val & 255;
    $b7 = ($val >> 8) & 255;
    $b6 = ($val >> 16) & 255;
    $b5 = ($val >> 24) & 255;
    $b4 = ($val >> 32) & 255;
    $b3 = ($val >> 40) & 255;
    $b2 = ($val >> 48) & 255;
    $b1 = ($val >> 56) & 255;
    print CLASSOUT pack("CCCCCCCC", $b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8);
}

sub write_u4 {
    my $val = shift;
    print CLASSOUT pack("N", $val);
}

sub write_u2 {
    my $val = shift;
    print CLASSOUT pack("n", $val);
}

sub write_u1 {
    my $byte = shift;
    print CLASSOUT pack("C", $byte);
}

sub write_n {
    my $val = shift;
    print CLASSOUT $val;	# XXX assumes $val."length" is 'n'
}

sub write_utf8 {
    my $utf8 = shift;
    print CLASSOUT $utf8
}

sub writeAttributes {
    my $r_attrs = shift;

    return if (!defined $r_attrs);

    my $i = 0;
    foreach $r_attr (@{$r_attrs}) {
	my %attribute = %{$r_attr};
	&write_u2($attribute{nameIndex});
	&write_u4($attribute{len});
	&write_n($attribute{attr});
    }
}

sub fatal {
    print STDERR @_;
    print STDERR "\n";
    exit 11;
}

1;

# eof